状态机管理游戏的不同状态(菜单、游戏中、游戏结束)。
状态机是一种管理程序状态的方法:
┌──────────────┐
│ 开始菜单 │
└──────┬───────┘
│ 按 Enter
↓
┌──────────────┐
│ 游戏中 │←──────┐
└──────┬───────┘ │
│ 撞墙 │ 按 R
↓ │
┌──────────────┐ │
│ 游戏结束 │───────┘
└──────┬───────┘
│ 按 M
↓
┌──────────────┐
│ 开始菜单 │
└──────────────┘
typedef enum {
STATE_MENU, // 开始菜单
STATE_PLAYING, // 游戏中
STATE_PAUSED, // 暂停
STATE_GAME_OVER // 游戏结束
} GameState;
typedef struct {
GameState state;
GameState next_state;
} Game;
void change_state(Game* g, GameState new_state) {
g->next_state = new_state;
}
void update_state(Game* g) {
if (g->state != g->next_state) {
// 退出当前状态
state_exit(g->state);
// 转换到新状态
g->state = g->next_state;
// 进入新状态
state_enter(g->state);
}
}
// 每个状态的处理函数
void menu_update(Game* g);
void menu_render(Game* g);
void menu_input(Game* g, int key);
void game_update(Game* g);
void game_render(Game* g);
void game_input(Game* g, int key);
void gameover_update(Game* g);
void gameover_render(Game* g);
void gameover_input(Game* g, int key);
void game_run(Game* g) {
while (g->running) {
// 处理输入
int key = getch();
switch (g->state) {
case STATE_MENU:
menu_input(g, key);
break;
case STATE_PLAYING:
game_input(g, key);
break;
case STATE_GAME_OVER:
gameover_input(g, key);
break;
}
// 更新状态
update_state(g);
// 更新逻辑
switch (g->state) {
case STATE_MENU:
menu_update(g);
break;
case STATE_PLAYING:
game_update(g);
break;
case STATE_GAME_OVER:
gameover_update(g);
break;
}
// 渲染
switch (g->state) {
case STATE_MENU:
menu_render(g);
break;
case STATE_PLAYING:
game_render(g);
break;
case STATE_GAME_OVER:
gameover_render(g);
break;
}
napms(16);
}
}
typedef struct {
void (*update)(Game*);
void (*render)(Game*);
void (*input)(Game*, int);
void (*enter)(Game*);
void (*exit)(Game*);
} StateHandler;
// 定义各个状态的处理器
StateHandler menu_handler = {
.update = menu_update,
.render = menu_render,
.input = menu_input,
.enter = menu_enter,
.exit = menu_exit
};
StateHandler game_handler = {
.update = game_update,
.render = game_render,
.input = game_input,
.enter = game_enter,
.exit = game_exit
};
// 使用
StateHandler* current_handler = &menu_handler;
void game_loop(Game* g) {
while (g->running) {
int key = getch();
current_handler->input(g, key);
current_handler->update(g);
current_handler->render(g);
}
}
#include <ncurses.h>
#include <stdbool.h>
typedef enum {
STATE_MENU,
STATE_PLAYING,
STATE_GAME_OVER
} State;
typedef struct {
State state;
State next_state;
bool running;
int x, y;
int score;
} Game;
// 菜单
void menu_render(Game* g) {
clear();
mvprintw(10, 30, "=== SNAKE GAME ===");
mvprintw(12, 28, "Press ENTER to start");
mvprintw(14, 30, "Press Q to quit");
refresh();
}
void menu_input(Game* g, int key) {
if (key == '\n') { // Enter
g->next_state = STATE_PLAYING;
g->x = 10; g->y = 10; g->score = 0;
} else if (key == 'q') {
g->running = false;
}
}
// 游戏
void game_update(Game* g) {
// 移动逻辑
}
void game_render(Game* g) {
clear();
mvprintw(0, 0, "Score: %d", g->score);
mvaddch(g->y, g->x, 'O');
refresh();
}
void game_input(Game* g, int key) {
if (key == KEY_UP) g->y--;
else if (key == KEY_DOWN) g->y++;
else if (key == KEY_LEFT) g->x--;
else if (key == KEY_RIGHT) g->x++;
else if (key == 'q') g->next_state = STATE_GAME_OVER;
}
// 游戏结束
void gameover_render(Game* g) {
clear();
mvprintw(10, 30, "GAME OVER");
mvprintw(12, 28, "Final Score: %d", g->score);
mvprintw(14, 26, "R to restart, M for menu");
refresh();
}
void gameover_input(Game* g, int key) {
if (key == 'r') {
g->next_state = STATE_PLAYING;
g->x = 10; g->y = 10; g->score = 0;
} else if (key == 'm') {
g->next_state = STATE_MENU;
}
}
// 主循环
void game_run(Game* g) {
while (g->running) {
// 状态转换
if (g->state != g->next_state) {
g->state = g->next_state;
}
int key = getch();
switch (g->state) {
case STATE_MENU:
menu_input(g, key);
menu_render(g);
break;
case STATE_PLAYING:
game_input(g, key);
game_update(g);
game_render(g);
break;
case STATE_GAME_OVER:
gameover_input(g, key);
gameover_render(g);
break;
}
napms(100);
}
}
int main(void) {
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
Game game = {
.state = STATE_MENU,
.next_state = STATE_MENU,
.running = true
};
game_run(&game);
endwin();
return 0;
}
为贪吃蛇添加暂停状态
实现一个设置菜单状态
添加状态转换时的动画效果
下一课:完成与扩展