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

STM32F446+OLED12864跑贪吃蛇游戏

[复制链接]
不吃肉的羊 发布时间:2018-2-7 14:16
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。
7 y) z7 u1 u' d0 N$ [
$ H. D* O  T# B4 J3 a突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.
; C! y8 O" d( [4 h/ O1 F4 D  X7 X- s4 h4 C1 F1 V
于是花了点时间,做了个贪吃蛇的游戏,写了段程序。
$ B& G7 D! C) g. L( t0 X
4 U5 d9 t& w( Z0 W4 m7 D0 y  Y8 Z程序硬件支持:
% ^! ?; d! t" l' C' ^STM32F446R+OLED12864+按键& p: K! e8 o; z) z

* ?: ?& d' r3 l9 P$ j软件结构:
+ _8 m6 O8 h' c1.DMA跑SPI刷屏
8 n! i7 Y/ a5 |% [2.贪吃蛇游戏算法$ m+ n5 Z* u" d+ v5 g  y! Y$ u( _
3.重新定义OLED显示方式
7 J2 t. Z7 _( g% g& i8 u
  ], z5 w: m6 k4 {4 R% O3 u底层驱动就不说,DMA配置,跑SPI数据。
' K% h9 K0 j6 q4 M/ X5 J  D# ]
. N  t& V) h. |2 Y( K我先做了个二位数组,存放64*128个点,初始值为0;
* y2 r- E) q: }% T+ F  A# Duint8_t OLED_Display_Data[8][128]( |0 a7 C$ \) h! p" B5 D
! t6 o( Y, u6 A4 m
之后开个定时器,每隔一段时间跑一次屏幕
: n) z2 `6 V' v+ s6 `9 [! F6 }2 d: o. M' k                if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)
: V3 R, Q/ U1 u+ l+ w1 [                {
* I5 B- I9 C  o: N) [; o7 w% N) f( \, A                        HardwareParamter.TimingOLED = 0;
- J" d0 {' g5 X) S9 O                        Display_Process(OLED_Display_Data);        : P6 d* F3 u4 o$ V( {) A  l5 o
                }
, ?5 X1 X& E9 i2 g# q1 _: J. i* a- _5 \% V& ?( r- E

. X+ Q$ G( p% w6 m" K基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。
  q2 v$ v) y, x  t1 j8 \9 D* ~* }* r* [0 I
贪吃蛇算法做的简单:: l* B( y6 P( J
首先是一个结构体,存放相关参数* j# I' ~' F# M. G

( A  x, `1 B4 X, s* ltypedef struct {  F+ O* c& h0 i" J2 R* y0 }
        uint8_t food_x;      //食物横坐标
$ w8 A1 X" w4 \6 N" n' a        uint8_t food_y;      //食物纵坐标
" l8 i3 C. \3 z( h- K1 g) y' _2 [5 V/ B/ p4 r+ f7 p6 E
        uint8_t gameEsc;     //游戏开始暂停$ w0 a, O# p' f  b. ?; i# H$ Q
        uint8_t gameSpeed;   //游戏速度
( ]7 S/ @3 m4 D9 p        uint8_t gameLevel;   //游戏难度) ?. _$ G' O/ C
        1 X1 V' x/ b/ J8 a- ?. _+ ^
        uint8_t x[39];
; B) v$ A9 [  |2 F7 w' }        uint8_t y[39];+ I8 C4 ~: r  ]9 [6 s
        uint8_t node;        //蛇的节数
! h4 a* ]% {5 |/ ^5 U' d# W' T3 ~        uint8_t direction;   //蛇头方向5 ]% u2 }: W0 F# D" n" a, u
}GameParamter_t;
4 z4 q$ f6 C9 f9 G: F: u9 n6 @
% d$ {: v; o" Z1 b5 E$ W
- f1 K; j; Z4 g两个核心函数:移动和创建食物
7 o: U* a' `8 }+ ^9 x+ L9 Z食物依赖于随机数生成函数
, X2 b3 A5 s* s* `4 S" `4 u/ R//    需要出现新食物  a" @2 S- f$ }3 r8 v. V6 B2 G/ [2 h
uint8_t createNewFood(void)- m5 N9 L3 q2 g5 p- M. S$ c
{% D0 h6 H- N" F0 n) K
        uint8_t i;
' r3 u  _, ?3 m. I        uint8_t size = GameParamter.node;
! d  a$ B* Q* Y& [        uint8_t flag = TRUE;                         //    标记创建的新事物与贪吃蛇的身体冲突
- C" h! t: N' o8 r/ |        srand(HardwareParamter.TimingVal);
9 D' v2 y6 t! j! Y8 t        GameParamter.food_x = rand() % MAP_H;; k+ }- p$ q, j* f/ y
        GameParamter.food_y = rand() % MAP_L;; K7 r% [0 y0 x& m( ]
       
% h# u# }/ ~+ O/ \+ z% |7 L+ Q6 X3 M1 E        //    食物的坐标必须为3的倍数才会在显示屏先被显示+ l5 b9 z- ?# y# ?# @; s# ^
        for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) {      ! F. a/ {+ H; y9 T, ~& m  U6 ]( |
                        ;* K3 }! q" U( V8 I9 |5 V
        }4 i4 F* s( f" R' u! C
       
! s6 N8 P2 Y8 E' }/ L& r        for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) {      
5 w" D5 U- k* l" I( @3 r4 k+ d                        ;
5 V0 j! X7 l7 b- |3 V0 t5 s7 v5 B        }1 o. e3 N  n& k% G% c. F
       
# F/ g& }2 B/ O, j( J7 o        for (i = 0; i < size; ++i) {+ x* f2 w8 Z+ p4 h$ Y! I" r/ F
                        if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {4 W0 d; H& [$ _: K1 h
                                        flag = FALSE;# y1 w5 K5 L; p0 B
                                        break;2 p$ ]2 y1 y$ q1 l
                        }0 O) v+ P: H- H, }: s. k  U
        }    ; U) L, z7 z) V! N
0 r, P; N7 r: m- U9 z) M& G6 j
        return flag;+ S6 t1 `6 J+ w! m- k* Z* h# T
}6 ~2 U: G9 h. _6 r# l

8 r4 O5 G. Y, J' d( P( M7 @. q. S& b& t: r4 T' U
移动就是简单的数据加减
% w2 q  ?1 R& {( e* A//    贪吃蛇移动
+ Z, z2 ]) F( H: t6 [  |  r/ A: _( ?void move(void)
$ z- ]; w6 n% h* @+ c9 @{5 r6 U, A7 k2 ~1 q
        uint8_t i;8 k. X2 m# G) m  p

$ M, _! b: {. N0 l: V0 a6 Q- Q        if(GameParamter.gameEsc)
2 p' h1 Y& @' S3 Y; m$ A# B        {
( ]. F( u' K7 `/ D                //    将蛇从最后个节点向前一个节点移动
, l3 ]4 M: t! Q: h* V5 f& u, T7 _% S                for(i = GameParamter.node - 1; i > 0; i--) {                    ( M& \( W/ a( D2 g2 s  F
                                GameParamter.x[i] = GameParamter.x[i - 1];; N% V; B7 K9 [' J
                                GameParamter.y[i] = GameParamter.y[i - 1];
' ?3 L! F# e; _% H                }
& Z! W% \( _( m& S               
2 K1 W& w# z+ e4 q* y4 Q' M0 e                //    根据此时贪吃蛇的方向,设置蛇头的位置            
. e  Q. y. A) ?. G                switch(GameParamter.direction)   
. Y/ w1 s; {# W9 v. `4 O% N- z                {                  5 [; X1 j/ w( G3 p
                        case RIGHT: ; u+ E2 j0 d& ^/ e+ O9 P1 T
                                        GameParamter.x[0] += DOT_H;  q6 i& Y  g8 p* V3 A! `1 ]
                                        break;
: W& T7 G* r% J                        case LEFT:
6 G, G# Z2 v1 |; Q" L                                        GameParamter.x[0] -= DOT_H;( O) R& o9 o. A5 a  N! d9 Y
                                        break;
  e% k+ g) F, ]6 @- R                        case UP: 1 r/ g) r  q7 G& s1 v) I( I
                                        GameParamter.y[0] -= DOT_L;# S9 A; k9 v, R1 Z
                                        break;
6 P" [0 }* V4 q4 Q# t. }/ C                        case DOWN: 9 \9 w7 V2 O  Z2 v9 q+ v9 N3 k
                                        GameParamter.y[0] += DOT_L;' ~+ [9 c/ L6 e0 c' i: h" T
                                        break;
$ h# w% w/ S  U2 L+ b  c+ E% X8 c                }
+ C  S; ?* T* x4 ?) W        }% r! q, d6 }! E$ P1 f( v0 n) v
}! ~  S, A1 T) r9 {
9 D/ h' k' V7 s: n! r* D
显示部分随时更新
2 G, v0 h4 R7 j+ B, v1 q& Yuint8_t temp;# {. U! N0 Y7 Q, x5 p! Z
       
  A& u9 A' ~- o! K        switch(State_Flag): o4 L, z) j" o9 s8 b
        {
# j0 p; k/ J6 X; R                case Snake_Move:1 \, b, x3 O! F; }$ f, k$ d  Y
                        //清除蛇尾部
' \" f3 ^7 g  |5 N$ L0 _                        for(temp = 0;temp < GameParamter.node;temp++)# {/ M+ _1 N% t) u% j3 f, M
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);3 o2 X, N( P. [1 W
                       
% I8 g) g5 j& P6 D# o* a                        //移动蛇: b8 g: m/ \% O! `
                        move();' ~/ M6 C* m$ X5 G" R2 ]3 l
                       
+ |$ _% E6 C7 O5 i                        //显示蛇的位置' T  u5 _; Y8 s- r) t( H4 X
                        for(temp = 0;temp < GameParamter.node;temp++)
9 r5 n4 g; S9 m" e                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);
1 c& h  P( i: r: \; }                        ) q: {9 G- H. y; l; f0 x: B. i6 g
                        //吃到食物,创建新的食物5 m$ U+ G' B) j1 [: \
                        if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])7 W! z- A4 T+ h( s
                                Oled_food();
" `$ I+ _# Y! Y0 X' l" ]                        ( U. G) l% K$ f) B* _: W. _  U
                        if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)3 R) h0 u- ~  [7 f, A- C7 J/ T$ ^
            {0 a+ v/ D$ E3 Q8 Z4 K) T5 b
                                OLEDClear();/ W1 c7 G3 F9 L; `8 s% a0 ^  e5 ~
                                OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME");
- @$ O3 O/ T$ v: Y                                OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER");
; f) y  ~0 x/ H) ^2 n                                State_Flag = Game_Over;
( r5 i$ [: h3 |$ c                                HardwareParamter.OLEDFlash_flag = 1;) l6 j2 A7 s' c! B% G
                        }
' B- m1 c- i7 ]  C3 U" j. z                break;
- D& R' i: I) u$ L/ G                case Game_Over:9 l9 `+ u9 R; B$ b9 X
: {/ _! j' x2 k8 D4 m
                break;# S6 k1 D, m: M3 S
                default:
5 V4 y8 c9 q9 ]$ H& ~8 ^                        OLEDClear();1 o/ M- T. h8 y/ |# g! i  v4 o2 a; P) i
                        State_Flag = Snake_Move;
. a6 _- I+ u& b                  HardwareParamter.OLEDFlash_flag = 0;, f! D$ z5 c2 N* _$ d( @
                break;* f$ F) D+ B; P" N
        }
. L# I- q$ x4 U, v% \' m. P9 O% N$ |( R) o* W, f9 h
- w1 D+ X, [( G! W; C
外带一个按键处理,解决方向和暂停重置问题。
* K; U7 E9 j9 P3 x
+ p! x  k! z& N& g: s/ s- {, A
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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版