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

STM32F446+OLED12864跑贪吃蛇游戏

[复制链接]
不吃肉的羊 发布时间:2018-2-7 14:16
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。7 ^# a+ V# m; r) G0 _+ [
% B, _1 C5 ]7 T$ M  F
突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.
: u4 ?) V( K: S( ?" y- o- D
9 C& Z# |+ H5 N于是花了点时间,做了个贪吃蛇的游戏,写了段程序。
  W/ b6 Z4 q- i
* P5 {( Q$ d0 f7 {- i3 d+ {程序硬件支持:
4 `, @1 c. S; z, J/ \STM32F446R+OLED12864+按键
" i, {, p0 i3 h6 _5 a% ?1 }8 E& c9 T! w% z5 E: y: M
软件结构:5 n" S- y; M1 o2 m& s# u# I
1.DMA跑SPI刷屏
. S2 d0 E6 x$ U! B2.贪吃蛇游戏算法! ^' s3 ^; A( q* z
3.重新定义OLED显示方式
. T- r& W' l( q4 e* L* f& O: z4 W0 ]0 J" a2 b& R2 ]
底层驱动就不说,DMA配置,跑SPI数据。
* G% \  ?8 Z6 I' W4 H; r! |$ ^
1 i$ P. D$ ^" m0 m我先做了个二位数组,存放64*128个点,初始值为0;
: G! r, ^: R9 c5 i" {1 {uint8_t OLED_Display_Data[8][128]
' o6 J0 P3 E9 l' F! P7 S8 c5 ?
  p8 Q1 b# K( g  A$ |$ G之后开个定时器,每隔一段时间跑一次屏幕  F; H5 v" ~' Z7 n- U8 U5 h
                if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)
, G  b2 g' [! o) c* P5 b9 o                {; |6 g6 B, ?/ d* r1 R& q
                        HardwareParamter.TimingOLED = 0;
4 }4 R6 f. J* x2 [9 s2 a                        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

$ g6 C5 J7 n  }! ]& B7 f. Z  L# V7 w贪吃蛇算法做的简单:) }( c4 o/ R' L% l
首先是一个结构体,存放相关参数8 l7 U3 C$ F- s3 F

  c% f! r; @/ ?! B, w8 b6 ~( J' J% Q8 ~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

# U: ^, P8 ?0 x6 i9 ~        uint8_t gameEsc;     //游戏开始暂停
1 v& O4 c, _# B) }5 D        uint8_t gameSpeed;   //游戏速度+ l5 E% p$ ]1 P8 n- a
        uint8_t gameLevel;   //游戏难度$ F$ O% f9 F6 e, p9 j. w* i$ P
       
* ~" i% m5 ?  X  W" I# U7 @        uint8_t x[39];
! [4 Y+ n/ Q& O! e& V7 E$ p; o# u( f        uint8_t y[39];  R5 c$ [: |1 M. Y5 p
        uint8_t node;        //蛇的节数7 d' |- E  K1 `9 g) {* `
        uint8_t direction;   //蛇头方向
0 {0 ^1 T& w' j8 V}GameParamter_t;
1 t- F: {3 N5 q( s/ X
- n$ w9 D# ]$ Q) D: E8 J( t% D- _7 B- i
两个核心函数:移动和创建食物
# N. ^6 D. X$ B3 @4 {; P* v食物依赖于随机数生成函数+ b2 ~: D: B6 T& h
//    需要出现新食物3 H2 K% A5 m5 h$ C
uint8_t createNewFood(void)+ a& U3 i; g0 P4 a- W
{
3 O% y! ~4 R2 F  _; R: S* |        uint8_t i;
) }/ U$ I5 E3 p/ Z+ B        uint8_t size = GameParamter.node;
: H1 E: P& D! g! O4 _) D        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的倍数才会在显示屏先被显示
) z' f( `# |/ U- ~" p        for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) {      & T- G( x2 L% Z7 k  ?
                        ;
; R2 q1 C1 p' ]        }
% n; t( ~( B6 g  R          h! Q1 @, [: F- I
        for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) {       . `2 N; g$ s6 {, ^4 Z5 S
                        ;
0 {) D9 ]$ x. C) w% _7 E" }        }# 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) {
6 _6 o" B. h2 ]                        if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {$ Z! j/ O+ t+ L% U+ u. K
                                        flag = FALSE;
9 B0 `7 l8 t6 b; m                                        break;
& B/ ^+ w4 v* I# {                        }
$ V$ m( y6 t1 D$ y        }   
) K  A* Y) G: @2 ]# M; ^
; D" F6 h6 N7 v' d9 ^3 U" [        return flag;0 H) z3 d* D( z5 E  S' F* L4 f
}- ~$ v+ T8 g7 Z. G2 \) p3 ]4 F1 Z

' T; ]: s3 Q8 @  n, d- @: E  F; |- G3 K" J. u5 f" x2 Q1 |
移动就是简单的数据加减
/ a6 \5 U" M+ W4 o1 `//    贪吃蛇移动
4 c2 e' s+ y1 D" I  l4 @9 Zvoid move(void)
7 G) N: q# N; r2 i% b{$ `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)
+ o. i. y% k& h/ o8 p7 U        {
2 e4 K% X5 @1 X! a: Z                //    将蛇从最后个节点向前一个节点移动
& g; Y& E9 L% O, R7 L! Y/ V% v                for(i = GameParamter.node - 1; i > 0; i--) {                    
; s3 x6 r, g. w/ W$ [; @8 L                                GameParamter.x[i] = GameParamter.x[i - 1];8 O5 A7 k9 O) S
                                GameParamter.y[i] = GameParamter.y[i - 1];
$ r' [0 r# j7 H; n                }( G/ k# ~# P& _( C
                ( S# d* {) U$ O1 H- I: z
                //    根据此时贪吃蛇的方向,设置蛇头的位置            - G% ^+ h! C2 O" j. W- K
                switch(GameParamter.direction)   
4 x" p9 U" g/ x! P                {                  
1 R! a- j" F( U& ]& Z7 Q: \- n                        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:
) c0 N6 t8 k2 v% W2 F                                        GameParamter.y[0] -= DOT_L;, d  q9 u: I1 P
                                        break;
7 ^, i. o. g+ M$ ]/ `+ H- V, K6 C1 E/ }                        case DOWN:
1 F; |- i2 L* h! w( G                                        GameParamter.y[0] += DOT_L;! w- C% L% V5 u! d. t+ j
                                        break;, y" z# q' j3 \$ N# [
                }
% o/ }8 R% m& C        }: F' x7 O+ ]" D8 e# ?1 M8 p! J8 z
}
% m+ s$ `  l+ ~, `6 L9 t* i
6 D3 k9 V. ?4 [3 n; A/ w2 Z! Y2 K9 X显示部分随时更新
2 c- C4 w( {! y# R& |, Iuint8_t temp;5 J1 m7 d8 m) g5 j) b; D
       
4 F: W! M, {2 z8 a: `7 D        switch(State_Flag)
/ B8 Q! T9 t- @* e  h        {
* r% Y2 b5 o& a! n; H* V7 L5 V. T                case Snake_Move:
) @. |8 @: {7 R3 ?7 a                        //清除蛇尾部
! n1 Z7 A! |* h                        for(temp = 0;temp < GameParamter.node;temp++)3 S8 O* [5 F+ F
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);
. l* H/ T1 E0 U5 {& t                       
" ~. a0 H8 o- @$ c$ u                        //移动蛇. s; P7 z8 n& x0 F; v( O( D8 ~/ Z4 Z
                        move();1 r2 B5 k5 J+ M' @
                        4 [& @5 Q) ?7 \
                        //显示蛇的位置
5 E! O) G5 v  ]0 `/ N                        for(temp = 0;temp < GameParamter.node;temp++)
7 A. n7 v/ `- N, M  e                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);
6 r; \( x) O( a5 Z4 J                        $ R9 s. z/ w( e4 |/ X
                        //吃到食物,创建新的食物
& k$ B. {# g, o4 [                        if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])
4 T9 h6 u( V4 B/ }                                Oled_food();
, I9 h& B/ M9 g1 X$ ]; P2 J) ~6 A                        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;
2 I" C7 R; w3 n, A                        }
  q2 D2 J* `! L& i                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;
* @6 O% ?1 J' A                default:
* G1 H! i5 x; I  x  ~  \0 D' Q                        OLEDClear();
3 Z/ }& U* t! I+ T                        State_Flag = Snake_Move;" _3 ^! B$ F# e4 X% R1 V; ]6 E
                  HardwareParamter.OLEDFlash_flag = 0;
0 W0 L" m& s! N! P8 o7 X3 o! G                break;
8 P, u" K2 ?8 K; T2 A) Y. U        }
" a7 Y% }/ R9 U9 h$ r+ Z4 `' |* A+ x& H8 m- g( H( K  v

+ g7 ?5 ~/ q+ c3 V7 ]外带一个按键处理,解决方向和暂停重置问题。/ w; a; H, l7 c+ b( ^* N& i9 y. B  u
2 T& o3 ?; |+ p* a) @1 |% X7 A# J0 `' Y+ ]
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 手机版