这篇博客是 承接:【项目思维】贪吃蛇(嵌入式进阶方向)中 聚焦于阶段 2 增强游戏可玩性、观赏性,提升用户交互体验,以修正 菜单系统设计与实现,这是游戏的控制操作界面,提升项目的专业性 与 用户的交互式体验的关键环节。
🎮 菜单系统实现详解(贪吃蛇嵌入式版)
目标:为贪吃蛇游戏添加一个完整的图形菜单系统,支持选择游戏、设置、声音、难度等选项。
一、菜单系统的核心概念
菜单系统的本质是:
状态驱动界面: 每个菜单页是一个状态;
选项列表渲染: 显示当前菜单项;
按键导航控制: 上下选择,左右切换,确认/返回。
二、工程代码实现
模块设计(menu.c / menu.h)
模块职责:
功能 说明
菜单结构定义 菜单项、页、选中项
菜单渲染 在OLED/TFT 上绘制菜单
按键响应 处理上下左右/确认返回按键事件
状态切换 进入游戏、设置参数、退出等
📦 菜单结构定义(menu.h)[基于模块化的结构设计来编程]
c
#ifndef __MENU_H__
#define __MENU_H__
typedef enum {
MENU_MAIN,
MENU_DIFFICULTY,
MENU_SOUND,
MENU_ABOUT
} MenuPage;
typedef enum {
DIFF_EASY,
DIFF_NORMAL,
DIFF_HARD
} Difficulty;
typedef struct {
MenuPage current_page;
uint8_t selected_index;
uint8_t total_items;
} MenuState;
// 全局菜单状态
extern MenuState menu;
void Menu_Init(void);
void Menu_Draw(void);
void Menu_Next(void); // 向下
void Menu_Previous(void); // 向上
void Menu_Select(void); // 确认
void Menu_Back(void); // 返回
Difficulty Menu_GetDifficulty(void);
uint8_t Menu_IsSoundOn(void);
uint8_t Menu_IsGameStartRequested(void);
#endif
三、选项设计(菜单界面)
1. 主菜单(MENU_MAIN)
> ▶ Start Game
▶ Difficulty
▶ Sound: ON
▶ About
2. 难度菜单(MENU_DIFFICULTY)
> ◉ Easy
○ Normal
○ Hard
3. 声音菜单(MENU_SOUND)
> Sound: ON
Sound: OFF
🧩 四、实现主逻辑(menu.c
)
1. 全局状态
c
MenuState menu = {
.current_page = MENU_MAIN,
.selected_index = 0,
.total_items = 4
};
static Difficulty current_difficulty = DIFF_NORMAL;
static uint8_t sound_on = 1;
static uint8_t game_start = 0;
2. 渲染函数
c
void Menu_Draw(void) {
OLED_Clear();
switch (menu.current_page) {
case MENU_MAIN:
OLED_ShowString(0, 0, menu.selected_index == 0 ? "> Start Game" : " Start Game", 12);
OLED_ShowString(0, 12, menu.selected_index == 1 ? "> Difficulty" : " Difficulty", 12);
OLED_ShowString(0, 24, menu.selected_index == 2 ? "> Sound: ON" : " Sound: ON", 12);
OLED_ShowString(0, 36, menu.selected_index == 3 ? "> About" : " About", 12);
break;
case MENU_DIFFICULTY:
OLED_ShowString(0, 0, current_difficulty == DIFF_EASY ? "> ◉ Easy" : " ○ Easy", 12);
OLED_ShowString(0, 12, current_difficulty == DIFF_NORMAL ? "> ◉ Normal" : " ○ Normal", 12);
OLED_ShowString(0, 24, current_difficulty == DIFF_HARD ? "> ◉ Hard" : " ○ Hard", 12);
break;
case MENU_SOUND:
OLED_ShowString(0, 0, sound_on ? "> ON" : " ON", 12);
OLED_ShowString(0, 12, !sound_on ? "> OFF" : " OFF", 12);
break;
case MENU_ABOUT:
OLED_ShowString(0, 0, "Snake v1.0", 12);
OLED_ShowString(0, 12, "By You", 12);
OLED_ShowString(0, 24, "Press Back", 12);
break;
}
OLED_Refresh();
}
3. 按键响应函数
c
void Menu_Next(void) {
if (++menu.selected_index >= menu.total_items) {
menu.selected_index = 0;
}
Menu_Draw();
}
void Menu_Previous(void) {
if (menu.selected_index == 0)
menu.selected_index = menu.total_items - 1;
else
menu.selected_index--;
Menu_Draw();
}
void Menu_Select(void) {
switch (menu.current_page) {
case MENU_MAIN:
switch (menu.selected_index) {
case 0: game_start = 1; break;
case 1: menu.current_page = MENU_DIFFICULTY; menu.selected_index = 0; menu.total_items = 3; break;
case 2: menu.current_page = MENU_SOUND; menu.selected_index = 0; menu.total_items = 2; break;
case 3: menu.current_page = MENU_ABOUT; menu.selected_index = 0; menu.total_items = 1; break;
}
break;
case MENU_DIFFICULTY:
current_difficulty = (Difficulty)menu.selected_index;
menu.current_page = MENU_MAIN;
menu.selected_index = 1;
menu.total_items = 4;
break;
case MENU_SOUND:
sound_on = menu.selected_index == 0 ? 1 : 0;
menu.current_page = MENU_MAIN;
menu.selected_index = 2;
menu.total_items = 4;
break;
case MENU_ABOUT:
menu.current_page = MENU_MAIN;
menu.selected_index = 3;
menu.total_items = 4;
break;
}
Menu_Draw();
}
void Menu_Back(void) {
menu.current_page = MENU_MAIN;
menu.selected_index = 0;
menu.total_items = 4;
Menu_Draw();
}
4. 主循环中调用菜单逻辑(main.c
)
c
int main(void) {
OLED_Init();
KEY_Init();
Menu_Init();
Menu_Draw();
while (1) {
if (KEY_UP_PRESSED()) Menu_Previous();
else if (KEY_DOWN_PRESSED()) Menu_Next();
else if (KEY_OK_PRESSED()) Menu_Select();
else if (KEY_BACK_PRESSED()) Menu_Back();
if (Menu_IsGameStartRequested()) {
Snake_SetDifficulty(Menu_GetDifficulty());
Sound_SetEnabled(Menu_IsSoundOn());
Game_Start();
Menu_Init(); // 重置菜单状态
Menu_Draw();
}
HAL_Delay(100);
}
}
最终效果:
OLED 或 TFT 屏幕显示:
> ▶ Start Game
▶ Difficulty
▶ Sound: ON
▶ About
按键上下移动光标,确认后进入子菜单。
另外,还可以扩展功能:
扩展 实现
可滑动菜单 支持超过 4 项,滚动显示
图标菜单 每项带图标,提升可视性
多语言支持 菜单文字抽象为可切换语言资源
动画切换 页面切换时添加过渡动画
虽然菜单系统相比于贪吃蛇 或者 其他项目的内部实现逻辑 显得过于简单,但是这却是很关键的环节之一,几乎在每一个项目中都会需要,菜单系统是游戏的"导航大脑",实现需要结合 状态机、输入控制和界面渲染三者的融合运用。
以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!