闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。 突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7. 于是花了点时间,做了个贪吃蛇的游戏,写了段程序。 }- P& i! y( }, B9 F 8 q3 v, n0 q& S1 I, Z0 Z 程序硬件支持: STM32F446R+OLED12864+按键 软件结构: 1.DMA跑SPI刷屏/ o. i$ k0 G& i' H+ G$ ] P 2.贪吃蛇游戏算法7 b" u, R' f) ` 3.重新定义OLED显示方式 底层驱动就不说,DMA配置,跑SPI数据。2 u8 B6 w; F. j. l9 G# e5 v 8 R7 Z: N8 _$ |( d* [0 x3 e! N 我先做了个二位数组,存放64*128个点,初始值为0;( P" ?$ D/ x: O0 u# b8 O uint8_t OLED_Display_Data[8][128] - n2 r! i0 b* ` 之后开个定时器,每隔一段时间跑一次屏幕' M# [# C7 a1 P z. k, x) U if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag) { HardwareParamter.TimingOLED = 0; Display_Process(OLED_Display_Data); ( ?( A7 L. B" ^$ R$ m9 t } 基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。 & |4 l' V! |9 s2 v8 L 贪吃蛇算法做的简单:! G7 Z/ E# q2 p) T 首先是一个结构体,存放相关参数 typedef struct {. Q* W* m" @% ]1 [$ }6 | uint8_t food_x; //食物横坐标7 L: C. \2 A, G/ q1 [3 N2 W3 G' ]$ D* } uint8_t food_y; //食物纵坐标 2 l2 z5 U8 m" n3 x1 M, m- C! L uint8_t gameEsc; //游戏开始暂停$ I8 J3 Q# a" f3 G+ Q uint8_t gameSpeed; //游戏速度) d6 h) k9 @7 G$ t: a; q uint8_t gameLevel; //游戏难度 uint8_t x[39];8 b$ x' P$ q. x2 \* k0 K uint8_t y[39];* g" M8 N! j& I; ]! I9 v uint8_t node; //蛇的节数, }' W- d5 x* r( j' j8 I! C" k" i uint8_t direction; //蛇头方向 }GameParamter_t; + \ a& ^( A4 H B8 _8 X* N 两个核心函数:移动和创建食物" R: t1 j& ~8 {+ H/ H 食物依赖于随机数生成函数 // 需要出现新食物 uint8_t createNewFood(void)0 }* S; B5 X1 x$ n {2 U. H4 m# i! U; R uint8_t i;0 r1 I0 m) @- |# \5 ]0 G) G uint8_t size = GameParamter.node;2 i4 H) N: b( Z uint8_t flag = TRUE; // 标记创建的新事物与贪吃蛇的身体冲突 srand(HardwareParamter.TimingVal); GameParamter.food_x = rand() % MAP_H;: c1 f) v5 C5 J5 v0 i6 [. s GameParamter.food_y = rand() % MAP_L;4 b4 S/ `! _$ ?3 n; D // 食物的坐标必须为3的倍数才会在显示屏先被显示 for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) { / G5 f0 z7 N, c9 m ; } for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) { % L% @/ L, x: Y7 ]- d* Q ;6 o8 w0 Q& _! c1 y }( p: g# O7 k7 N9 L for (i = 0; i < size; ++i) {9 \8 _, a; O6 [# }" c8 ~. V# k1 u- j if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) { flag = FALSE; break; }0 y* \9 H- L4 g) d( e. U7 ?/ Q } return flag; } ! c9 O8 b* H3 y/ g& y% a$ C 移动就是简单的数据加减 // 贪吃蛇移动 void move(void) {: M+ Q) h- f: \( O+ ] uint8_t i;0 x; _& T; Z7 R9 ^0 y5 Y k9 g" U- k4 l% o$ l" R% | if(GameParamter.gameEsc)# n" f9 k; }; W/ J' [5 f { // 将蛇从最后个节点向前一个节点移动. A8 y; y- G, S# |/ @4 G5 q for(i = GameParamter.node - 1; i > 0; i--) { GameParamter.x[i] = GameParamter.x[i - 1]; GameParamter.y[i] = GameParamter.y[i - 1];& r& u1 `5 b/ H3 d( P } 7 p6 r: C1 ^2 _" p# C6 Q0 X. O: ?: O // 根据此时贪吃蛇的方向,设置蛇头的位置 , R: h( U8 y h4 U2 {( X switch(GameParamter.direction) { 2 U7 H! X \3 N! t/ [5 z! T case RIGHT: $ X5 f* E- [4 r8 J$ ] GameParamter.x[0] += DOT_H; break; [5 n) J5 Z8 b6 k case LEFT: GameParamter.x[0] -= DOT_H;9 p4 `; ^( f! k$ z break; case UP: 9 y5 l7 a, C! J9 b5 _$ ?; r0 i GameParamter.y[0] -= DOT_L; break;0 p' U" z8 _+ i* B. `3 n; j7 f case DOWN: 1 U( O! u5 g) o1 Y" d1 j GameParamter.y[0] += DOT_L; break;% b& B Q7 ?. P- \ }5 k4 O. u: G( k, N& M- c } } " V; i! H$ `3 S8 L. n6 q 显示部分随时更新 uint8_t temp; switch(State_Flag)% h+ ]8 X/ T/ K' W8 f { case Snake_Move: //清除蛇尾部 for(temp = 0;temp < GameParamter.node;temp++)$ D2 L# [' o* ]; S) L5 a `6 d; R Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);6 @& |! U+ ]! N0 p3 z 5 B& t) @& P( H+ @* J* k! Z //移动蛇 move();: ?% n. e/ x1 h- {( F //显示蛇的位置0 j# H' `+ ~$ Q+ a% [ for(temp = 0;temp < GameParamter.node;temp++)) E" j1 G0 y3 l, Q Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);4 U2 ^4 V6 @! s4 K$ R * W6 a* _5 ~7 W% O h //吃到食物,创建新的食物9 V" [* b- U6 R if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])+ V% j% Z% w# J, x# o! d Oled_food();; x+ I% |+ z! d: R. A4 t [ if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)+ Q0 m) B4 a( T {4 y1 S9 p. m2 K6 ? OLEDClear(); OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME"); OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER");/ |* u* G, j @& S! e State_Flag = Game_Over;# H1 S" @* A7 [ HardwareParamter.OLEDFlash_flag = 1; }3 \: `7 T# f) p" _ break; case Game_Over: 8 v+ h% }0 q; U8 M% A/ H' j- N break;: T: R( u3 D1 M default:0 t' g: u: Z K" L0 N OLEDClear(); State_Flag = Snake_Move;+ A# z- R7 T5 t8 X, j/ b% [ HardwareParamter.OLEDFlash_flag = 0; break;! M9 p4 t1 Y0 `% s+ Q' @( m }/ @* K5 u7 v+ Q2 r& P, V 外带一个按键处理,解决方向和暂停重置问题。& |: g% G. B0 Z ) [5 o. k B9 Q+ ~ |
说起贪吃蛇,以前在大学的时候,学完数据结构,然后就在dos下面写了一个极其简陋的!!! |
可以试试 |
有点意思。楼主有时间放个视频上来看看。 |
感谢分享,已汇总到2月技术原创 https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html |
程序下载链接木有? |
楼主,想知道蛇头方向改变蛇身呈L或者类似矩形方波这些不规则的情况下蛇的移动部分的代码 |
最近正在捣鼓446,等手头的事情告一段落也想试着搞一个 |
【中文文档】AN3965_STM32F40x和STM32F41x基于串口的IAP
STM32F4-DISC 实现USB主机(U盘)和USB设备(虚拟串口)自动切换
STM32固件库分享,超全系列整理
STM32F4中文用户手册
基于STM32F407的FreeRTOS阶段性的总结(13)
STM32F400、STM32F402 Cortex-M4超值单片机
基于STM32F407的FreeRTOS获取各任务运行时间及占用情况(4)
基于STM32F407的FreeRTOS任务的挂起与恢复(3)
基于STM32F407的FreeRTOS任务的创建与删除经验分享(2)
基于STM32F407的FreeRTOS环境搭建经验分享(1)