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

STM32F446+OLED12864跑贪吃蛇游戏

[复制链接]
不吃肉的羊 发布时间:2018-2-7 14:16
闲暇之余发现看了下OLED12864的数据手册,发现他的显示在Y轴上只有页写,也就是一次写8个点。& f, e* F0 g3 i/ F% b
' A' K2 ^$ [6 N. Q8 Z! K8 l
突然想到,如果自己程序需要精准定位到某个点,那这不是会很艹蛋么?去网上搜索,基本上都是页写的代码,也就是在Y轴上的定位只能是0~7.
" }3 C9 z/ d4 @% @+ F, X/ b) V" {) K+ F( P. d% [) H6 E+ q# F, J
于是花了点时间,做了个贪吃蛇的游戏,写了段程序。, X# B  J. Z9 J4 B% D
' D  b; Z& G, |1 V- ?
程序硬件支持:5 \! }( }: Y! S2 Q
STM32F446R+OLED12864+按键" J8 D. F# d) o! |2 X

6 z6 G) `5 o/ x8 D1 ?1 m软件结构:
( ~& s( X. g  X* |1.DMA跑SPI刷屏
- _' M" v3 ~5 ?0 N+ |2.贪吃蛇游戏算法
9 o0 R, c- A, h& }# ^8 d3.重新定义OLED显示方式
. b5 i+ C, ?4 T$ e$ E
, L4 U; p& Y" i7 Z5 L底层驱动就不说,DMA配置,跑SPI数据。
1 t$ U3 h7 x+ A: @+ t' e+ |! o% _! J% N0 E' N
我先做了个二位数组,存放64*128个点,初始值为0;# {7 z3 V8 V* M
uint8_t OLED_Display_Data[8][128]
! i' g- u" H! R9 A: _, q/ q$ W- e
9 ?: W2 `' Q# r+ F: R之后开个定时器,每隔一段时间跑一次屏幕8 B' E; h" \0 K
                if(HardwareParamter.TimingOLED>=Refresh_Time && !HardwareParamter.OLEDFlash_flag)3 D& [: [) ^+ n1 K4 ~
                {4 }0 _5 `' R$ u7 A
                        HardwareParamter.TimingOLED = 0;8 R) @. X3 f2 V+ P
                        Display_Process(OLED_Display_Data);        1 D; W2 Y8 n# n2 M, v6 g; P5 j# M
                }
$ y3 ?8 q$ d- e/ L- ~# Z8 \" z
# b. U, z; D* i+ z4 ?$ ?, o- k; W9 u3 T8 r7 v9 i
基本想法就是,不停的跑这个数组,数组就是屏幕的每个点。+ A0 [' b( d- E! N- }, u
7 a, Y* O, A4 u% T$ t2 B( t9 e
贪吃蛇算法做的简单:
/ a+ q8 a9 H+ S8 X: C* f首先是一个结构体,存放相关参数
( q2 [8 c  k5 r6 c7 }$ D( Z
& h- W& E5 |# n) I0 t1 ~/ vtypedef struct {- ^2 Q3 C( o% F0 f% g1 j. l* z
        uint8_t food_x;      //食物横坐标- H! O! r; V6 n* l, h5 F  j  s' U
        uint8_t food_y;      //食物纵坐标
& m/ m& Z( o( Q' {" B/ P
+ G. U% ^$ T# t; r        uint8_t gameEsc;     //游戏开始暂停
4 ?6 Z& `4 [: `7 J9 ~: w, D% M& m        uint8_t gameSpeed;   //游戏速度
+ f. V, l5 k. G8 W        uint8_t gameLevel;   //游戏难度! C8 p% w% u4 d/ c1 b: P8 s: x
        & X' J& d* q* e$ b# e' Y& L- J
        uint8_t x[39];
2 E8 x/ J3 U' a        uint8_t y[39];
  H- d" K% M- l- u8 }        uint8_t node;        //蛇的节数8 o7 ~8 \9 y$ ~/ A1 d( T- N% B
        uint8_t direction;   //蛇头方向: r9 ]& y  _3 @: M( N' v
}GameParamter_t;& g4 _% t0 |3 K) Y; F0 U9 |8 Q
2 ~, R; l0 k) N4 e( ^* q

5 S2 O  F& p% X. r  E两个核心函数:移动和创建食物
& C; D! `0 D& `7 D+ a食物依赖于随机数生成函数9 \* a) H+ e2 O6 \& x- e
//    需要出现新食物
7 R3 ]. y, y  V# T) uuint8_t createNewFood(void)
, {# S, |& N) `) E: U{
7 Y& h* Z9 b. s! C. x        uint8_t i;( w  ~& F+ ]9 X. ?# a- o' t
        uint8_t size = GameParamter.node;7 r" ^0 o! K3 h7 K% {- j
        uint8_t flag = TRUE;                         //    标记创建的新事物与贪吃蛇的身体冲突9 j" G+ h* z5 E# k1 n
        srand(HardwareParamter.TimingVal);( {  J2 V; r1 k1 l: Q$ N5 ]) ]
        GameParamter.food_x = rand() % MAP_H;
0 C* _$ R' T+ ^        GameParamter.food_y = rand() % MAP_L;
, ~8 F/ X4 C1 u7 @2 t* e4 ~4 b- u       
1 F7 a9 f& [/ i8 d+ V+ Q* g- Y        //    食物的坐标必须为3的倍数才会在显示屏先被显示
; Y, ?) o; R$ S7 A        for (; GameParamter.food_x % DOT_H != 0; ++GameParamter.food_x) {      
- ^; S9 S1 G; O" U0 i                        ;
- D' y1 q. v) `2 b        }
! a* [- A. k# ]       
6 T% T) {& K$ \4 \) D3 v        for (; GameParamter.food_y % DOT_L != 0; ++GameParamter.food_y) {      
( m1 ]& @; f: @/ {8 m9 Q& d$ a. s% B                        ;  A5 U* a. A7 a) M& m. F
        }
6 t% K# M1 V/ w3 S% U6 M3 I. K       
" Y2 B+ D1 T0 W$ D" n* a- r2 l- s        for (i = 0; i < size; ++i) {
: A% q# q& |/ U: `, I6 ?- e* z                        if (GameParamter.food_x == GameParamter.x[i] && GameParamter.food_y == GameParamter.y[i]) {
& W1 X: e5 c2 \* m                                        flag = FALSE;3 e; `6 \/ w) F9 I3 w
                                        break;; `; G! |+ w2 m: k2 ]7 B0 ~
                        }
- X0 E% \; U/ ]& U6 Z; P8 ~        }   
5 ^% f7 b# O7 H0 ?5 J
6 c. Y" L9 N% N0 Q+ @2 H        return flag;
: P! f  [( j3 `5 B  f  p5 N}' T/ |  A( P8 w4 L5 J8 K1 M! R
" r' Y" K% s, R# F% A! i/ O
- ?" T8 b, |4 J. M0 U( V
移动就是简单的数据加减1 o1 p  N1 L% P% a$ M' x% G
//    贪吃蛇移动5 x! I# `2 U; U2 ~% s; ^: a
void move(void)
4 E; ^5 D  s1 l! C. q; C{9 o$ b# ?; X! w8 o+ p! v
        uint8_t i;- D( O9 \; V( b5 t

: E. N/ G$ O3 U+ X) M# [  U) F        if(GameParamter.gameEsc)# g2 `& H- Y: n" H
        {9 u8 g6 p* a+ J1 A1 h
                //    将蛇从最后个节点向前一个节点移动
+ w# N& f; A) b9 R- B5 p) C" p                for(i = GameParamter.node - 1; i > 0; i--) {                    
$ m" K+ t# U% a: I; r* \. [0 i                                GameParamter.x[i] = GameParamter.x[i - 1];- {9 e, p) e2 ^$ a, C) E. U1 a- D
                                GameParamter.y[i] = GameParamter.y[i - 1];, i7 T% Z! v1 B! Y4 V4 U
                }/ }* ~5 `& f+ l. A! K& W( @
                % N' m4 C$ t1 i) c
                //    根据此时贪吃蛇的方向,设置蛇头的位置            6 k( W5 S4 a0 C- D
                switch(GameParamter.direction)   
# y6 ?. [3 {1 O+ Y. A; g7 N                {                  
4 j) j3 P9 m0 {* V                        case RIGHT:
* M8 K) B* ^" J; [                                        GameParamter.x[0] += DOT_H;# y  e* E& c% d6 q$ K9 \" _5 v
                                        break;
8 U! R' g! D0 c. U  Y                        case LEFT:
5 K; N; u( ^- E2 f                                        GameParamter.x[0] -= DOT_H;# K; R! J/ v/ d
                                        break;
7 D8 r1 O) D% \! b                        case UP: ! f$ N- c2 Y* ?0 O1 r
                                        GameParamter.y[0] -= DOT_L;8 Y+ h+ Z5 U* t( P- \7 u
                                        break;/ k$ I. P- M& v/ I, ]3 o' V
                        case DOWN:
4 p2 y+ O+ ^8 x- T+ T5 _2 S& j. T: f                                        GameParamter.y[0] += DOT_L;$ Q9 @9 A; z/ t8 @1 F0 s
                                        break;
# W6 `" C6 R- f5 k7 ^% j                }
; a; J) M$ |. k4 _7 }' X& f        }
% P3 I& p$ D7 I}4 R: n- C1 i7 n! v! s
. L9 |" r0 e+ ^; Q5 B# Q$ L  m# e' h
显示部分随时更新
5 q# T7 t$ |2 M$ ]5 c! wuint8_t temp;1 V$ G4 I, O- L) u& [# k* Q9 a
       
