【STM32】贪吃蛇 [阶段2](嵌入式进阶方向)

这篇博客是 承接:【项目思维】贪吃蛇(嵌入式进阶方向)中 聚焦于阶段 2 增强游戏可玩性、观赏性,提升用户交互体验,以修正 菜单系统设计与实现,这是游戏的控制操作界面,提升项目的专业性 与 用户的交互式体验的关键环节。

🎮 菜单系统实现详解(贪吃蛇嵌入式版)

目标:为贪吃蛇游戏添加一个完整的图形菜单系统,支持选择游戏、设置、声音、难度等选项。

一、菜单系统的核心概念

菜单系统的本质是:

状态驱动界面: 每个菜单页是一个状态;
选项列表渲染: 显示当前菜单项;
按键导航控制: 上下选择,左右切换,确认/返回。

二、工程代码实现

模块职责:

复制代码
功能							说明
菜单结构定义					菜单项、页、选中项
菜单渲染						在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 项,滚动显示
图标菜单						每项带图标,提升可视性
多语言支持					菜单文字抽象为可切换语言资源
动画切换						页面切换时添加过渡动画

虽然菜单系统相比于贪吃蛇 或者 其他项目的内部实现逻辑 显得过于简单,但是这却是很关键的环节之一,几乎在每一个项目中都会需要,菜单系统是游戏的"导航大脑",实现需要结合 状态机、输入控制和界面渲染三者的融合运用。

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!

相关推荐
CC呢9 小时前
基于单片机老人防丢失防摔倒系统/老人健康状态检测系统
stm32·单片机·嵌入式硬件·单片机设计
CC呢10 小时前
基于单片机智能鞋柜/智能鞋橱/智能鞋盒
单片机·嵌入式硬件·毕业设计·智能鞋柜
小憩-10 小时前
【stm32】定时器(超详细)
stm32·单片机·嵌入式硬件
xhload3d10 小时前
场景切换 × 流畅过渡动画实现方案 | 图扑软件
物联网·3d·智慧城市·html5·动画·webgl·数字孪生·可视化·虚拟现实·工业互联网·工控·工业·2d·轻量化·过渡动画
zimoyin11 小时前
C# FlaUI win 自动化框架,介绍
microsoft·c#·自动化
La Pulga11 小时前
【STM32】GPIO(上)
c语言·stm32·单片机·嵌入式硬件
hardStudy_h11 小时前
STM32最小系统
stm32·单片机·嵌入式硬件
铅笔侠_小龙虾12 小时前
大话 IOT 技术(1) -- 架构篇
物联网·架构
西林er12 小时前
【先楫HPM5E00_EVK系列-板卡测评2】hpm5e00evk平台EtherCat通信
单片机·嵌入式硬件