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

STM32F446+OLED12864跑贪吃蛇游戏

[复制链接]
不吃肉的羊 发布时间:2018-2-7 14:16
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。! f3 G* _+ U9 K9 i

: @% P% X# j. U$ N$ V4 |- C突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.
2 N4 i$ Z) V5 K6 ^
* H# m8 B- |3 p于是花了点时间,做了个贪吃蛇的游戏,写了段程序。8 ^" \4 P6 a0 k4 M# K6 W( [

8 ~! c: N$ C" j程序硬件支持:$ O$ |3 z1 m. `7 u1 ^- X, @# \
STM32F446R+OLED12864+按键
" \) k4 S- c6 }* _/ S; F7 Y
$ Y1 C* M7 K; Y软件结构:5 J- {) B- R0 |% l$ H# U- L
1.DMA跑SPI刷屏# l8 z3 T* R7 h1 }
2.贪吃蛇游戏算法# b$ u0 J* p( n# }' w2 m
3.重新定义OLED显示方式
. R6 i3 s! n: N- ^
; q3 i# }, `! B# @) S- L( f底层驱动就不说,DMA配置,跑SPI数据。( b" c' _$ _; c
7 k4 a: g6 {2 Y$ n2 O5 k6 R
我先做了个二位数组,存放64*128个点,初始值为0;, g) X2 W8 Z( U7 k% g4 N3 o8 l
uint8_t OLED_Display_Data[8][128], V- w7 G" C4 s( u6 a0 f5 T1 j

3 Q" B( T9 D2 c  @: ~. O* G之后开个定时器,每隔一段时间跑一次屏幕& O& o# v- r6 D
                if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)
' _8 n2 U/ n/ C) U% X% Y                {7 o' d5 Q9 M, C2 T- U* t) ^/ l
                        HardwareParamter.TimingOLED = 0;
