请选择 进入手机版 | 继续访问电脑版

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

STM32F446+OLED12864跑贪吃蛇游戏

[复制链接]
不吃肉的羊 发布时间:2018-2-7 14:16
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。
! U, k) E# T- {
0 {) H- b4 f, \9 A5 [0 D- `突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.
; b" C0 O7 G* v4 C0 e
$ P- }/ I4 h7 x( w- ~' W于是花了点时间,做了个贪吃蛇的游戏,写了段程序。  }- P& i! y( }, B9 F
8 q3 v, n0 q& S1 I, Z0 Z
程序硬件支持:
3 Q) H3 s$ s8 K# R+ iSTM32F446R+OLED12864+按键
6 N' w( d0 j& C% P( {. H" E2 G
4 F8 |+ F& U& V- [0 S软件结构:
& d# ~  p8 h; K- e/ |1.DMA跑SPI刷屏/ o. i$ k0 G& i' H+ G$ ]  P
2.贪吃蛇游戏算法7 b" u, R' f) `
3.重新定义OLED显示方式
& A. F/ |$ r) u
8 I9 c) V. Y4 |; D. l* a+ M. {底层驱动就不说,DMA配置,跑SPI数据。2 u8 B6 w; F. j. l9 G# e5 v
8 R7 Z: N8 _$ |( d* [0 x3 e! N
我先做了个二位数组,存放64*128个点,初始值为0;( P" ?$ D/ x: O0 u# b8 O
uint8_t OLED_Display_Data[8][128]
3 x7 K! l1 }8 P  e- F. N* @- n2 r! i0 b* `
之后开个定时器,每隔一段时间跑一次屏幕' M# [# C7 a1 P  z. k, x) U
                if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)
! r9 f4 S/ s# v+ i: |5 w                {
  s# [* z8 d, v                        HardwareParamter.TimingOLED = 0;
6 o# ]2 [/ D6 t3 E  }% q! f5 [                        Display_Process(OLED_Display_Data);        ( ?( A7 L. B" ^$ R$ m9 t
                }
, S( B  p1 |; I+ |2 |/ ]
7 _$ C2 z3 A8 F4 ]
4 @# F: H+ S! |0 R5 P: O) c基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。
% w2 O& h4 r9 Z; c7 p4 g& |4 l' V! |9 s2 v8 L
贪吃蛇算法做的简单:! G7 Z/ E# q2 p) T
首先是一个结构体,存放相关参数
% @, m8 n" f, d, S
7 W4 F7 M+ {, q) m1 @) atypedef struct {. Q* W* m" @% ]1 [$ }6 |
        uint8_t food_x;      //食物横坐标7 L: C. \2 A, G/ q1 [3 N2 W3 G' ]$ D* }
        uint8_t food_y;      //食物纵坐标
6 z) q" p; V* I# _) I* S4 Z; H2 l2 z5 U8 m" n3 x1 M, m- C! L
        uint8_t gameEsc;     //游戏开始暂停$ I8 J3 Q# a" f3 G+ Q
        uint8_t gameSpeed;   //游戏速度) d6 h) k9 @7 G$ t: a; q
        uint8_t gameLevel;   //游戏难度
. H" @/ q0 k# ?: i# A. k$ @8 D8 b       
* c! `- }$ ~5 a- L        uint8_t x[39];8 b$ x' P$ q. x2 \* k0 K
        uint8_t y[39];* g" M8 N! j& I; ]! I9 v
        uint8_t node;        //蛇的节数, }' W- d5 x* r( j' j8 I! C" k" i
        uint8_t direction;   //蛇头方向
9 T- c9 d0 k0 K; d) k}GameParamter_t;
! ]/ M8 D# s# G% p; ]6 e1 P+ \  a& ^( A4 H  B8 _8 X* N

