内容介绍
内容介绍
一、项目介绍
本项目基于树莓派RP2040开发板,实现了一款重力感应控制的迷宫滚球小游戏。玩家通过倾斜游戏机来控制小球在迷宫中移动,目标是在规定时间内到达终点,同时避开墙壁以获得更高分数。项目集成了姿态传感器、LCD显示、按键输入和蜂鸣器输出等功能,提供了完整的游戏体验。
1.1 硬件介绍

本项目使用的硬件组件包括:
组件名称 | 型号/规格 | 功能描述 |
|---|---|---|
主控芯片 | 树莓派RP2040 | 负责游戏逻辑处理和硬件控制 |
显示屏幕 | 240x240 LCD | 显示游戏界面、迷宫、小球和游戏信息 |
姿态传感器 | MMA7660 | 检测设备倾斜角度,控制小球移动 |
按键 | 4个物理按键 | 实现开始、暂停、重开与关卡切换功能 |
蜂鸣器 | 压电蜂鸣器 | 提供碰撞和通关的声音提示 |
存储 | 板载Flash | 存储迷宫地图数据 |
硬件连接关系:
- LCD屏幕通过SPI接口连接到RP2040
- MMA7660姿态传感器通过I2C接口连接
- 按键直接连接到RP2040的GPIO引脚
- 蜂鸣器连接到RP2040的PWM输出引脚
- USB Type C连接器用于供电、程序下载
1.2 功能概览
本项目实现了以下功能:
- 重力感应控制:使用MMA7660姿态传感器检测设备倾斜角度,控制小球在迷宫中移动
- 多关卡迷宫:设计了5个不同难度的固定迷宫,每个迷宫有独立的起点和终点
- 游戏状态管理:包含启动界面、游戏菜单、游戏中、暂停、关卡完成和游戏结束等状态
- 计时与得分系统:每关限时60秒,根据完成时间和碰撞次数计算得分
- 碰撞检测:检测小球与墙壁的碰撞,增加惩罚时间并播放碰撞音效
- 声音提示:碰撞和通关时播放不同的音效
- 迷宫数据存储:支持将迷宫数据保存到Flash和从Flash加载
- 按键控制:通过按键实现开始、暂停、重开、关卡切换等操作
1.3 设计思路
- 模块化设计:将游戏功能分解为多个独立模块,如硬件初始化、迷宫绘制、小球控制、碰撞检测、输入处理等
- 状态机管理:使用状态机模式管理游戏流程,清晰地处理不同游戏状态之间的转换
- 数据结构设计:使用结构体存储关卡信息,包括起点、终点和迷宫地图
- 传感器数据处理:对MMA7660传感器数据进行校准和处理,实现平滑的小球控制
- 碰撞检测算法:基于网格的碰撞检测,准确判断小球是否与墙壁碰撞
- 得分计算逻辑:根据完成时间和碰撞惩罚计算关卡得分,并累加总分