0 x: s8 V0 e3 J                        Display_Process(OLED_Display_Data);        : B/ l, n5 `; \7 y+ S
                }1 A! t2 S: H1 s" x7 H3 u! F+ x
) A/ p7 n9 X( G/ p+ R: ^

8 G" V! x2 y/ E) Q$ G' ?基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。; l% D5 l0 ^' r6 s% _" Q( }

1 x/ v- k9 H5 r9 C8 ?! f贪吃蛇算法做的简单:
+ A5 ?& I; `9 g! j首先是一个结构体,存放相关参数: [& h. S8 [" C0 x0 q$ ^! E

: T4 U5 [+ f2 j) n; N% \- ktypedef struct {+ S" k' q: o/ V
        uint8_t food_x;      //食物横坐标
3 u* v6 e1 p! A+ e7 ~% d& `1 U        uint8_t food_y;      //食物纵坐标, O- w& Z; }" B! ?! s* |8 S, {; m1 C" l

8 K$ B/ ^" i  @7 D& T% s        uint8_t gameEsc;     //游戏开始暂停
5 X! w$ [6 r3 v, B0 {2 s        uint8_t gameSpeed;   //游戏速度
8 @* D* X$ w' ?+ O6 d# X        uint8_t gameLevel;   //游戏难度3 C2 v/ J8 {, P( c6 G  q
       
0 V; D( R5 D0 i3 V- Z        uint8_t x[39];
9 X7 Z( y6 {. P' a2 w" W% J        uint8_t y[39];  O: B9 ^' a( C, ~
        uint8_t node;        //蛇的节数
6 ^+ P: }2 Y. x) Y1 `. q        uint8_t direction;   //蛇头方向
3 x/ L$ c) V- F" S9 Z}GameParamter_t;, T: n5 x3 H$ ~: @

  H' R, Z# \' Y! v. [6 ?
1 q8 J; L8 v- h( c两个核心函数:移动和创建食物
3 }3 _; I* v7 A; q食物依赖于随机数生成函数) I/ u0 R# y# V7 u3 V' c
//    需要出现新食物5 N) @2 H0 b3 G+ C2 e$ V1 z
uint8_t createNewFood(void)
  C- F4 f" j! S" o9 M8 \2 B" b3 `{- h' k+ ~- A6 A6 f* u7 t# c4 H
        uint8_t i;2 d; ]2 @* }1 `5 L8 D2 j5 Q+ q" M1 s
        uint8_t size = GameParamter.node;0 `5 P3 W; |- C; }( l
        uint8_t flag = TRUE;                         //    标记创建的新事物与贪吃蛇的身体冲突# |6 M- s: N4 P. S
        srand(HardwareParamter.TimingVal);9 i% e: q% y# {# m2 F3 `
        GameParamter.food_x = rand() % MAP_H;
) H6 P4 f: N' x% G3 u        GameParamter.food_y = rand() % MAP_L;8 R* X- F+ _/ P  `! X: }0 B3 a
        1 U  c+ Q3 v* A! [
        //    食物的坐标必须为3的倍数才会在显示屏先被显示
7 h2 N' ], g8 R' X0 D        for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) {      
  N6 X( w8 U! q                        ;+ C5 g3 A1 Z- Z  {* v
        }7 J; B  i5 V1 A" I
        ( n5 c; [' U$ x( h  G! K" u
        for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) {      
( f9 t* n# C5 d( A- S' H                        ;% N, H( v& K) L+ a, s9 ^# W
        }
* e* ^8 z4 F3 z$ |       
% i1 Q1 }0 K3 z: f        for (i = 0; i < size; ++i) {
% q  b3 j  j6 S; P8 d                        if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {
( f) W' R4 k3 O. N1 y                                        flag = FALSE;
, b0 g) j; Q" F# Y6 J                                        break;" d2 I$ `  P8 _6 w8 M' G: \
                        }
( t2 k% r1 A2 W; M* e        }   
  l/ b9 G2 z% Y, X5 y* K. A
8 l0 X# n% B% q2 R        return flag;
! O! f3 m6 T+ Y}) b2 ^* Z) f% f. S9 o
7 E1 O' e; G3 ], H3 \$ W, k
. b1 H3 A/ x2 |  N
移动就是简单的数据加减: J! o) L5 X+ X3 o8 Q( `
//    贪吃蛇移动
3 v. [5 t1 Q  T& V6 ?' Nvoid move(void): F% j9 x* g! ?6 l" p
{% b" S; _8 w( |/ R9 k8 S
        uint8_t i;8 D( [1 a! ~5 z0 X( r
3 H: G2 t2 n  ~- }" y. c& {0 _
        if(GameParamter.gameEsc)( D" c# i2 m0 F" A- K! ~. \
        {
( K4 Q# F8 S* m/ x# }                //    将蛇从最后个节点向前一个节点移动
0 A; \; j0 ~  O                for(i = GameParamter.node - 1; i > 0; i--) {                    
/ R/ q' {( h# N0 _0 s6 J6 P8 W) f: `                                GameParamter.x[i] = GameParamter.x[i - 1];* `# ~# N7 [3 F+ c
                                GameParamter.y[i] = GameParamter.y[i - 1];% O5 A% e8 ^. a) l" H
                }5 o. _. U8 p8 N# Z6 \0 {
                3 r1 q3 _3 ]+ N" I! X. h: N
                //    根据此时贪吃蛇的方向,设置蛇头的位置            . E2 j6 ?+ i0 ~
                switch(GameParamter.direction)   
! D' w( H, ]' x+ g% X. r                {                  - C) d3 D$ t* F
                        case RIGHT: ! a8 T3 H9 i3 V" F: c
                                        GameParamter.x[0] += DOT_H;
4 ^- N1 I. Y9 b" |                                        break;( ~0 j1 J6 U8 P# f
                        case LEFT: ) s  v. d1 y; G+ Y# Y3 f. P* {! Q
                                        GameParamter.x[0] -= DOT_H;4 d8 y: [* U/ z8 s. b! _2 ]
                                        break;
, H0 t# Q- w( I& d5 N) J& C- t3 f                        case UP:   ~: P: [4 V$ v( a
                                        GameParamter.y[0] -= DOT_L;
/ Y- t' s) K4 y: Z' ?1 F                                        break;
! t5 i" f* r& j0 R; W9 }                        case DOWN: 6 U% M6 s/ e: S1 ?( v6 Q5 O) O/ {5 G
                                        GameParamter.y[0] += DOT_L;6 N& w+ r2 S0 d# L2 w3 m1 e
                                        break;! U8 n# r% j# u1 n
                }! C" _7 s" q( i9 ]2 B0 ?
        }
) [# x' z' Y  N}
! q5 L' \7 y/ ^8 n7 S: R! [* R
1 [/ A/ y: O% ]3 ?显示部分随时更新" l, E. v/ Y0 Z+ w' Q0 I
uint8_t temp;( K: ]$ B8 h  w4 K  s% O
        3 ?) \, B1 f- o7 t
        switch(State_Flag)
2 m+ h2 {7 R/ f7 R' G' J8 j" x# q        {
+ V# H2 ]+ M6 y. W) P) w& w9 @6 W                case Snake_Move:
' q% s2 i+ z* H0 m) U                        //清除蛇尾部% [* O* |0 U& Y
                        for(temp = 0;temp < GameParamter.node;temp++)
) m% G+ \+ P9 m. _4 E9 b6 M3 L                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);
  X/ A5 t- x) h7 \9 ]7 j                       
$ B" |' A3 F2 e) G                        //移动蛇# f2 _4 s. \8 T# C- ?
                        move();3 I* R; B. u3 A( E* E
                       
$ c% m* M& _2 s4 g+ ?  _                        //显示蛇的位置
5 V/ ~( A! b/ O% i# }) H$ |                        for(temp = 0;temp < GameParamter.node;temp++)# O; y( Z% m5 y  J7 b
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);
, u2 i4 i* t2 x$ C2 L1 S1 P; n                       
  V; R% |0 ]9 @0 ], B& Z' k) l                        //吃到食物,创建新的食物
2 K5 x/ N% \* U6 k( K7 y4 f4 ^5 q4 E                        if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])
) Q/ F2 J, w( \4 j                                Oled_food();
, _) G  e; T. N+ U- T% F, `                        + u0 K; h  n9 i; A; m! q; a
                        if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)
4 D: |! b; A1 y$ y            {
* y, {; R9 ?0 b& D                                OLEDClear();
9 M' E- C8 [  ~- K6 g; T4 T  ?                                OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME");2 C  g% @: s0 d6 ?
                                OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER");
3 R; \$ S9 K5 `$ K                                State_Flag = Game_Over;
* q) r3 F' }2 ]4 S% b                                HardwareParamter.OLEDFlash_flag = 1;
" G# c3 q7 L* D4 G* U                        }
5 U% E) B6 F3 ^  D; R/ H                break;* q  p& v5 ]6 f* h9 O. ?. v7 C
                case Game_Over:
/ H  _& b8 _/ |, Z
9 `# P( n: F( q; d: Z                break;0 P2 _4 {- u- x! s9 F+ ~
                default:/ G0 v# p7 A1 U1 ~6 b4 A" f/ {' i
                        OLEDClear();* L- C0 x8 ?/ f2 k; \8 x
                        State_Flag = Snake_Move;
: E5 i& B% n' f5 Z" k. g) B# Z/ y                  HardwareParamter.OLEDFlash_flag = 0;9 n1 }; V5 F& \6 i
                break;' p# }4 m  o9 g& Z
        }( R2 a. x7 B' `& W) K( {, D7 p9 e
5 ^7 t" i* [3 ?: I* d

9 K" k7 U; f1 Q) B7 p' n1 e0 }0 i$ J外带一个按键处理,解决方向和暂停重置问题。
' @6 C# r0 i, Z( h+ t  Z4 u* B' G1 d! v; J
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 手机版