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

STM32F446+OLED12864跑贪吃蛇游戏

[复制链接]
不吃肉的羊 发布时间:2018-2-7 14:16
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。
: M0 v2 T! t& {  r
; N) T% U6 r. e# {! Q2 J突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.
+ E4 J) ]- a7 b) _1 ~; J& N# x
' O; n1 `. S. s& j4 k+ q. a于是花了点时间,做了个贪吃蛇的游戏,写了段程序。
+ M6 g3 ]* Q. W2 C7 c( m  y7 R+ M0 O4 p% K1 U
程序硬件支持:
1 Z. x8 H2 B8 P2 O8 D& @3 |STM32F446R+OLED12864+按键
8 J- ]4 }& p$ N5 G
; _/ U! m( J) o软件结构:
; Y: r, A  Q5 ^. ?, ]/ A1.DMA跑SPI刷屏2 F9 T7 r9 e, ^; U
2.贪吃蛇游戏算法
0 ?/ s- }2 J1 F6 h# z3.重新定义OLED显示方式
. k$ \2 ^5 n% o7 L+ c9 G9 t! z/ h! E
底层驱动就不说,DMA配置,跑SPI数据。
7 [1 |# G5 c& m
% Z. r% e9 N$ K0 [; o! m  U8 w. I3 k我先做了个二位数组,存放64*128个点,初始值为0;4 |2 U" |+ |& G# c+ V, w- U
uint8_t OLED_Display_Data[8][128]+ s0 D! W1 F5 p( y4 f

6 O4 k: [; E4 |4 X& z之后开个定时器,每隔一段时间跑一次屏幕
! R6 }# u# {- j* U7 m8 R$ V                if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)- I0 |* }  L6 X9 h5 P- j
                {
/ d4 A  x- _6 F, s4 q0 V+ V2 E7 E                        HardwareParamter.TimingOLED = 0;# J; m0 i% k5 b+ ^- M' ?
                        Display_Process(OLED_Display_Data);        & D  a6 _: p- H
                }
/ W  w! c9 ^& d$ |( v( A
3 e6 v# q2 L2 M! X% [" x" Y8 e
% {" Y- h+ K3 r. j基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。
: s7 t  J5 }* M6 S$ U$ K
+ h7 B4 T' ~0 U! h* K贪吃蛇算法做的简单:- _- _8 ?9 Y9 n) H  o
首先是一个结构体,存放相关参数
* \$ ]& g5 R4 Q
) u- ?" U1 o, m2 h: z2 e% Btypedef struct {! {. |" E+ s! k3 X
        uint8_t food_x;      //食物横坐标
! M' Q+ P7 u. a- I        uint8_t food_y;      //食物纵坐标# ^4 F6 {7 {1 d6 I  z+ D
: H$ k: o2 o* C7 u. F$ r
        uint8_t gameEsc;     //游戏开始暂停
3 J# |/ [! A5 l7 z        uint8_t gameSpeed;   //游戏速度" T6 ]' l# T# f6 R. e( j% b3 p
        uint8_t gameLevel;   //游戏难度, \! N8 b* y$ ?, ?. E
        5 B( n' [9 U) }+ T* b
        uint8_t x[39];4 Q8 Y! W# \3 f! @; }/ E
        uint8_t y[39];
) r% n- m# a; U# D- A/ e& ~        uint8_t node;        //蛇的节数
2 q" x* ^* R/ _4 D5 H        uint8_t direction;   //蛇头方向- w- D/ a1 A/ V5 J! T. [/ f
}GameParamter_t;
: ]+ `+ Y2 r% S  h6 n7 v
5 G; L2 J6 h% s" W" O/ r3 H# [& y. s* M- _( g
两个核心函数:移动和创建食物
; o/ G- u: U( c5 g6 Y; j5 k食物依赖于随机数生成函数! A1 W# L) y1 ]) R# b) {
//    需要出现新食物
+ S- i: a- `! C- N% Iuint8_t createNewFood(void)3 A" {9 Z/ P  v
{3 {. c( f8 h3 B% R5 y
        uint8_t i;
* _% B: g% Y4 I: U. M        uint8_t size = GameParamter.node;
  D, @! F' [1 R& B  l& W        uint8_t flag = TRUE;                         //    标记创建的新事物与贪吃蛇的身体冲突
: B- C/ W( z) Y! n: t        srand(HardwareParamter.TimingVal);$ l" S" t; e5 A6 P; S  A, w# i% ^
        GameParamter.food_x = rand() % MAP_H;9 \! U1 k% ^0 A+ l  O& G( z, P8 K
        GameParamter.food_y = rand() % MAP_L;) f- z7 ~6 I* s. N" V$ ]$ ~7 @
        ! s8 G  U' R9 n  t% d# G
        //    食物的坐标必须为3的倍数才会在显示屏先被显示! a' M6 m* M* k: @$ g1 I% ?
        for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) {      
. d% S- [' A4 ]+ z. ^; k9 e0 z2 `                        ;
. U2 w2 I% Q) `        }- d, A# s& M$ u; z2 e
       
& G7 N" C8 Z6 Y2 o% X        for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) {      
0 d0 i! s; d4 P. }                        ;
$ z; {( ^, S+ K        }
/ T! [- ]' y; w       
( A7 l  n3 s9 g6 @# L        for (i = 0; i < size; ++i) {
: k' C# r, ?  `                        if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {
( A2 _3 D1 k9 x, A  M% v) N                                        flag = FALSE;/ C3 B6 E; w# l! \8 T2 Z
                                        break;
6 d. K" X2 \3 z4 z& G                        }
7 D4 ^6 W+ T% _- ]: w& V        }   
! n. D0 y+ q& H- ~9 ~; h) V7 [2 g7 c" v
        return flag;
3 m0 J7 V$ e2 J% d% t/ N% x1 U}, X" z+ |2 j6 c6 ]) r

& q/ E6 L* O: }
' F! v3 Y; x' d. ~移动就是简单的数据加减; j; b0 b% s, ^' Z( U3 M2 |4 g
//    贪吃蛇移动
% \' {2 m& Z& T- T1 tvoid move(void)
8 R7 S9 e) E  r6 d) H{$ J# Q$ }1 T  s9 `  U7 b' c% [2 N2 m
        uint8_t i;
( F: e; Y7 L' S8 T) y
' Q7 W. x0 K! V& r* ^' H        if(GameParamter.gameEsc)
! {9 {1 q' D0 s3 A+ v/ K  k5 c7 O+ \        {
( f# p) p$ m( L, U                //    将蛇从最后个节点向前一个节点移动. L- X/ o/ B0 Q* b; d
                for(i = GameParamter.node - 1; i > 0; i--) {                    
+ |+ r4 M: F0 G* g- f                                GameParamter.x[i] = GameParamter.x[i - 1];
# |0 d* m8 [+ p! O+ K                                GameParamter.y[i] = GameParamter.y[i - 1];
  C# {- d4 W  K% F- s                }' B# t) |& s0 F+ v) r3 K3 W
               
6 M: i& ]6 g! w( o" I2 d                //    根据此时贪吃蛇的方向,设置蛇头的位置            8 y# [' \# _5 a4 O, n
                switch(GameParamter.direction)   
9 ?) U& ^! V& G9 Z. B. z+ F                {                  1 N% R, v9 P! q: K4 D, P0 W
                        case RIGHT:
) Q6 k  j% ^: l7 H+ D) ?                                        GameParamter.x[0] += DOT_H;
; B: b0 y7 g! d* m3 x                                        break;
6 n  V/ W& g6 n                        case LEFT:
- c; C$ Y3 O( t$ @                                        GameParamter.x[0] -= DOT_H;" C- M/ S6 v' F3 A7 ?: v- h1 ?
                                        break;: }- x3 h( I2 `
                        case UP:
- ?: V6 u( O+ S  ^) v5 Q                                        GameParamter.y[0] -= DOT_L;* ]4 Z* ^2 w- s' d) b& h2 R2 k7 Y# \
                                        break;3 G; t/ i5 q1 y$ P; Y- a
                        case DOWN:
: I9 f7 S2 H: ?) }2 {, E$ B( L                                        GameParamter.y[0] += DOT_L;
" U: c7 `& o* D% m4 ~                                        break;
- A1 m5 G/ J3 C9 k2 C, {9 F9 P                }6 X2 L7 h3 h8 I& q: _+ j: w' N
        }
3 _$ m2 [4 K. J+ I1 G4 m) \6 l}
: ?/ J3 t9 S1 k9 e" a  _& C; c" R: \* r7 k6 L+ G8 [  K# O/ K' |. F
显示部分随时更新: {/ R( i! p" D/ k& E3 }
uint8_t temp;
) |" N! @! h4 w7 C, R+ A1 y) \        # s0 ~/ e" j8 D6 h2 n! t5 T
        switch(State_Flag)
7 _- X9 ?5 C/ M6 ]6 L  q        {$ T, _5 S: O5 y: Q" B& u* j
                case Snake_Move:
9 ]0 @# H2 _* M  a* }3 N* S5 f" X                        //清除蛇尾部  n+ y6 C& l  b" P$ f; `; n; }! g: y
                        for(temp = 0;temp < GameParamter.node;temp++)
+ Y8 Q* A4 Z3 y# S' A                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);
1 j# p4 s1 P" Q                        " @9 ]! u4 d6 w, b% ]9 o; U$ \/ h
                        //移动蛇
7 B/ w* u) e5 `5 N                        move();
$ c. R* E, [9 G2 ?4 k  @7 p9 v                        + ~5 @3 E4 d. O/ p$ T: n( r
                        //显示蛇的位置
; B. n$ k5 A6 p/ a+ C8 s/ h- w9 x# [                        for(temp = 0;temp < GameParamter.node;temp++)6 ^7 e/ B* S1 O. |
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);" v6 n7 P1 N6 B; \- ^% |2 q4 J9 K
                       
" S4 ?# E6 Z. }1 v) C1 j                        //吃到食物,创建新的食物
( k; d# n+ ~; f- y5 q7 s2 z. s                        if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])
) c/ o( l# I1 s5 r                                Oled_food();' S) o9 K. R, [* t: o
                       
, e% P5 T% z3 L1 y- d                        if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)
) n. ?1 I/ l) v1 ]* e8 B) z            {$ b, `7 {2 t, T9 U/ V
                                OLEDClear();
. H1 Z, x5 s/ r' W3 k/ s/ W3 b) Q                                OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME");( }# e3 p& N# |% I' g* B) x1 e/ V  }  W1 }
                                OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER");& Q+ c! [5 G' h+ H+ l
                                State_Flag = Game_Over;
2 R2 D+ y* N+ `                                HardwareParamter.OLEDFlash_flag = 1;! f4 Y' A- e, P' I& b1 B2 ]
                        }" T6 a* H  Q9 c" r" y8 Y( o
                break;1 }) o. I  u# d. p
                case Game_Over:
4 V. u3 S/ s1 o6 q$ d
, m  x  W# J0 Q                break;" m9 Q$ j& e& P
                default:' K4 D; H! m! w  v
                        OLEDClear();
9 j( t% B: x3 J3 a5 P" |, `                        State_Flag = Snake_Move;
: q/ F8 s  t7 w5 G. o# h                  HardwareParamter.OLEDFlash_flag = 0;
  x: i* g  o2 M9 [5 p# c; t0 i                break;
& J* S. p! X2 s* t2 T, _* J        }
, ~! G/ j: b) N
4 U( m- D, g5 ^2 l4 x2 ^/ S9 e7 F6 G) T$ g9 j8 s% n8 J
外带一个按键处理,解决方向和暂停重置问题。5 I' I+ Z$ y7 J' I. G
9 E& n: U- y. R9 E
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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版