闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。 - N0 B: ?& `3 K! L( U; O 突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.6 g% Q/ u. O; r1 ?8 e 于是花了点时间,做了个贪吃蛇的游戏,写了段程序。9 o; u. ^7 X& Y1 I1 J, k/ v3 S 5 k0 z4 \+ E/ n" G, y/ ? 程序硬件支持:, g8 S- t! J% j( b STM32F446R+OLED12864+按键 j1 m' D9 _! }) x& L 软件结构:% U* I) Y1 o& A8 v 1.DMA跑SPI刷屏# S# t z' h* k+ p" z 2.贪吃蛇游戏算法 3.重新定义OLED显示方式 " }' e/ x' O* ]5 t 底层驱动就不说,DMA配置,跑SPI数据。! d! M+ e" c* P; l7 S& N d+ C9 }( z6 U0 N1 f2 A6 e 我先做了个二位数组,存放64*128个点,初始值为0; uint8_t OLED_Display_Data[8][128] * _+ z7 t6 e* I 之后开个定时器,每隔一段时间跑一次屏幕 if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag) { HardwareParamter.TimingOLED = 0;) i: i4 E4 _* Z" O Display_Process(OLED_Display_Data); $ V0 S3 K- D( x8 W8 J }2 _! R1 H5 n) E( n- n 2 U) W" T- i/ {$ S- c/ ] ) b$ w3 c! u% g0 T& c0 O# n1 j8 z6 N 基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。 $ S, l: @ N, L! o' R% Q/ [. f$ F8 g 贪吃蛇算法做的简单: 首先是一个结构体,存放相关参数 typedef struct { uint8_t food_x; //食物横坐标- A, e3 }2 {& c [! T uint8_t food_y; //食物纵坐标 uint8_t gameEsc; //游戏开始暂停 uint8_t gameSpeed; //游戏速度 uint8_t gameLevel; //游戏难度7 f/ ~# D: {( T6 R 8 l/ A0 ^/ K% S# p; q( D; u uint8_t x[39]; uint8_t y[39]; uint8_t node; //蛇的节数. ]# T- b5 v9 d7 B, U5 d7 X uint8_t direction; //蛇头方向, u! D2 T6 p9 h9 `4 C5 h }GameParamter_t; 两个核心函数:移动和创建食物 食物依赖于随机数生成函数 // 需要出现新食物2 w& C$ \- G" e7 K" ~& U2 _7 I3 S uint8_t createNewFood(void)6 t- A/ x- e4 P' B T! z {& h# q( d6 L5 x i" ^- _( O% t7 f uint8_t i;4 L; l. d3 f% ?9 }' F2 Q uint8_t size = GameParamter.node; uint8_t flag = TRUE; // 标记创建的新事物与贪吃蛇的身体冲突6 p o1 [, w! @# J srand(HardwareParamter.TimingVal); GameParamter.food_x = rand() % MAP_H;0 B7 M7 ~+ ~# Z' H" E4 \- a GameParamter.food_y = rand() % MAP_L; // 食物的坐标必须为3的倍数才会在显示屏先被显示, T# W: j. e" B$ L+ D for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) { 3 P! C# ^& c8 m2 O7 _+ _ ; }3 z+ \1 D/ u- k) R' x1 B for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) { ;# \6 s4 z9 B9 [! r, U0 P }% R2 P+ i: m1 q: h) y& e* ^+ v8 ~ 5 D5 \' e; `7 d for (i = 0; i < size; ++i) { if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {8 ]% K' [- ~! L7 ^0 U2 V7 N flag = FALSE; break;0 X% ~; ^: d1 D; b7 Q8 A6 p } } return flag;3 w1 B F# G, Z1 X4 Q } 1 f" e. N" }: J8 Q1 }: P 移动就是简单的数据加减 // 贪吃蛇移动 void move(void)) P* b. m* g( v& {; T3 t {* e2 n9 S% t0 j. l; a' x4 l uint8_t i;, A- g+ w3 z. I r; I/ x# g' C: Z5 l) j0 s& } if(GameParamter.gameEsc)% v+ ~) b2 K, v) `0 V {$ p! ?( T9 |& h7 h // 将蛇从最后个节点向前一个节点移动+ H0 Y" c3 j3 o0 {' i. F, O( y- g for(i = GameParamter.node - 1; i > 0; i--) { 6 N i/ b& a1 @" R GameParamter.x[i] = GameParamter.x[i - 1]; GameParamter.y[i] = GameParamter.y[i - 1]; } // 根据此时贪吃蛇的方向,设置蛇头的位置 switch(GameParamter.direction) # d" u' d; X5 R { case RIGHT: 7 O0 l' Q' n% A( ]" e GameParamter.x[0] += DOT_H; break; case LEFT: + L3 I; F( }8 E1 X" `+ D GameParamter.x[0] -= DOT_H;4 s: L1 U* X, f2 } break;2 i1 e! h6 S% X5 C1 v3 s' l case UP: 3 g% l- W7 _9 M; D: A4 ^6 \5 k GameParamter.y[0] -= DOT_L; break; case DOWN: GameParamter.y[0] += DOT_L;- y4 I3 I) ^1 W; m0 D; R break; c9 T8 \4 g$ S# L9 E7 {0 C, S6 U' J } }" {7 C8 v# d( p2 T; w; J } K/ G: a: e9 o9 h 显示部分随时更新" U3 W1 s3 ~) u# H4 X% } uint8_t temp; switch(State_Flag)/ d2 J7 v( Q L: } { case Snake_Move:7 B- j3 f4 r0 E$ E' L7 v4 v //清除蛇尾部 for(temp = 0;temp < GameParamter.node;temp++) Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0); 3 c' ^4 A# K3 c( V //移动蛇% ]) o* `2 Z! C' R move();7 L" T; T9 a5 `$ Y' a- B _ {4 J/ J //显示蛇的位置9 ?8 [; X# w7 s# y3 R for(temp = 0;temp < GameParamter.node;temp++) Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1); 3 v: M* c6 U: O% T //吃到食物,创建新的食物0 u2 r0 e% ]- I2 x& v4 n if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0]): S. C+ d5 X- {; b4 t Oled_food(); 9 U; O" Q: |5 Z, Q5 q" g) u if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L) { OLEDClear(); M* u0 F9 E0 J, `4 g" Y. `+ w OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME");7 N6 Q) q1 h1 T) j% @3 u7 z" @5 x OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER"); State_Flag = Game_Over; HardwareParamter.OLEDFlash_flag = 1;7 L' j7 X1 ?& \ } break; l! [" `' l- ~ case Game_Over:/ w' O; ~3 Y# P+ m+ F- r) L break;6 E4 \' a) {( b default:# T8 w+ r; B4 @* X+ {3 O OLEDClear(); State_Flag = Snake_Move;: L, x. Q5 W5 u/ { U8 |. ? HardwareParamter.OLEDFlash_flag = 0;' l5 a+ O* @ h6 z( ^/ e2 [, \ break; }, r" s' f. d3 \3 n, S l 1 v6 H1 e$ t: p0 j& ]( t 外带一个按键处理,解决方向和暂停重置问题。 |
说起贪吃蛇,以前在大学的时候,学完数据结构,然后就在dos下面写了一个极其简陋的!!! |
可以试试 |
有点意思。楼主有时间放个视频上来看看。 |
感谢分享,已汇总到2月技术原创 https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html |
程序下载链接木有? |
楼主,想知道蛇头方向改变蛇身呈L或者类似矩形方波这些不规则的情况下蛇的移动部分的代码 |
最近正在捣鼓446,等手头的事情告一段落也想试着搞一个 |
STM32固件库分享,超全系列整理
【中文文档】AN3965_STM32F40x和STM32F41x基于串口的IAP
STM32F4-DISC 实现USB主机(U盘)和USB设备(虚拟串口)自动切换
STM32F4中文用户手册
基于STM32F407的FreeRTOS阶段性的总结(13)
STM32F400、STM32F402 Cortex-M4超值单片机
基于STM32F407的FreeRTOS获取各任务运行时间及占用情况(4)
基于STM32F407的FreeRTOS任务的挂起与恢复(3)
基于STM32F407的FreeRTOS任务的创建与删除经验分享(2)
基于STM32F407的FreeRTOS环境搭建经验分享(1)