一、项目概述
本项目在STM32平台上实现两款经典小游戏:恐龙小跳(类似Chrome离线游戏)和躲避障碍小游戏。两款游戏均使用OLED显示屏(SSD1306)和简单按键控制,具有完整的游戏逻辑、动画效果和计分系统。
二、系统架构
+-------------------+ +-------------------+ +-------------------+
| 输入处理模块 | | 游戏逻辑模块 | | 显示驱动模块 |
| (按键扫描与消抖) |<--->| (双游戏状态机) |<--->| (OLED显示控制) |
+-------------------+ +-------------------+ +-------------------+
| | | | | | | | | | | | |
| | | | | | | | | | +---->+-------------------+
| | | | | | | | | +-------->| 游戏状态管理 |
| | | | | | | | +------------>| 分数计算 |
| | | | | | | +---------------->| 动画效果 |
| | | | | | +-------------------->| 音效反馈(可选) |
| | | | | +-------------------------------->| |
| | | | +------------------------------------>| |
| | | +---------------------------------------->| |
| | +-------------------------------------------->| |
| +--------------------------------------------------->| |
+--------------------------------------------------->| |
三、硬件配置
主控芯片:STM32F103C8T6
显示模块:0.96寸OLED(SSD1306,128×64,I2C)
输入设备:
按键1:游戏选择/跳跃/上移
按键2:开始/下移/左移
按键3:返回/右移
音频输出:无源蜂鸣器(可选)
四、游戏1:恐龙小跳(Dino Jump)
1. 游戏机制
玩家控制恐龙跳跃躲避仙人掌
随着分数增加,游戏速度逐渐加快
碰撞仙人掌游戏结束
2. 核心代码实现
// dino.h
#define DINO_WIDTH 8
#define DINO_HEIGHT 8
#define GROUND_Y 56
#define GRAVITY 1
#define JUMP_FORCE -12
typedef struct {
int x, y; // 位置
int vel_y; // 垂直速度
int is_jumping; // 跳跃状态
int width, height; // 尺寸
} Dino;
typedef struct {
int x; // 位置
int width, height; // 尺寸
} Cactus;
// dino.c
void InitDino(Dino* dino) {
dino->x = 20;
dino->y = GROUND_Y - DINO_HEIGHT;
dino->vel_y = 0;
dino->is_jumping = 0;
dino->width = DINO_WIDTH;
dino->height = DINO_HEIGHT;
}
void UpdateDino(Dino* dino) {
// 应用重力
dino->vel_y += GRAVITY;
dino->y += dino->vel_y;
// 地面碰撞检测
if (dino->y > GROUND_Y - DINO_HEIGHT) {
dino->y = GROUND_Y - DINO_HEIGHT;
dino->vel_y = 0;
dino->is_jumping = 0;
}
}
void JumpDino(Dino* dino) {
if (!dino->is_jumping) {
dino->vel_y = JUMP_FORCE;
dino->is_jumping = 1;
}
}
void InitCactus(Cactus* cactus) {
cactus->x = 128;
cactus->width = 6;
cactus->height = 12;
}
void UpdateCactus(Cactus* cactus, int speed) {
cactus->x -= speed;
if (cactus->x < -10) {
cactus->x = 128;
}
}
int CheckCollision(Dino* dino, Cactus* cactus) {
return (dino->x < cactus->x + cactus->width &&
dino->x + dino->width > cactus->x &&
dino->y < GROUND_Y &&
dino->y + dino->height > GROUND_Y - cactus->height);
}
五、游戏2:躲避障碍(Obstacle Avoidance)
1. 游戏机制
玩家控制飞船左右移动躲避陨石
随机生成不同大小和速度的陨石
随着时间增加,陨石数量增多
被陨石击中游戏结束
2. 核心代码实现
// obstacle.h
#define SHIP_WIDTH 12
#define SHIP_HEIGHT 8
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
typedef struct {
int x, y; // 位置
int width, height; // 尺寸
} Ship;
typedef struct {
int x, y; // 位置
int width, height; // 尺寸
int speed; // 速度
} Asteroid;
// obstacle.c
void InitShip(Ship* ship) {
ship->x = SCREEN_WIDTH / 2 - SHIP_WIDTH / 2;
ship->y = SCREEN_HEIGHT - SHIP_HEIGHT - 5;
ship->width = SHIP_WIDTH;
ship->height = SHIP_HEIGHT;
}
void MoveShip(Ship* ship, int dir) {
ship->x += dir * 5;
if (ship->x < 0) ship->x = 0;
if (ship->x > SCREEN_WIDTH - SHIP_WIDTH)
ship->x = SCREEN_WIDTH - SHIP_WIDTH;
}
void GenerateAsteroid(Asteroid* ast) {
ast->x = SCREEN_WIDTH;
ast->y = rand() % (SCREEN_HEIGHT - 20) + 10;
ast->width = rand() % 10 + 5;
ast->height = rand() % 10 + 5;
ast->speed = rand() % 3 + 1;
}
void UpdateAsteroid(Asteroid* ast) {
ast->x -= ast->speed;
}
int CheckHit(Ship* ship, Asteroid* ast) {
return (ship->x < ast->x + ast->width &&
ship->x + ship->width > ast->x &&
ship->y < ast->y + ast->height &&
ship->y + ship->height > ast->y);
}
六、双游戏整合实现
1. 主游戏状态机
// game.h
typedef enum {
MENU,
DINO_GAME,
OBSTACLE_GAME,
GAME_OVER
} GameState;
typedef struct {
GameState state;
Dino dino;
Cactus cactus;
Ship ship;
Asteroid asteroids[5];
int asteroid_count;
int score;
int game_speed;
int selected_game;
} GameManager;
2. 主游戏循环
// main.c
int main(void) {
HAL_Init();
SystemClock_Config();
OLED_Init();
Input_Init();
GameManager game = {
0};
game.state = MENU;
game.selected_game = 0; // 0: Dino, 1: Obstacle
while (1) {
ProcessInput(&game);
UpdateGame(&game);
RenderGame(&game);
HAL_Delay(30); // 控制游戏速度
}
}
void ProcessInput(GameManager* game) {
if (game->state == MENU) {
if (KeyPressed(KEY_UP)) {
game->selected_game = (game->selected_game + 1) % 2;
} else if (KeyPressed(KEY_DOWN)) {
game->selected_game = (game->selected_game + 1) % 2;
} else if (KeyPressed(KEY_ENTER)) {
if (game->selected_game == 0) {
game->state = DINO_GAME;
InitDinoGame(game);
} else {
game->state = OBSTACLE_GAME;
InitObstacleGame(game);
}
}
}
else if (game->state == DINO_GAME) {
if (KeyPressed(KEY_JUMP)) {
JumpDino(&game->dino);
}
}
else if (game->state == OBSTACLE_GAME) {
if (KeyPressed(KEY_LEFT)) {
MoveShip(&game->ship, -1);
} else if (KeyPressed(KEY_RIGHT)) {
MoveShip(&game->ship, 1);
}
}
else if (game->state == GAME_OVER) {
if (KeyPressed(KEY_ENTER)) {
game->state = MENU;
}
}
}
void UpdateGame(GameManager* game) {
if (game->state == DINO_GAME) {
UpdateDino(&game->dino);
UpdateCactus(&game->cactus, game->game_speed);
if (CheckCollision(&game->dino, &game->cactus)) {
game->state = GAME_OVER;
}
// 计分
if (game->cactus.x < 0) {
game->score += 10;
game->game_speed = 3 + game->score / 100;
InitCactus(&game->cactus);
}
}
else if (game->state == OBSTACLE_GAME) {
// 随机生成新陨石
if (rand() % 20 == 0 && game->asteroid_count < 5) {
GenerateAsteroid(&game->asteroids[game->asteroid_count]);
game->asteroid_count++;
}
// 更新所有陨石
for (int i = 0; i < game->asteroid_count; i++) {
UpdateAsteroid(&game->asteroids[i]);
if (CheckHit(&game->ship, &game->asteroids[i])) {
game->state = GAME_OVER;
}
// 移除超出屏幕的陨石
if (game->asteroids[i].x < -20) {
// 移动最后一个陨石到当前位置
if (i < game->asteroid_count - 1) {
game->asteroids[i] = game->asteroids[game->asteroid_count - 1];
}
game->asteroid_count--;
}
}
// 计分
game->score++;
}
}
七、显示渲染系统
1. 双游戏渲染
void RenderGame(GameManager* game) {
OLED_Clear();
switch (game->state) {
case MENU:
RenderMenu(game);
break;
case DINO_GAME:
RenderDinoGame(game);
break;
case OBSTACLE_GAME:
RenderObstacleGame(game);
break;
case GAME_OVER:
RenderGameOver(game);
break;
}
OLED_Refresh();
}
void RenderDinoGame(GameManager* game) {
// 绘制地面
OLED_DrawLine(0, GROUND_Y, 127, GROUND_Y);
// 绘制恐龙
OLED_DrawRect(game->dino.x, game->dino.y,
game->dino.width, game->dino.height);
// 绘制仙人掌
OLED_DrawRect(game->cactus.x, GROUND_Y - game->cactus.height,
game->cactus.width, game->cactus.height);
// 显示分数
char score_str[20];
sprintf(score_str, "Score: %d", game->score);
OLED_ShowString(0, 0, score_str);
}
void RenderObstacleGame(GameManager* game) {
// 绘制飞船
OLED_DrawTriangle(game->ship.x, game->ship.y + game->ship.height,
game->ship.x + game->ship.width/2, game->ship.y,
game->ship.x + game->ship.width, game->ship.y + game->ship.height);
// 绘制所有陨石
for (int i = 0; i < game->asteroid_count; i++) {
OLED_DrawCircle(game->asteroids[i].x + game->asteroids[i].width/2,
game->asteroids[i].y + game->asteroids[i].height/2,
game->asteroids[i].width/2);
}
// 显示分数
char score_str[20];
sprintf(score_str, "Score: %d", game->score);
OLED_ShowString(0, 0, score_str);
}
八、游戏特色功能
1. 动画效果
// 恐龙动画(奔跑效果)
void AnimateDino(Dino* dino, int frame) {
if (frame % 10 < 5) {
// 绘制站立状态
OLED_DrawRect(dino->x, dino->y, 8, 8);
} else {
// 绘制抬腿状态
OLED_DrawRect(dino->x, dino->y, 8, 4);
OLED_DrawRect(dino->x, dino->y+4, 4, 4);
}
}
// 爆炸效果
void DrawExplosion(int x, int y) {
for (int i = 0; i < 8; i++) {
int angle = i * 45;
int dx = cos(angle) * 5;
int dy = sin(angle) * 5;
OLED_DrawPixel(x + dx, y + dy, 1);
}
}
2. 音效系统(可选)
// 使用PWM驱动蜂鸣器
void PlaySound(int frequency, int duration) {
uint32_t period = 1000000 / frequency;
uint32_t cycles = duration * 1000 / period;
for (uint32_t i = 0; i < cycles; i++) {
HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_SET);
Delay_us(period / 2);
HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_RESET);
Delay_us(period / 2);
}
}
// 游戏音效
void PlayJumpSound() {
PlaySound(523, 50); // C5
}
void PlayCrashSound() {
PlaySound(196, 200); // G3
PlaySound(131, 300); // C3
}
3. 游戏难度曲线
// 动态调整游戏难度
void UpdateDifficulty(GameManager* game) {
if (game->state == DINO_GAME) {
// 每100分增加速度
game->game_speed = 3 + game->score / 100;
// 每200分增加障碍物频率
if (game->score % 200 == 0) {
// 增加新障碍物类型
}
}
else if (game->state == OBSTACLE_GAME) {
// 每500分增加陨石数量
if (game->score % 500 == 0 && game->asteroid_count < 8) {
// 增加陨石数量上限
}
}
}
参考代码 基于stm32的恐龙小跳游戏+躲避障碍小游戏 www.youwenfan.com/contentalh/183117.html
九、项目资源
1. 硬件连接
| 外设 | STM32引脚 | 功能 |
|---|---|---|
| OLED_SDA | PB7 | I2C数据线 |
| OLED_SCL | PB6 | I2C时钟线 |
| KEY_UP | PA0 | 上/跳跃 |
| KEY_DOWN | PA1 | 下/选择 |
| KEY_LEFT | PA2 | 左移 |
| KEY_RIGHT | PA3 | 右移 |
| KEY_ENTER | PA4 | 确认/开始 |
| BUZZER | PA5 | 音频输出 |
2. 开发环境
IDE: STM32CubeIDE
编译器: GCC ARM Embedded
库依赖:
HAL库
SSD1306 OLED驱动
标准外设库
3. 项目结构
├── Core/
│ ├── Inc/
│ │ ├── game.h
│ │ ├── dino.h
│ │ ├── obstacle.h
│ │ └── oled.h
│ ├── Src/
│ │ ├── main.c
│ │ ├── game.c
│ │ ├── dino.c
│ │ ├── obstacle.c
│ │ └── oled.c
├── Drivers/
└── STM32F1xx_HAL_Driver/
十、总结
本实现展示了如何在STM32平台上开发两款经典小游戏:恐龙小跳和躲避障碍。项目特点包括:
双游戏整合:通过状态机实现两款游戏的无缝切换
完整游戏机制:包含角色控制、碰撞检测、计分系统和难度曲线
动画效果:实现角色动画和特效
可扩展性:模块化设计便于添加新游戏或功能
资源优化:针对嵌入式平台优化资源使用