1 o: B6 [3 Z# T/ E两个核心函数:移动和创建食物" R: t1 j& ~8 {+ H/ H
食物依赖于随机数生成函数
3 o( W4 A* c0 U. k" w//    需要出现新食物
5 d& f$ u$ V0 [+ \) B) C! i1 Vuint8_t createNewFood(void)0 }* S; B5 X1 x$ n
{2 U. H4 m# i! U; R
        uint8_t i;0 r1 I0 m) @- |# \5 ]0 G) G
        uint8_t size = GameParamter.node;2 i4 H) N: b( Z
        uint8_t flag = TRUE;                         //    标记创建的新事物与贪吃蛇的身体冲突
2 l. N, T# B1 F/ _$ n  {        srand(HardwareParamter.TimingVal);
# X  U4 f: v8 S& N; T  A5 W        GameParamter.food_x = rand() % MAP_H;: c1 f) v5 C5 J5 v0 i6 [. s
        GameParamter.food_y = rand() % MAP_L;4 b4 S/ `! _$ ?3 n; D
       
7 K$ I' U' _* a3 p) w        //    食物的坐标必须为3的倍数才会在显示屏先被显示
4 @7 `% a+ c& `5 [        for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) {      / G5 f0 z7 N, c9 m
                        ;
- _" X' E! Q7 b$ O        }
( N5 U' M0 p. W# e" v+ \       
# v3 R- s8 |. \3 c" F( X9 g3 r/ I        for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) {       % L% @/ L, x: Y7 ]- d* Q
                        ;6 o8 w0 Q& _! c1 y
        }( p: g# O7 k7 N9 L
       
! n4 o$ Y0 q  O+ [6 B8 v  A        for (i = 0; i < size; ++i) {9 \8 _, a; O6 [# }" c8 ~. V# k1 u- j
                        if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {
% i/ q( n1 b; F/ z2 p                                        flag = FALSE;
9 Z/ U4 s" L4 |0 I                                        break;
2 l* J7 W: T  i- F8 ]                        }0 y* \9 H- L4 g) d( e. U7 ?/ Q
        }   
0 w7 f: [  v/ j
! u* \4 ?% l" m/ t        return flag;
1 k5 L) {" s! j5 r; X3 F}
) S. V( A* Z6 [5 l8 P2 {! k! c9 O8 b* H3 y/ g& y% a$ C

* O; x( L- _4 s9 K/ g. g移动就是简单的数据加减
3 q+ a( l( {3 |" [0 w//    贪吃蛇移动
/ |3 S) L8 _3 g# E. uvoid move(void)
2 R1 G, b! O, ]$ X" k( ~{: M+ Q) h- f: \( O+ ]
        uint8_t i;0 x; _& T; Z7 R9 ^0 y5 Y
  k9 g" U- k4 l% o$ l" R% |
        if(GameParamter.gameEsc)# n" f9 k; }; W/ J' [5 f
        {
# d* O' _1 Z- W6 Y  L. D, h3 m                //    将蛇从最后个节点向前一个节点移动. A8 y; y- G, S# |/ @4 G5 q
                for(i = GameParamter.node - 1; i > 0; i--) {                    
- q  s1 I9 M0 b                                GameParamter.x[i] = GameParamter.x[i - 1];
2 u; }. i" K' H$ m- v! y! C5 {  N; b                                GameParamter.y[i] = GameParamter.y[i - 1];& r& u1 `5 b/ H3 d( P
                }
7 U" h; }& K8 t7 {/ F& z' A  z                7 p6 r: C1 ^2 _" p# C6 Q0 X. O: ?: O
                //    根据此时贪吃蛇的方向,设置蛇头的位置            , R: h( U8 y  h4 U2 {( X
                switch(GameParamter.direction)   
1 r0 s: u! }8 T9 {$ D                {                  2 U7 H! X  \3 N! t/ [5 z! T
                        case RIGHT: $ X5 f* E- [4 r8 J$ ]
                                        GameParamter.x[0] += DOT_H;
0 F* b, t! j' r: Y. Y                                        break;  [5 n) J5 Z8 b6 k
                        case LEFT:
6 ]) K" G, h7 j                                        GameParamter.x[0] -= DOT_H;9 p4 `; ^( f! k$ z
                                        break;
  _3 R: E7 V! u/ y                        case UP: 9 y5 l7 a, C! J9 b5 _$ ?; r0 i
                                        GameParamter.y[0] -= DOT_L;
4 H# g4 A! Q4 }5 d( M+ ?                                        break;0 p' U" z8 _+ i* B. `3 n; j7 f
                        case DOWN: 1 U( O! u5 g) o1 Y" d1 j
                                        GameParamter.y[0] += DOT_L;
2 P$ R8 y" K, ?: F( D' p                                        break;% b& B  Q7 ?. P- \
                }5 k4 O. u: G( k, N& M- c
        }
