你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32F446+OLED12864跑贪吃蛇游戏

[复制链接]
不吃肉的羊 发布时间:2018-2-7 14:16
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。
$ v/ h' i0 D& Q8 ^0 G7 U- N0 B: ?& `3 K! L( U; O
突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.6 g% Q/ u. O; r1 ?8 e

3 s$ F# d/ \4 w+ @于是花了点时间,做了个贪吃蛇的游戏,写了段程序。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

" Q' s3 L/ S% r4 w. R软件结构:% U* I) Y1 o& A8 v
1.DMA跑SPI刷屏# S# t  z' h* k+ p" z
2.贪吃蛇游戏算法
9 W6 B& Q: B# N1 E4 s" B0 J3.重新定义OLED显示方式
/ X7 x6 _( i) q6 v" }' 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;
0 w  K6 t8 c/ ^$ Puint8_t OLED_Display_Data[8][128]
/ F' j; O( U2 C) i, h* _+ z7 t6 e* I
之后开个定时器,每隔一段时间跑一次屏幕
+ c: D# {7 x, l                if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)
/ W* v. [% \5 h0 A* Q% G6 o                {
5 t" n! S$ \8 }$ l7 \- K5 T* P9 B                        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
基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。
$ C; Y% d& A. e$ S, l: @  N, L! o' R% Q/ [. f$ F8 g
贪吃蛇算法做的简单:
7 M: O& p  u" C& @/ R首先是一个结构体,存放相关参数
. L9 M" s+ Y' s+ Y. O6 C, c
; U; n, m+ C# I' s) L5 i% Ntypedef struct {
- N2 R- E- F+ o( I8 X        uint8_t food_x;      //食物横坐标- A, e3 }2 {& c  [! T
        uint8_t food_y;      //食物纵坐标
% N; P( q/ [  h5 k0 K% e" d2 A
3 ]+ }. P6 k7 ~1 x" W        uint8_t gameEsc;     //游戏开始暂停
4 G- p( s5 i% E7 z5 P+ b        uint8_t gameSpeed;   //游戏速度
$ T" X* j+ T6 Z/ n5 D        uint8_t gameLevel;   //游戏难度7 f/ ~# D: {( T6 R
        8 l/ A0 ^/ K% S# p; q( D; u
        uint8_t x[39];
: m+ h5 D' p- X) o        uint8_t y[39];
9 |% ]+ b' g7 ?        uint8_t node;        //蛇的节数. ]# T- b5 v9 d7 B, U5 d7 X
        uint8_t direction;   //蛇头方向, u! D2 T6 p9 h9 `4 C5 h
}GameParamter_t;
9 W- Q+ l! R5 y* b
' `' V( v( t& f+ x5 T  {
& G% G7 T1 H( z+ [5 F) O两个核心函数:移动和创建食物
3 x  b6 Q* j) x4 W! M( |食物依赖于随机数生成函数
- w" N1 q- X+ g* ?+ o  f' |//    需要出现新食物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;
9 I7 G; w/ S* H9 i1 e        uint8_t flag = TRUE;                         //    标记创建的新事物与贪吃蛇的身体冲突6 p  o1 [, w! @# J
        srand(HardwareParamter.TimingVal);
8 `4 Z# E7 R  w* G7 i) f9 K& \3 ^        GameParamter.food_x = rand() % MAP_H;0 B7 M7 ~+ ~# Z' H" E4 \- a
        GameParamter.food_y = rand() % MAP_L;
, e1 \% c0 d+ Y9 A/ F- ~       
, m, u% ~7 S; T& I1 p; H: b        //    食物的坐标必须为3的倍数才会在显示屏先被显示, T# W: j. e" B$ L+ D
        for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) {      3 P! C# ^& c8 m2 O7 _+ _
                        ;
* Q. O9 p' F  n0 L" j$ o3 ]3 ~        }3 z+ \1 D/ u- k) R' x1 B
       
0 m1 A  l" g8 N% V, S/ N        for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) {      
' _( r$ t* I2 w) `5 A                        ;# \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) {
3 A  r" t/ z; Y                        if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {8 ]% K' [- ~! L7 ^0 U2 V7 N
                                        flag = FALSE;
4 b8 o2 `* w3 _# m! c                                        break;0 X% ~; ^: d1 D; b7 Q8 A6 p
                        }
* w0 b0 q, w. N' h        }   
+ J4 j8 s  x* |: \
- p" s+ J7 r: G0 y# z5 @3 X6 y        return flag;3 w1 B  F# G, Z1 X4 Q
}
$ H( C. i2 {1 x! |; h: D2 X9 V( o
  ^- Q' O) @1 _1 Q9 M% P1 f" e. N" }: J8 Q1 }: P
移动就是简单的数据加减
. T. V! O, p9 f$ P$ n& e) V//    贪吃蛇移动
( l- s$ |8 ]& P0 M% xvoid 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];
3 v8 Q1 l( I% j( L                                GameParamter.y[i] = GameParamter.y[i - 1];
# ]( Z6 q4 K, P8 G1 P                }
/ c$ v% ?# g% }% P& Z, P2 e               
1 X1 j' B) `, ]$ ~$ r! F                //    根据此时贪吃蛇的方向,设置蛇头的位置            
9 ~0 Z; f1 s# S" K# z                switch(GameParamter.direction)    # d" u' d; X5 R
                {                  
9 [! y) C! A5 |; G6 e+ g; T                        case RIGHT: 7 O0 l' Q' n% A( ]" e
                                        GameParamter.x[0] += DOT_H;
$ l* b4 ^) s# ~                                        break;
4 ]$ G8 \- n8 J9 K$ n. ]                        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;
, x# d- V9 }$ B; g+ X) ~/ U# F                                        break;
; w4 Z( f0 U/ v) R" j                        case DOWN:
$ V; u2 \" K* X+ k0 f+ Q5 W                                        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
                }
  o5 P; P: z: N, g) \        }" {7 C8 v# d( p2 T; w; J
}
/ i# b: m* _- T$ P  K/ G: a: e9 o9 h
显示部分随时更新" U3 W1 s3 ~) u# H4 X% }
uint8_t temp;
- L& o; [" ~! \0 Z& ~       
5 e$ U- E+ f! ]        switch(State_Flag)/ d2 J7 v( Q  L: }
        {
4 t  l; Z# r0 @3 b0 Y                case Snake_Move:7 B- j3 f4 r0 E$ E' L7 v4 v
                        //清除蛇尾部
) z! t6 i( w6 x/ r, c6 Q; Q  r                        for(temp = 0;temp < GameParamter.node;temp++)
, a' L/ x3 t1 }5 A  g3 w& d                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);
9 n  M& C. K! L4 j! t) l2 O                        3 c' ^4 A# K3 c( V
                        //移动蛇% ]) o* `2 Z! C' R
                        move();7 L" T; T9 a5 `$ Y' a- B  _  {4 J/ J
                       