二、功能实现
2.1 软件流程图
2.2 实现过程
1. 硬件初始化
void initializeHardware() {
// 初始化串口
Serial.begin(115200);
// 初始化I2C总线(用于MMA7660)
i2c.begin();
// 初始化MMA7660
accelemeter.begin();
accelemeter.setMode(MMA7660::MODE_ACTIVE);
// 初始化LCD
tft.init();
tft.setRotation(LCD_ROTATION);
tft.fillScreen(TFT_BLACK);
// 初始化按键引脚
pinMode(BUTTON_A, INPUT_PULLUP);
pinMode(BUTTON_B, INPUT_PULLUP);
pinMode(BUTTON_START, INPUT_PULLUP);
pinMode(BUTTON_MENU, INPUT_PULLUP);
// 初始化蜂鸣器引脚
pinMode(BUZZER_PIN, OUTPUT);
// 初始化摇杆引脚
pinMode(JOYSTICK_X, INPUT);
pinMode(JOYSTICK_Y, INPUT);
}
2. 迷宫数据结构
typedef struct {
uint8_t startX;
uint8_t startY;
uint8_t goalX;
uint8_t goalY;
uint8_t maze[MAZE_SIZE][MAZE_SIZE];
} LevelInfo;
LevelInfo levels[MAX_LEVELS] = {
{
.startX = 1,
.startY = 1,
.goalX = 18,
.goalY = 17,
.maze = {
// 迷宫数据...
}
},
// 更多关卡...
};
3. 小球控制与碰撞检测
void updateBallPosition() {
float accelX = 0, accelY = 0;
// 读取IMU数据
if (imuEnabled) {
float x, y, z;
accelemeter.getAcceleration(&x, &y, &z);
// 对调X和Y轴数据,并将X轴反向
accelX = y;
accelY = -x;
// 应用校准偏移值
accelX -= accelXOffset;
accelY -= accelYOffset;
// 加速度阈值和档位设置
// ...
// 计算速度增益
// ...
// 方向优先级逻辑
// ...
// 更新速度
ballSpeedX += accelX * gainX;
ballSpeedY += accelY * gainY;
}
// 速度限制
// ...
// 摩擦力
ballSpeedX *= 0.95;
ballSpeedY *= 0.95;
// 更新位置
ballX += ballSpeedX;
ballY += ballSpeedY;
// 边界检查
// ...
// 清除旧球位置并绘制新位置
// ...
}
void checkCollision() {
// 计算迷宫参数
// ...
int cellX = (ballX - mazeStartX) / cellSize;
int cellY = (ballY - mazeStartY) / cellSize;
// 检查墙壁碰撞
if (levels[currentLevel].maze[cellY][cellX] == 1) {
// 增加惩罚时间
penaltyTime += PENALTY_TIME * 1000;
// 播放碰撞音效
playCollisionSound();
}
// 检查终点
if (cellX == levels[currentLevel].goalX && cellY == levels[currentLevel].goalY) {
gameState = STATE_LEVEL_COMPLETE;
}
}
4. 得分系统
void nextLevel() {
// 计算得分
unsigned long totalTime = millis() - startTime + penaltyTime;
levelScore = 1000 - (int)(totalTime / 100);
if (levelScore < 0) levelScore = 0;
totalScore += levelScore;
// 播放胜利音效
playVictorySound();
// 检查是否通关所有关卡
if (currentLevel < MAX_LEVELS - 1) {
// 进入下一关
loadMaze(currentLevel + 1);
gameState = STATE_PLAYING;
} else {
// 游戏结束
gameState = STATE_GAME_OVER;
}
}
5. 游戏状态管理
void loop() {
switch (gameState) {
case STATE_INIT:
// 启动界面
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(2);
tft.setCursor(60, 70);
tft.println("Gravity Maze");
tft.setTextSize(1);
tft.setCursor(70, 120);
tft.println("Press A to start");
tft.setCursor(65, 140);
tft.println("Press Menu to set");
// 检测按键
bool aState = digitalRead(BUTTON_A) == LOW;
bool menuState = digitalRead(BUTTON_MENU) == LOW;
delay(BUTTON_DEBOUNCE);
if (aState || menuState) {
gameState = STATE_MENU;
}
break;
case STATE_MENU:
drawMenu();
handleMenuInput();
break;
case STATE_PLAYING:
updateBallPosition();
checkCollision();
handleInput();
updateDisplay();
break;
case STATE_PAUSED:
tft.setCursor(80, 110);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(2);
tft.print("Paused");
handleInput();
break;
case STATE_LEVEL_COMPLETE:
nextLevel();
break;
case STATE_GAME_OVER:
gameOver();
break;
}
}
6. 迷宫数据存储
void saveMazesToFlash() {
// 准备数据
MazeData data;
data.version = FLASH_VERSION;
memcpy(data.levels, levels, sizeof(levels));
// 擦除Flash
flashIap.erase(FLASH_MAZE_START, 4096);
// 写入数据
flashIap.program((const uint8_t*)&data, FLASH_MAZE_START, sizeof(data));
}
void loadMazesFromFlash() {
// 从Flash读取数据
MazeData data;
flashIap.read((uint8_t*)&data, FLASH_MAZE_START, sizeof(data));
// 检查版本
if (data.version == FLASH_VERSION) {
memcpy(levels, data.levels, sizeof(levels));
}
}
三、功能展示
启动界面
游戏启动后显示启动界面,包含游戏标题"Gravity Maze"和操作提示"Press A to start"、"Press Menu to set"。


