基于M5StickC Plus的体感游戏手柄
使用M5StickC Plus设计的一款重力感应的贪吃蛇游戏
标签
嵌入式系统
ESP32
游戏
M5Stick
2022暑假在家练
冷月烟
更新2022-09-02
622

1.项目描述

项目目的

   使用姿态传感器完成体感游戏手柄,并设计完成一个游戏,例如俄罗斯方块、左右晃动手柄使方块左右移动。

   计划使用M5StickC Plus设计一款重力感应的贪吃蛇游戏。

设计思路

   利用重力感应去作为方向按键,控制蛇的上下左右移动,共计支持四个方向。

   板卡正面按键作为开始按钮,当蛇死亡时,按此按键可以重新开始游戏。

   游戏显示底为纯绿色,屏幕四周绘制红色圆形围栏,碰上后游戏结束。蛇本身采用纯黑色,食物采用蓝色,蛇、食物、围栏采用圆形图形。

   

2.硬件介绍

 

  • 基于 ESP32开发,支持WiFi
  • 内置3轴加速计与3轴陀螺仪
  • 内置Red LED
  • 集成红外发射管
  • 内置RTC
  • 集成麦克风
  • 用户按键, LCD(1.14 寸), 电源/复位按键
  • 120 mAh 锂电池
  • 拓展接口
  • 集成无源蜂鸣器
  • 可穿戴 & 可固定
  • 兼容多平台开发:
主控资源 参数
ESP32 240MHz dual core, 600 DMIPS, 520KB SRAM, Wi-Fi, dual mode Bluetooth
Flash闪存 4MB Flash
输入电压 5V @ 500mA
接口 TypeC x 1, GROVE(I2C+I/0+UART) x 1
LCD屏幕 1.14 inch, 135*240 Colorful TFT LCD, ST7789v2
麦克风 SPM1423
按键 自定义按键 x 2
LED 红色 LED x 1
RTC BM8563
PMU AXP192
蜂鸣器 板载蜂鸣器
IR Infrared transmission
MEMS MPU6886
天线 2.4G 3D天线
外接引脚 G0, G25/G26, G36, G32, G33
电池 120 mAh @ 3.7V, inside vb
工作温度 32°F to 104°F ( 0°C to 40°C )
净重 16g
毛重 21g
产品尺寸 48.2*25.5*13.7mm
包装尺寸 65*25*15mm
外壳材质 Plastic ( PC )

 

3.开发环境

M5StickC PLUS支持多种开发环境,官方建议使用UIFlow, MicroPython, Arduino三种环境中的一种。本次开发选用Arduino作为开发环境:

首先将设备连接至PC,打开设备管理器为设备安装 FTDI驱动 。

访问 Arduino 官网 ,选择对应自己操作系统的安装包进行下载。

打开 Arduino IDE,选择 文件->首选项->设置

复制下方的 M5Stack 板管理网址到 附加开发板管理器: 中

https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json

选择 工具->开发板:->开发板管理器...

在新弹出的对话框中,输入并搜索 M5Stack,点击安装(若出现搜索失败的情况,可以尝试重启Arduino程序)

选择 工具->开发板:->M5Stack Arduino, 根据我们所使用的设备(M5Stick-C-Plus)选择对应的开发板配置。相关库
不同的硬件设备,有着不同的案例程序库,根据所使用的设备选择下载。打开 Arduino IDE, 然后选择 项目->加载库->库管理...
搜索 M5StickCPlus 并安装,下载时请根据弹窗提示,安装相关依赖库。

 

4.软件运行流程图

Fs18Y4Urp84zW4FYEj_VvCbOyov-

 

5.主要代码片段及说明

初始化部分

清屏,绘制整体的绿底,设置蛇的初始出现位置,设置蛇的初始长度,设置第一颗食物刷新的位置,绘制四边围栏。

snake_body[0][0]=3;
snake_body[0][1]=1;
snake_body[1][0]=2;
snake_body[1][1]=1;
snake_body[2][0]=1;
snake_body[2][1]=1;
snake_lenght = 3;
food_coordinate[0] = 7;
food_coordinate[1] = 7;
//清屏
M5.Lcd.fillScreen(TFT_GREEN);
//画围栏
for(uint8_t i=0; i<=SNAKE_X_MAX; i++) {
  M5.Lcd.fillCircle(TFT_X_MIN + DOT_RADIUS + i*DOT_RADIUS*2, TFT_Y_MIN + DOT_RADIUS, DOT_RADIUS, TFT_RED);
  M5.Lcd.fillCircle(TFT_X_MIN + DOT_RADIUS + i*DOT_RADIUS*2, TFT_Y_MIN + DOT_RADIUS + SNAKE_Y_MAX*DOT_RADIUS*2, DOT_RADIUS, TFT_RED);
}
for(uint8_t i=0; i<=SNAKE_Y_MAX; i++) {
  M5.Lcd.fillCircle(TFT_X_MIN + DOT_RADIUS, TFT_Y_MIN + DOT_RADIUS + i*DOT_RADIUS*2, DOT_RADIUS, TFT_RED);
  M5.Lcd.fillCircle(TFT_X_MIN + DOT_RADIUS + SNAKE_X_MAX*DOT_RADIUS*2, TFT_Y_MIN + DOT_RADIUS + i*DOT_RADIUS*2, DOT_RADIUS, TFT_RED);
}

蛇移动部分