( f& E4 Y! y5 I# ?/ T                        //显示蛇的位置9 ?8 [; X# w7 s# y3 R
                        for(temp = 0;temp < GameParamter.node;temp++)
* }2 B! S( D$ S3 E                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);
) G0 ^- i5 P# D3 G) g% I9 a, Z& ]6 z                        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();
6 |$ T3 a, b3 l& V3 y* W0 l: a                        9 U; O" Q: |5 Z, Q5 q" g) u
                        if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)
: t& ^$ \# W/ L8 r9 [            {
, H; R: K, y+ u8 A                                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");
9 {! Z& W7 S1 Y7 v$ V7 F                                State_Flag = Game_Over;
: I$ S, o) W% i- H1 B; F. n9 O                                HardwareParamter.OLEDFlash_flag = 1;7 L' j7 X1 ?& \
                        }
6 E7 c% _& u$ N, E! A                break;  l! [" `' l- ~
                case Game_Over:/ w' O; ~3 Y# P+ m+ F- r) L

' Y9 ^; D2 B0 t( W1 G/ H2 q8 Q$ G                break;6 E4 \' a) {( b
                default:# T8 w+ r; B4 @* X+ {3 O
                        OLEDClear();
) D' }) r# ?7 E" g$ c                        State_Flag = Snake_Move;: L, x. Q5 W5 u/ {  U8 |. ?
                  HardwareParamter.OLEDFlash_flag = 0;' l5 a+ O* @  h6 z( ^/ e2 [, \
                break;
) f8 W  E/ j1 B/ e  I* M% F        }, r" s' f. d3 \3 n, S  l
1 v6 H1 e$ t: p0 j& ]( t

: W/ B6 g; c% R5 k外带一个按键处理,解决方向和暂停重置问题。
: y: w" Z# J  z# Q' ^2 `, l
( t" S! }8 T# _
1.png
N$TC_KA52ICSCU3@IQJ~OBQ.png
收藏 1 评论7 发布时间:2018-2-7 14:16

举报

7个回答
MrJiu 回答时间:2018-2-7 15:17:28
说起贪吃蛇,以前在大学的时候,学完数据结构,然后就在dos下面写了一个极其简陋的!!!
西点钟灵毓秀 回答时间:2018-2-7 22:21:36
可以试试
hello_bug 回答时间:2018-2-8 08:14:23
有点意思。楼主有时间放个视频上来看看。
zero99 回答时间:2018-2-12 14:17:18
感谢分享,已汇总到2月技术原创  https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html
gdyn2 回答时间:2018-8-21 16:22:06
程序下载链接木有?
xuetudou 回答时间:2018-10-10 16:55:48
楼主,想知道蛇头方向改变蛇身呈L或者类似矩形方波这些不规则的情况下蛇的移动部分的代码
Litthins 回答时间:2019-4-17 21:57:54
最近正在捣鼓446,等手头的事情告一段落也想试着搞一个
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版