
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。 突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7. X7 X- s4 h4 C1 F1 V 于是花了点时间,做了个贪吃蛇的游戏,写了段程序。 程序硬件支持: STM32F446R+OLED12864+按键& p: K! e8 o; z) z 软件结构: 1.DMA跑SPI刷屏 2.贪吃蛇游戏算法$ m+ n5 Z* u" d+ v5 g y! Y$ u( _ 3.重新定义OLED显示方式 底层驱动就不说,DMA配置,跑SPI数据。 我先做了个二位数组,存放64*128个点,初始值为0; uint8_t OLED_Display_Data[8][128]( |0 a7 C$ \) h! p" B5 D ! t6 o( Y, u6 A4 m 之后开个定时器,每隔一段时间跑一次屏幕 if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag) { HardwareParamter.TimingOLED = 0; Display_Process(OLED_Display_Data); : P6 d* F3 u4 o$ V( {) A l5 o } : J. i* a- _5 \% V& ?( r- E 基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。 * ~* }* r* [0 I 贪吃蛇算法做的简单:: l* B( y6 P( J 首先是一个结构体,存放相关参数* j# I' ~' F# M. G typedef struct { F+ O* c& h0 i" J2 R* y0 } uint8_t food_x; //食物横坐标 uint8_t food_y; //食物纵坐标 ) y' _2 [5 V/ B/ p4 r+ f7 p6 E uint8_t gameEsc; //游戏开始暂停$ w0 a, O# p' f b. ?; i# H$ Q uint8_t gameSpeed; //游戏速度 uint8_t gameLevel; //游戏难度) ?. _$ G' O/ C 1 X1 V' x/ b/ J8 a- ?. _+ ^ uint8_t x[39]; uint8_t y[39];+ I8 C4 ~: r ]9 [6 s uint8_t node; //蛇的节数 uint8_t direction; //蛇头方向5 ]% u2 }: W0 F# D" n" a, u }GameParamter_t; 两个核心函数:移动和创建食物 食物依赖于随机数生成函数 // 需要出现新食物 a" @2 S- f$ }3 r8 v. V6 B2 G/ [2 h uint8_t createNewFood(void)- m5 N9 L3 q2 g5 p- M. S$ c {% D0 h6 H- N" F0 n) K uint8_t i; uint8_t size = GameParamter.node; uint8_t flag = TRUE; // 标记创建的新事物与贪吃蛇的身体冲突 srand(HardwareParamter.TimingVal); GameParamter.food_x = rand() % MAP_H;; k+ }- p$ q, j* f/ y GameParamter.food_y = rand() % MAP_L;; K7 r% [0 y0 x& m( ] // 食物的坐标必须为3的倍数才会在显示屏先被显示+ l5 b9 z- ?# y# ?# @; s# ^ for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) { ! F. a/ {+ H; y9 T, ~& m U6 ]( | ;* K3 }! q" U( V8 I9 |5 V }4 i4 F* s( f" R' u! C for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) { ; }1 o. e3 N n& k% G% c. F for (i = 0; i < size; ++i) {+ x* f2 w8 Z+ p4 h$ Y! I" r/ F if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {4 W0 d; H& [$ _: K1 h flag = FALSE;# y1 w5 K5 L; p0 B break;2 p$ ]2 y1 y$ q1 l }0 O) v+ P: H- H, }: s. k U } ; U) L, z7 z) V! N 0 r, P; N7 r: m- U9 z) M& G6 j return flag;+ S6 t1 `6 J+ w! m- k* Z* h# T }6 ~2 U: G9 h. _6 r# l ( P( M7 @. q. S& b& t: r4 T' U 移动就是简单的数据加减 // 贪吃蛇移动 void move(void) {5 r6 U, A7 k2 ~1 q uint8_t i;8 k. X2 m# G) m p if(GameParamter.gameEsc) { // 将蛇从最后个节点向前一个节点移动 for(i = GameParamter.node - 1; i > 0; i--) { ( M& \( W/ a( D2 g2 s F GameParamter.x[i] = GameParamter.x[i - 1];; N% V; B7 K9 [' J GameParamter.y[i] = GameParamter.y[i - 1]; } // 根据此时贪吃蛇的方向,设置蛇头的位置 switch(GameParamter.direction) { 5 [; X1 j/ w( G3 p case RIGHT: ; u+ E2 j0 d& ^/ e+ O9 P1 T GameParamter.x[0] += DOT_H; q6 i& Y g8 p* V3 A! `1 ] break; case LEFT: GameParamter.x[0] -= DOT_H;( O) R& o9 o. A5 a N! d9 Y break; case UP: 1 r/ g) r q7 G& s1 v) I( I GameParamter.y[0] -= DOT_L;# S9 A; k9 v, R1 Z break; case DOWN: 9 \9 w7 V2 O Z2 v9 q+ v9 N3 k GameParamter.y[0] += DOT_L;' ~+ [9 c/ L6 e0 c' i: h" T break; } }% r! q, d6 }! E$ P1 f( v0 n) v }! ~ S, A1 T) r9 { 9 D/ h' k' V7 s: n! r* D 显示部分随时更新 uint8_t temp;# {. U! N0 Y7 Q, x5 p! Z switch(State_Flag): o4 L, z) j" o9 s8 b { case Snake_Move:1 \, b, x3 O! F; }$ f, k$ d Y //清除蛇尾部 for(temp = 0;temp < GameParamter.node;temp++)# {/ M+ _1 N% t) u% j3 f, M Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);3 o2 X, N( P. [1 W //移动蛇: b8 g: m/ \% O! ` move();' ~/ M6 C* m$ X5 G" R2 ]3 l //显示蛇的位置' T u5 _; Y8 s- r) t( H4 X for(temp = 0;temp < GameParamter.node;temp++) Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1); ) q: {9 G- H. y; l; f0 x: B. i6 g //吃到食物,创建新的食物5 m$ U+ G' B) j1 [: \ if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])7 W! z- A4 T+ h( s Oled_food(); ( U. G) l% K$ f) B* _: W. _ U if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)3 R) h0 u- ~ [7 f, A- C7 J/ T$ ^ {0 a+ v/ D$ E3 Q8 Z4 K) T5 b OLEDClear();/ W1 c7 G3 F9 L; `8 s% a0 ^ e5 ~ OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME"); OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER"); State_Flag = Game_Over; HardwareParamter.OLEDFlash_flag = 1;) l6 j2 A7 s' c! B% G } break; case Game_Over:9 l9 `+ u9 R; B$ b9 X : {/ _! j' x2 k8 D4 m break;# S6 k1 D, m: M3 S default: OLEDClear();1 o/ M- T. h8 y/ |# g! i v4 o2 a; P) i State_Flag = Snake_Move; HardwareParamter.OLEDFlash_flag = 0;, f! D$ z5 c2 N* _$ d( @ break;* f$ F) D+ B; P" N } , v% \' m. P9 O% N$ |( R) o* W, f9 h - w1 D+ X, [( G! W; C 外带一个按键处理,解决方向和暂停重置问题。 |
说起贪吃蛇,以前在大学的时候,学完数据结构,然后就在dos下面写了一个极其简陋的!!!![]() ![]() ![]() |
可以试试![]() |
有点意思。楼主有时间放个视频上来看看。 |
感谢分享,已汇总到2月技术原创 https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html |
程序下载链接木有? |
楼主,想知道蛇头方向改变蛇身呈L或者类似矩形方波这些不规则的情况下蛇的移动部分的代码 |
最近正在捣鼓446,等手头的事情告一段落也想试着搞一个![]() |