读取加速度值,通过比较各个方向的加速度值大小与正负,判断蛇应该前进的方向。

//移动蛇
M5.IMU.getAccelData(&accX, &accY, &accZ);
for(uint8_t i=snake_lenght; i>0; i--)
{
  snake_body[i][0] = snake_body[i-1][0];
  snake_body[i][1] = snake_body[i-1][1];
}
if(accX < 0 && abs(accX) >= abs(accY)) {
  snake_body[0][1]--;
}
else if(accX > 0 && abs(accX) >= abs(accY)) {
  snake_body[0][1]++;
}
else if(accY < 0 && abs(accY) >= abs(accX)) {
  snake_body[0][0]--;
}
else// if(accY > 0 && abs(accY) >= abs(accX)) 
{
  snake_body[0][0]++;
}

吃食物与碰撞检测部分

判断蛇头与墙、食物、蛇身的重叠,然后进行不同的处理,碰到食物时:增加蛇长,重新产生一个随机位置的食物;碰到墙或自身时:游戏结束。

//吃食物
if(snake_body[0][0] == food_coordinate[0] && snake_body[0][1] == food_coordinate[1]) {
  snake_lenght ++;
  //重新生成食物
  food_coordinate[0] = random(1, SNAKE_X_MAX);
  food_coordinate[1] = random(1, SNAKE_Y_MAX);
}

//碰墙
if(snake_body[0][0] == 0 || snake_body[0][0] == SNAKE_X_MAX || snake_body[0][1] == 0 || snake_body[0][1] == SNAKE_Y_MAX) {
  goto GAME_OVER;
}
//碰自己
for(uint8_t i=1; i<snake_lenght; i++) {
  if(snake_body[0][0] == snake_body[i][0] && snake_body[0][1] == snake_body[i][1]) {
    goto GAME_OVER;
  }
}

显示蛇、食物部分

刷新新的蛇头,食物,清除不需要显示的蛇部分(使用覆盖刷新的办法)。

//刷新蛇
M5.Lcd.fillCircle(TFT_X_MIN + DOT_RADIUS + snake_body[0][0]*DOT_RADIUS*2, TFT_Y_MIN + DOT_RADIUS + snake_body[0][1]*DOT_RADIUS*2, DOT_RADIUS, TFT_BLACK);
if(snake_body[snake_lenght][0]!=0 && snake_body[snake_lenght][1]!=0){
  M5.Lcd.fillCircle(TFT_X_MIN + DOT_RADIUS + snake_body[snake_lenght][0]*DOT_RADIUS*2, TFT_Y_MIN + DOT_RADIUS  + snake_body[snake_lenght][1]*DOT_RADIUS*2, DOT_RADIUS, TFT_GREEN);
}
//刷新食物
M5.Lcd.fillCircle(TFT_X_MIN + DOT_RADIUS  + food_coordinate[0]*DOT_RADIUS*2, TFT_Y_MIN + DOT_RADIUS  + food_coordinate[1]*DOT_RADIUS*2, DOT_RADIUS, TFT_BLUE);

delay(500);

游戏结束

显示 GAME OVER! ,等待按键按下,重新开始。

GAME_OVER:
M5.Lcd.setCursor(20, 40);
M5.Lcd.print("GAME OVER!");
while (1) {
if (digitalRead(M5_BUTTON_HOME) == LOW) {
  while (digitalRead(M5_BUTTON_HOME) == LOW)
    ;
    break;
  }
}

6.功能演示

游戏进行中

Fq7Z6IYZvOyzfJiR7YYhmimV9v2i

游戏结束

FkOaDo-rgyBVqljVeJXgX_udozTF

碰墙结束

FlLnk8nRZao-J6TRhOARaakaZwTH

 

7.遇到的主要难题及解决方法

屏幕显示刷新感强,闪烁严重

官方提供的屏幕显示代码是直接刷新到屏幕,并不是先刷新到缓冲区,然后再统一刷新到屏幕上的方式。这种直接刷新的方式,会有肉眼可见的刷新感,能看到整个的刷新过程,显示观感很差。解决的话,可以采用类似脏矩形的机制去刷新,只去刷新那些有改变的地方。例如当蛇移动过去的时候,在需要消失的地方刷新一个与底颜色相同的圆。对于这个设计,我将其简化为底加一个个不同颜色不同位置的圆,圆可能出现的位置是固定的,当我需要让某一个圆消失的时候,就可以在此圆的位置上刷新一个与底相同的圆覆盖刷新,这种方式既优化了显示,减少了刷新感,又加快了计算速度,减少了计算压力,让整体看起来更加流畅自然。

倾斜移动有问题,有些方向识别不好

代码编写问题,判断方向时,一开始的代码是直接进行大小比较,导致部分方向判断不准确,修改代码,改为判断加速度值正负,然后使用abs函数处理加速度之后比较大小,进而判断方向。

8.未来的计划或建议

当前只是初步实现了贪吃蛇的功能,下一步计划增加计分跟关卡功能,每一关设置一个目标,当到达目标后就会通过当前关卡,到达下一关,每一关都设计不同的地图,在每一关里面随着蛇不断变长,蛇移动速度也在不断增加。

当前存在一个小的逻辑问题,当向蛇前进方向相反的方向倾斜设备,就会让蛇原地掉头,导致碰到自己的身体而游戏结束,暂时没有想到太好的解决办法,以后想到之后可以对其进行改进。

附件下载
代码.zip
团队介绍
团队成员
冷月烟
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号