游戏界面
游戏界面显示当前关卡、迷宫地图、小球位置、剩余时间等信息。迷宫使用蓝色线条绘制墙壁,红色标记起点,黄色标记终点。


游戏操作
- A键:开始游戏/暂停游戏
- B键:返回启动界面
- Start键:同A键功能,长按切换IMU状态
- Menu键:打开/关闭菜单
- A+B长按:重开本关
- Start+Menu长按:软重启
游戏流程
- 启动游戏,进入启动界面
- 按A键进入菜单,选择关卡
- 进入游戏,通过倾斜设备控制小球移动
- 避开墙壁,尽快到达终点
- 完成当前关卡后,自动进入下一关
- 完成所有关卡后,显示总分并返回启动界面
四、总结
遇到的问题
- 传感器噪声:初期姿态数据抖动明显,通过互补滤波和适当降低灵敏度得以改善。
- 使用的是 TFT_eSPI 绘制的地图,导致小球在移动过程中会擦除一点路径的墙壁。暂时没有什么好的解决方案。
心得体会
本项目成功实现了基于树莓派RP2040的重力迷宫滚球小游戏,具有以下特点:
- 完整的游戏体验:实现了从启动到通关的完整游戏流程,包括多个关卡、计时、得分、音效等功能
- 良好的控制体验:使用MMA7660姿态传感器实现了流畅的重力感应控制,支持方向优先级逻辑
- 模块化设计:代码结构清晰,模块化程度高,便于维护和扩展
- 数据持久化:支持将迷宫地图数据保存到Flash,实现数据持久化
- 硬件资源利用:充分利用了RP2040的硬件资源,包括I2C、SPI、GPIO等
项目还存在一些可以改进的地方:
- 迷宫编辑器:可以开发一个迷宫编辑器,允许用户自定义迷宫
- 难度调整:可以增加难度设置,调整迷宫大小、时间限制等参数
- 更多游戏模式:可以添加时间挑战、无尽模式等游戏模式
- 视觉效果:可以增加小球移动的动画效果,提升游戏视觉体验
- 电源管理:可以优化电源管理,延长电池续航时间
总体而言,本项目成功实现了一个功能完整、体验良好的重力迷宫滚球小游戏,展示了树莓派RP2040的强大功能和应用潜力。
最后感谢电子森林推出的 《寒假练》 系列活动,对于我来说是个很好的学习机会,理论结合实践。我们下期活动再见!
五、参考资料
- https://github.com/ams-OSRAM/tmf8820_21_28_driver_arduino
- https://github.com/ams-OSRAM/tmf8820_21_28_driver_python
- https://docs.micropython.org/en/latest/index.html
- https://www.eetree.cn/platform/3616
- https://blog.csdn.net/qq_42679415/article/details/136740895
- https://gitee.com/eetree-git/RP2040_Game_Kit
软硬件
附件下载
源码.zip
PlatformIO 工程源码
团队介绍
业余电子爱好者
团队成员
鲜de芒果
评论
0 / 100
查看更多
猜你喜欢
2026寒假练 - 用RP2040游戏机实现重力迷宫滚球小游戏该项目使用了树莓派RP2040、MicroPython语言,实现了【RP2040游戏机】重力迷宫滚球小游戏的设计,它的主要功能为:使用姿态传感器(IMU)俯仰和横滚控制小球在迷宫中移动。
刘文博+1120212208
11
2026寒假练 - 用RP2040游戏机设计重力迷宫滚球小游戏该项目使用了RP2040游戏机,实现了重力迷宫滚球小游戏的设计,它的主要功能为:使用姿态传感器(IMU)俯仰和横滚控制小球在迷宫中移动。
符煜晨
20
2026寒假练 - 用RP2040实现重力迷宫滚球小游戏该项目使用了Micropython语言,实现了一个基于RP2040游戏机的迷宫滚球小游戏的设计,它的主要功能为:通过倾斜来控制小球在迷宫中的移动。
沐111
11