
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。& f, e* F0 g3 i/ F% b ' A' K2 ^$ [6 N. Q8 Z! K8 l 突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7. " {) K+ F( P. d% [) H6 E+ q# F, J 于是花了点时间,做了个贪吃蛇的游戏,写了段程序。, X# B J. Z9 J4 B% D ' D b; Z& G, |1 V- ? 程序硬件支持:5 \! }( }: Y! S2 Q STM32F446R+OLED12864+按键" J8 D. F# d) o! |2 X 软件结构: 1.DMA跑SPI刷屏 2.贪吃蛇游戏算法 3.重新定义OLED显示方式 底层驱动就不说,DMA配置,跑SPI数据。 + t' e+ |! o% _! J% N0 E' N 我先做了个二位数组,存放64*128个点,初始值为0;# {7 z3 V8 V* M uint8_t OLED_Display_Data[8][128] 之后开个定时器,每隔一段时间跑一次屏幕8 B' E; h" \0 K if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)3 D& [: [) ^+ n1 K4 ~ {4 }0 _5 `' R$ u7 A HardwareParamter.TimingOLED = 0;8 R) @. X3 f2 V+ P Display_Process(OLED_Display_Data); 1 D; W2 Y8 n# n2 M, v6 g; P5 j# M } , o- k; W9 u3 T8 r7 v9 i 基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。+ A0 [' b( d- E! N- }, u 7 a, Y* O, A4 u% T$ t2 B( t9 e 贪吃蛇算法做的简单: 首先是一个结构体,存放相关参数 typedef struct {- ^2 Q3 C( o% F0 f% g1 j. l* z uint8_t food_x; //食物横坐标- H! O! r; V6 n* l, h5 F j s' U uint8_t food_y; //食物纵坐标 uint8_t gameEsc; //游戏开始暂停 uint8_t gameSpeed; //游戏速度 uint8_t gameLevel; //游戏难度! C8 p% w% u4 d/ c1 b: P8 s: x & X' J& d* q* e$ b# e' Y& L- J uint8_t x[39]; uint8_t y[39]; uint8_t node; //蛇的节数8 o7 ~8 \9 y$ ~/ A1 d( T- N% B uint8_t direction; //蛇头方向: r9 ]& y _3 @: M( N' v }GameParamter_t;& g4 _% t0 |3 K) Y; F0 U9 |8 Q 2 ~, R; l0 k) N4 e( ^* q 两个核心函数:移动和创建食物 食物依赖于随机数生成函数9 \* a) H+ e2 O6 \& x- e // 需要出现新食物 uint8_t createNewFood(void) { uint8_t i;( w ~& F+ ]9 X. ?# a- o' t uint8_t size = GameParamter.node;7 r" ^0 o! K3 h7 K% {- j uint8_t flag = TRUE; // 标记创建的新事物与贪吃蛇的身体冲突9 j" G+ h* z5 E# k1 n srand(HardwareParamter.TimingVal);( { J2 V; r1 k1 l: Q$ N5 ]) ] GameParamter.food_x = rand() % MAP_H; GameParamter.food_y = rand() % MAP_L; // 食物的坐标必须为3的倍数才会在显示屏先被显示 for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) { ; } for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) { ; A5 U* a. A7 a) M& m. F } for (i = 0; i < size; ++i) { if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) { flag = FALSE;3 e; `6 \/ w) F9 I3 w break;; `; G! |+ w2 m: k2 ]7 B0 ~ } } return flag; }' T/ | A( P8 w4 L5 J8 K1 M! R " r' Y" K% s, R# F% A! i/ O - ?" T8 b, |4 J. M0 U( V 移动就是简单的数据加减1 o1 p N1 L% P% a$ M' x% G // 贪吃蛇移动5 x! I# `2 U; U2 ~% s; ^: a void move(void) {9 o$ b# ?; X! w8 o+ p! v uint8_t i;- D( O9 \; V( b5 t if(GameParamter.gameEsc)# g2 `& H- Y: n" H {9 u8 g6 p* a+ J1 A1 h // 将蛇从最后个节点向前一个节点移动 for(i = GameParamter.node - 1; i > 0; i--) { GameParamter.x[i] = GameParamter.x[i - 1];- {9 e, p) e2 ^$ a, C) E. U1 a- D GameParamter.y[i] = GameParamter.y[i - 1];, i7 T% Z! v1 B! Y4 V4 U }/ }* ~5 `& f+ l. A! K& W( @ % N' m4 C$ t1 i) c // 根据此时贪吃蛇的方向,设置蛇头的位置 6 k( W5 S4 a0 C- D switch(GameParamter.direction) { case RIGHT: GameParamter.x[0] += DOT_H;# y e* E& c% d6 q$ K9 \" _5 v break; case LEFT: GameParamter.x[0] -= DOT_H;# K; R! J/ v/ d break; case UP: ! f$ N- c2 Y* ?0 O1 r GameParamter.y[0] -= DOT_L;8 Y+ h+ Z5 U* t( P- \7 u break;/ k$ I. P- M& v/ I, ]3 o' V case DOWN: GameParamter.y[0] += DOT_L;$ Q9 @9 A; z/ t8 @1 F0 s break; } } }4 R: n- C1 i7 n! v! s . L9 |" r0 e+ ^; Q5 B# Q$ L m# e' h 显示部分随时更新 uint8_t temp;1 V$ G4 I, O- L) u& [# k* Q9 a switch(State_Flag) { case Snake_Move: //清除蛇尾部 for(temp = 0;temp < GameParamter.node;temp++)4 ~' |- m) b# H Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0); ?. M2 H) W. M //移动蛇 move(); " e- w3 E2 J; U1 U8 ?' w& I& ~ b9 F. n //显示蛇的位置+ p2 i# A2 N2 B+ _& a0 y for(temp = 0;temp < GameParamter.node;temp++)6 t+ n" [( b7 F5 I. c Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);$ D, P' h* l- O& P+ t& C; v //吃到食物,创建新的食物4 ?! g" l9 ]3 m2 D: g, U& h if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0]) Oled_food();4 E- g6 k- N6 U$ H' \- j : G9 \' }0 Q; i$ p3 l# p5 z if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L) {0 a* y* y1 j! G OLEDClear();8 ^8 d8 s9 r5 t8 P S. T- \% U OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME"); OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER"); State_Flag = Game_Over;! o7 b6 n" h0 u3 e( o9 d HardwareParamter.OLEDFlash_flag = 1;7 ~& M( K& |- Y: n; t0 F } break;+ E/ b& G# E! X- U case Game_Over:& x8 U( ?# W- u) | break; default: OLEDClear(); State_Flag = Snake_Move;7 S. V1 S8 y/ b. X0 J; X3 @! u8 D HardwareParamter.OLEDFlash_flag = 0;. R, n' b2 Y1 u9 V0 @$ w break; } # n* m9 v$ I! Z' E3 [4 x 外带一个按键处理,解决方向和暂停重置问题。5 f0 Q7 J( h0 a& T, }, ~ |
说起贪吃蛇,以前在大学的时候,学完数据结构,然后就在dos下面写了一个极其简陋的!!!![]() ![]() ![]() |
可以试试![]() |
有点意思。楼主有时间放个视频上来看看。 |
感谢分享,已汇总到2月技术原创 https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html |
程序下载链接木有? |
楼主,想知道蛇头方向改变蛇身呈L或者类似矩形方波这些不规则的情况下蛇的移动部分的代码 |
最近正在捣鼓446,等手头的事情告一段落也想试着搞一个![]() |