闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。7 ^# a+ V# m; r) G0 _+ [ % B, _1 C5 ]7 T$ M F 突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7. 于是花了点时间,做了个贪吃蛇的游戏,写了段程序。 程序硬件支持: STM32F446R+OLED12864+按键 & c9 T! w% z5 E: y: M 软件结构:5 n" S- y; M1 o2 m& s# u# I 1.DMA跑SPI刷屏 2.贪吃蛇游戏算法! ^' s3 ^; A( q* z 3.重新定义OLED显示方式 : z4 W0 ]0 J" a2 b& R2 ] 底层驱动就不说,DMA配置,跑SPI数据。 我先做了个二位数组,存放64*128个点,初始值为0; uint8_t OLED_Display_Data[8][128] 之后开个定时器,每隔一段时间跑一次屏幕 F; H5 v" ~' Z7 n- U8 U5 h if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag) {; |6 g6 B, ?/ d* r1 R& q HardwareParamter.TimingOLED = 0; Display_Process(OLED_Display_Data); ' T0 j" I& E* ^ } w7 X% t" D, z ; M* A m: w/ |9 u , m/ `/ z) ?' Z: g 基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。6 l9 {( N: j {( |0 X \6 M6 s 贪吃蛇算法做的简单:) }( c4 o/ R' L% l 首先是一个结构体,存放相关参数8 l7 U3 C$ F- s3 F typedef struct {- w5 M/ b0 Q0 N+ P e/ N4 u uint8_t food_x; //食物横坐标 j# w: b& ^/ Z/ ^! R( V) O7 z7 z uint8_t food_y; //食物纵坐标* f! x0 W1 _' m# M n) x& E8 b uint8_t gameEsc; //游戏开始暂停 uint8_t gameSpeed; //游戏速度+ l5 E% p$ ]1 P8 n- a uint8_t gameLevel; //游戏难度$ F$ O% f9 F6 e, p9 j. w* i$ P uint8_t x[39]; uint8_t y[39]; R5 c$ [: |1 M. Y5 p uint8_t node; //蛇的节数7 d' |- E K1 `9 g) {* ` uint8_t direction; //蛇头方向 }GameParamter_t; : E8 J( t% D- _7 B- i 两个核心函数:移动和创建食物 食物依赖于随机数生成函数+ b2 ~: D: B6 T& h // 需要出现新食物3 H2 K% A5 m5 h$ C uint8_t createNewFood(void)+ a& U3 i; g0 P4 a- W { uint8_t i; uint8_t size = GameParamter.node; uint8_t flag = TRUE; // 标记创建的新事物与贪吃蛇的身体冲突4 G9 k8 }5 @% M4 ]0 E9 r4 j) J0 t srand(HardwareParamter.TimingVal);* d4 c% D& e7 |" d GameParamter.food_x = rand() % MAP_H;/ u# V5 E% ?1 i% Y GameParamter.food_y = rand() % MAP_L;7 Y1 [( [3 h8 @2 Q8 Q- F ) m) k( [- G$ d+ ]& M$ ^- L$ F* h // 食物的坐标必须为3的倍数才会在显示屏先被显示 for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) { & T- G( x2 L% Z7 k ? ; } h! Q1 @, [: F- I for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) { . `2 N; g$ s6 {, ^4 Z5 S ; }# t* d; l" r# y! h- a4 T6 o 4 N9 U0 n6 O- y8 `* a- w9 N7 s for (i = 0; i < size; ++i) { if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {$ Z! j/ O+ t+ L% U+ u. K flag = FALSE; break; } } return flag;0 H) z3 d* D( z5 E S' F* L4 f }- ~$ v+ T8 g7 Z. G2 \) p3 ]4 F1 Z , d- @: E F; |- G3 K" J. u5 f" x2 Q1 | 移动就是简单的数据加减 // 贪吃蛇移动 void move(void) {$ `8 z h% U9 I% c uint8_t i;% k2 N. B) U9 H t% R( v% I 3 q* M0 n: E! W if(GameParamter.gameEsc) { // 将蛇从最后个节点向前一个节点移动 for(i = GameParamter.node - 1; i > 0; i--) { GameParamter.x[i] = GameParamter.x[i - 1];8 O5 A7 k9 O) S GameParamter.y[i] = GameParamter.y[i - 1]; }( G/ k# ~# P& _( C ( S# d* {) U$ O1 H- I: z // 根据此时贪吃蛇的方向,设置蛇头的位置 - G% ^+ h! C2 O" j. W- K switch(GameParamter.direction) { case RIGHT: ) N+ u9 J: r$ o* P GameParamter.x[0] += DOT_H;" q0 q) Z1 W9 E3 M break;- V5 F& o; F$ V* \4 @6 C1 U case LEFT: 5 Y2 F) \& j* n& B$ M: D GameParamter.x[0] -= DOT_H;- o2 N. ?1 M& d' o8 A% e break;& U: {3 |% E) |1 n% H0 x$ S6 m case UP: GameParamter.y[0] -= DOT_L;, d q9 u: I1 P break; case DOWN: GameParamter.y[0] += DOT_L;! w- C% L% V5 u! d. t+ j break;, y" z# q' j3 \$ N# [ } }: F' x7 O+ ]" D8 e# ?1 M8 p! J8 z } 显示部分随时更新 uint8_t temp;5 J1 m7 d8 m) g5 j) b; D switch(State_Flag) { case Snake_Move: //清除蛇尾部 for(temp = 0;temp < GameParamter.node;temp++)3 S8 O* [5 F+ F Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0); //移动蛇. s; P7 z8 n& x0 F; v( O( D8 ~/ Z4 Z move();1 r2 B5 k5 J+ M' @ 4 [& @5 Q) ?7 \ //显示蛇的位置 for(temp = 0;temp < GameParamter.node;temp++) Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1); $ R9 s. z/ w( e4 |/ X //吃到食物,创建新的食物 if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0]) Oled_food(); 0 m* d" [1 h! C! q" Y7 N. D) p+ p0 b if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)" A- _7 {. ^: ~+ C( S# w* y {$ _# f N! P! y3 |& y) Y OLEDClear();" A! r7 |& W% r/ @, Y2 ?3 a OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME");. m* b2 r- a' w" {7 n OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER");% o- U. N7 {! ^ State_Flag = Game_Over;, ?5 r5 v" e" ?- G0 O2 I k5 h- D HardwareParamter.OLEDFlash_flag = 1; } break; Y! s3 z! M5 S, U% } case Game_Over:* [: S3 D4 S' ?! Y7 {9 H 5 u+ f3 \2 |7 ?4 {& h' S break; default: OLEDClear(); State_Flag = Snake_Move;" _3 ^! B$ F# e4 X% R1 V; ]6 E HardwareParamter.OLEDFlash_flag = 0; break; } ' |* A+ x& H8 m- g( H( K v 外带一个按键处理,解决方向和暂停重置问题。/ w; a; H, l7 c+ b( ^* N& i9 y. B u 2 T& o3 ?; |+ p* a) @1 |% X7 A# J0 `' Y+ ] |
说起贪吃蛇,以前在大学的时候,学完数据结构,然后就在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)