6 P: q9 e3 I: K: X8 ?. v$ F}
8 P5 a0 P7 R/ C4 I. ]" V; i! H$ `3 S8 L. n6 q
显示部分随时更新
. b2 _( B' ~. o6 p. F& [, muint8_t temp;
6 N- X3 B! W5 W' }3 U. ?' S       
! F" o( K6 \3 V        switch(State_Flag)% h+ ]8 X/ T/ K' W8 f
        {
% t2 y, b, m4 A0 V" V$ ~9 n# @                case Snake_Move:
$ D  B' ]) P5 [  J+ O5 n                        //清除蛇尾部
3 }$ Z0 }& d; t/ R5 Q+ A% F5 d/ V                        for(temp = 0;temp < GameParamter.node;temp++)$ D2 L# [' o* ]; S) L5 a  `6 d; R
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);6 @& |! U+ ]! N0 p3 z
                        5 B& t) @& P( H+ @* J* k! Z
                        //移动蛇
0 N! [1 t5 i! }" N" F; U) y3 q                        move();: ?% n. e/ x1 h- {( F
                       
! ]4 }. Y6 z& [2 n8 `                        //显示蛇的位置0 j# H' `+ ~$ Q+ a% [
                        for(temp = 0;temp < GameParamter.node;temp++)) E" j1 G0 y3 l, Q
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);4 U2 ^4 V6 @! s4 K$ R
                        * W6 a* _5 ~7 W% O  h
                        //吃到食物,创建新的食物9 V" [* b- U6 R
                        if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])+ V% j% Z% w# J, x# o! d
                                Oled_food();; x+ I% |+ z! d: R. A4 t  [
                       
! m9 _6 U$ _3 g4 R! c1 ?' m1 O                        if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)+ Q0 m) B4 a( T
            {4 y1 S9 p. m2 K6 ?
                                OLEDClear();
# Q* L# ^' b3 o* p7 l                                OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME");
/ k8 _8 W2 B. R  ^5 F5 p) T' s: A                                OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER");/ |* u* G, j  @& S! e
                                State_Flag = Game_Over;# H1 S" @* A7 [
                                HardwareParamter.OLEDFlash_flag = 1;
& w7 p  N2 q6 b/ u                        }3 \: `7 T# f) p" _
                break;
; U# m, i# b3 F" N, q) h. b; O                case Game_Over:
5 M: Q+ f( q/ y, l1 g4 Z1 @0 ?8 v+ h% }0 q; U8 M% A/ H' j- N
                break;: T: R( u3 D1 M
                default:0 t' g: u: Z  K" L0 N
                        OLEDClear();
* r3 ?2 ^( |+ o0 F                        State_Flag = Snake_Move;+ A# z- R7 T5 t8 X, j/ b% [
                  HardwareParamter.OLEDFlash_flag = 0;
: o% s0 E, \8 b3 @, H  B6 j                break;! M9 p4 t1 Y0 `% s+ Q' @( m
        }/ @* K5 u7 v+ Q2 r& P, V

1 V& t9 ?# K9 I
4 R' G- e8 a: {3 R. d: {4 v7 X3 e外带一个按键处理,解决方向和暂停重置问题。& |: g% G. B0 Z
) [5 o. k  B9 Q+ ~
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 手机版