1. 本板卡最终实现了什么功能
任务3:使用姿态传感器完成体感游戏手柄,并设计完成一个游戏,例如俄罗斯方块、左右晃动手柄使方块左右移动
基本功能:
- 向上移动进入游戏
- 显示游戏画面, 包含要击打的方块, 乒乓球和可移动的反射板方块
- 左右移动可以移动反射板并可以反弹乒乓球
- 结束游戏后进入结束画面, 此时向上移动可以重新进入游戏
项目环境:
- Arduino v1.8.16: 一款便捷灵活、方便上手的开源电子原型平台
- VS code v1.60.0: 微软开发的跨平台源代码编辑器
- Vscode-Arduino: VScode开发Arduino的插件
- arduino-esp32 v2.0.0: ESP32平台的Arduino开发库
硬件:
M5StickC PLUS: M5StickC的大屏幕版本,主控采用ESP32-PICO-D4模组,具备蓝牙4.2与WIFI功能,小巧的机身内部集成了丰富的硬件资源,如红外、RTC、麦克风、LED、IMU、按键、蜂鸣器、PMU等,在保留原有M5StickC功能的基础上加入了无源蜂鸣器,同时屏幕尺寸升级到1.14寸、135*240分辨率的TFT屏幕,相较之前的0.96寸屏幕增加18.7%的显示面积,电池容量达到120mAh,接口同样支持HAT与Unit系列产品
2. 各功能对应的主要代码片段及说明
1. 初始化相关参数
对屏幕引脚, 反弹板位置, 球的位置, 以及该屏幕参数进行设置
#define TFT_GREY 0x5AEB
#define lightblue 0x2D18
#define orange 0xFB60
#define purple 0xFB9B
float ys = 1;
int8_t xj, yj, bj;
float x = random(30, 100);
float y = 70;
int ny = y;
int nx = x;
float px = 45;
int pxn = px;
int vrije[2] = {1, -1};
int enx[16] = {8, 33, 58, 83, 108, 8, 33, 58, 83, 108, 22, 47, 72, 97, 47, 72};
int eny[16] = {37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 53, 53, 53, 53, 61, 61};
int enc[16] = {TFT_RED, TFT_RED, TFT_RED, TFT_RED, TFT_RED, TFT_GREEN, TFT_GREEN, TFT_GREEN, TFT_GREEN, TFT_GREEN, orange, orange, orange, orange, lightblue, lightblue};
int score = 0;
int level = 1;
float amount[4] = {0.25, 0.50, 0.75, 1};
float xs = amount[random(4)] * vrije[random(2)];
int fase = 0;
2. 设置图片素材
设置图像素材, 将要使用刀的背景素材转换为数组, 设置到.H文件中
#if defined(__AVR__)
#include <avr/pgmspace.h>
#elif defined(__PIC32MX__)
#define PROGMEM
#elif defined(__arm__)
#define PROGMEM
#endif
const unsigned short bootlogo[32400] PROGMEM={
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0010 (16) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0020 (32) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0030 (48) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0040 (64) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0050 (80) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0022, 0x09AA, // 0x0060 (96) pixels
0x022D, 0x0290, 0x3C15, 0x54B7, 0x54D7, 0x54D7, 0x54F8, 0x54F8, 0x5518, 0x54F8, 0x54F7, 0x54D7, 0x54D7, 0x54D7, 0x54B6, 0x6538, // 0x0070 (112) pixels
0x7E3B, 0x865B, 0x75B9, 0x969D, 0x7DFA, 0x5CD6, 0x54D7, 0x54D7, 0x54F7, 0x54F8, 0x54F8, 0x5CD7, 0x965B, 0xC75E, 0xCF7E, 0xDF7E, // 0x0080 (128) pixels
0xDF9E, 0xDF9F, 0xDFBF, 0xC6DC, 0xB69B, 0xDF7E, 0xEFDF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0090 (144) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00A0 (160) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00B0 (176) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00C0 (192) pixels
....
// Generated by : ImageConverter 565 Online
// Generated from : IN_L8067.JPG
// Time generated : Wed, 07 Oct 20 08:46:39 +0200 (Server timezone: CET)
// Image Size : 135x240 pixels
// Memory usage : 64800 bytes
#if defined(__AVR__)
#include <avr/pgmspace.h>
#elif defined(__PIC32MX__)
#define PROGMEM
#elif defined(__arm__)
#define PROGMEM
#endif
const unsigned short bootlogo[32400] PROGMEM={
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0010 (16) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0020 (32) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0030 (48) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0040 (64) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0050 (80) pixels
0x0841, 0x0841, 0x0841, 0x0841, 0x0841, 0x0841, 0x0841, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0841, 0x0841, // 0x0060 (96) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0070 (112) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0080 (128) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x0090 (144) pixels
0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, // 0x00A0 (160) pixels
...
3 设置姿态重力器控制
通过读取重力传感器的数值来控制左右的方向, 可以先使用官方的IMU测试样例, 检查一下多大的IMU重力传感器返回的数值范围.
[.img 读取重力传感器]
经过测试, 返回的范围再0~0.99. 因而我选择了0.2作为触发值.
float accX = 0.0F;
float accY = 0.0F;
float accZ = 0.0F;
void check()
{
M5.IMU.getAccelData(&accX,&accY,&accZ);
xj = 20;
yj = 10;
bj = 10;
}
对方块的Y轴进行设置来调整方块的位置, 相加之前需要对重力传感器的数值加上一个权重, 来调整重力传感器对位置移动的影响量, 就是调整游戏的手感, 调整重力传感器的灵敏度.
if (px >= 2 && px <= 109)
{
if (accX < -0.2)
px = px - 0.8+accX;
if (accX > -0.2)
px = px + 0.8+accX;
}
4 通过重力传感器实现下一局的操作
每次游戏结束之后, 向上摇动一下, 使重力传感器Z轴的值大于50%, 即可进入下一局游戏
if (accZ < 0.5)
{
if (pom == 0)
{
M5.Lcd.fillScreen(TFT_BLACK);
M5.Lcd.drawLine(0, 17, 0, 240, TFT_GREY);
M5.Lcd.drawLine(0, 17, 135, 17, TFT_GREY);
M5.Lcd.drawLine(134, 17, 134, 240, TFT_GREY);
M5.Lcd.setCursor(3, 3, 2);
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(0, 0, 2);
M5.Lcd.println("SCORE " + String(score));
M5.Lcd.setCursor(99, 0, 2);
M5.Lcd.println("LVL" + String(level));
fase = fase + 1;
pom = 1;
}
}
5 游戏结束画面
状态为游戏结束时, 显示游戏结束画面, 显示内容包括有些结束, 分数, 等级, 等
M5.Lcd.fillScreen(TFT_BLACK);
M5.Lcd.setCursor(13, 103, 2);
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Lcd.setTextSize(1);
M5.Lcd.println("GAME OVER");
M5.Lcd.setCursor(13, 123, 4);
M5.Lcd.println("SCORE:" + String(score));
M5.Lcd.setCursor(13, 153, 4);
M5.Lcd.println("LEVEL:" + String(level));
M5.Lcd.setCursor(13, 123, 4);
M5.Lcd.println("SCORE:" + String(score));
3. 实现功能的演示及说明
1 向左倾斜, 可以向左移动打击板
2 向右倾斜, 可以向右移动打击板
3 游戏结束进入结算画面
4 结算画面向上倾斜重新进入游戏
4. 对本活动的心得体会
艰难的安装
我的Arduino ESP32库安装了好多次都有问题. 主要是网络的问题, 在下载arduino-esp32的Git hub 仓库的时候就打不开了
后面我Clone仓库下载解压到安装目录, 造成了之后的问题
VS code的嚎叫
在Arduino中可以使用, 之后我转战VScode, 结果发现VScode又不能正常使用ESP32, 直接在开发板管理工具里面找不到了
最后只能删掉库, 在VScode的开发板管理工具里面重新安装才可以完成
解决网络的问题
如果网络有问题, 可以根据命令行的提示, 在github上面找到对应的zip包, 解压到C://user/username/Appdata/Arduino15/staging/package中, Arduino会直接使用下载好的包, 这样就可以大大减少重新安装的时间和避免网络比稳定到时下载失败的情况. 比如说可以使用一些下载工具先进行下载.
总是感觉Arduino作为编辑器还是有很多不足的地方, 比如代码提示, 库的安装之类的. 以后进行开发我会优先使用Linux下的VSCode