8 s0 |: J& o# |! N7 ^        switch(State_Flag)
# R  Z% m5 C$ H/ x        {
1 [* S/ p$ a3 x# P$ {' o$ u                case Snake_Move:
" Q5 z# H! g/ N                        //清除蛇尾部
9 l. q0 K7 k; b  l4 G! B                        for(temp = 0;temp < GameParamter.node;temp++)4 ~' |- m) b# H
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],0);  ?. M2 H) W. M
                       
- {/ L, a% V! E                        //移动蛇
# p2 N; B; K) {" _                        move();
7 A5 A4 `, b/ V                        " e- w3 E2 J; U1 U8 ?' w& I& ~  b9 F. n
                        //显示蛇的位置+ p2 i# A2 N2 B+ _& a0 y
                        for(temp = 0;temp < GameParamter.node;temp++)6 t+ n" [( b7 F5 I. c
                                Oled_Dot(GameParamter.x[temp],GameParamter.y[temp],1);$ D, P' h* l- O& P+ t& C; v
                       
3 B1 y8 D  i* W. m2 _6 V# c5 G                        //吃到食物,创建新的食物4 ?! g" l9 ]3 m2 D: g, U& h
                        if(GameParamter.food_x == GameParamter.x[0] && GameParamter.food_y == GameParamter.y[0])
2 l/ v8 i2 N) E/ `9 A" Q. I                                Oled_food();4 E- g6 k- N6 U$ H' \- j
                        : G9 \' }0 Q; i$ p3 l# p5 z
                        if(GameParamter.x[0] >= MAP_H || GameParamter.y[0] >= MAP_L)
4 g+ I% V$ b) E9 n# v- y            {0 a* y* y1 j! G
                                OLEDClear();8 ^8 d8 s9 r5 t8 P  S. T- \% U
                                OLED_ShowString(((128 - ((strlen("GAME")*10)))>>1),2,"GAME");
+ R# _3 J. U$ n( H' g0 [# E3 z                                OLED_ShowString(((128 - ((strlen("OVER")*10)))>>1),5,"OVER");
, L$ k# R* O+ H' ?- f                                State_Flag = Game_Over;! o7 b6 n" h0 u3 e( o9 d
                                HardwareParamter.OLEDFlash_flag = 1;7 ~& M( K& |- Y: n; t0 F
                        }
9 H2 H6 C$ w* J$ R                break;+ E/ b& G# E! X- U
                case Game_Over:& x8 U( ?# W- u) |

+ p% @4 G/ p2 g, [( \$ s; r. i2 r                break;
" a% I" B8 z9 V3 P2 H% V                default:
% a9 u: w/ H0 [( ]6 Q                        OLEDClear();
7 `' k3 |0 U- C$ i6 a                        State_Flag = Snake_Move;7 S. V1 S8 y/ b. X0 J; X3 @! u8 D
                  HardwareParamter.OLEDFlash_flag = 0;. R, n' b2 Y1 u9 V0 @$ w
                break;
6 T& r' u6 d2 q2 s+ j8 @) T, u0 W        }
8 U. B/ ?5 x. j* B+ ?8 l0 m
  k$ ~3 A- l* @, o4 Z& v8 [# n* m9 v$ I! Z' E3 [4 x
外带一个按键处理,解决方向和暂停重置问题。5 f0 Q7 J( h0 a& T, }, ~

& v" \( r! S" u& _( ^
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 手机版