8.游戏的不同界面
为了便于实现主要功能,之前我们所有的状态控制都是放在游戏中,但实际上我们应该把这些状态控制抽离出来,通过菜单来控制,以便在不同游戏界面间切换。
- 菜单界面
- 游戏界面
- 排行榜
- 游戏结束界面
- 其他一些扩展界面
enum state {
in_menu, //菜单界面
gaming, //游戏界面
_check_ranking,//排行榜界面
snake_dead,//游戏结束界面
to_quit //退出
}State;
9.菜单界面

首先在game.h头文件中,我们先定义好菜单中的内容
//game.h
#define WELCOME "SNAKE"
#define DEAD_INFO "WASTED"
#define RANKING "RANKING"
#define LONG_LINE "------------------------------------------------------------------"
#define CHOICE_NUM 3
#define MENU_ONE "开始游戏"
#define MENU_TWO "查看排名"
#define MENU_THREE "退出游戏"
然后就是选择菜单项的实现,按下前面定义的move_up
,move_down
按钮,实现上下翻,按下enter
实现选择
//game.c
void menu()
{
show_menu();
int input = no_option;
int choice = 0;
int last_choice = 0;
while (State == in_menu)
{
if (_kbhit())
{
input = _getch();
switch (input)
{
case move_up:
if (choice - 1 >= 0)
{
last_choice = choice;
choice = choice - 1;
}
break;
case move_down:
if (choice + 1 < CHOICE_NUM)
{
last_choice = choice;
choice = choice + 1;
}
break;
case enter:
if (choice == 0)
{
State = gaming;
}
else if (choice == 1)
{
State = _check_ranking;
}
else if (choice == 2)
{
State = to_quit;
}
break;
default:
continue;
}
set_cursor_position(10, 6 + choice);
printf("--->");
set_cursor_position(10, 6 + last_choice);
printf(" ");
}
}
}
void show_menu()
{
set_console_color(3, 0);
set_cursor_position(0, 0);
printf("%s", LONG_LINE);
set_cursor_position(15, 1);
printf("%s", WELCOME);
set_cursor_position(0, 2);
printf("%s", LONG_LINE);
set_console_color(8, 0);
set_cursor_position(15, 6);
printf("%s", MENU_ONE);
set_cursor_position(15, 7);
printf("%s", MENU_TWO);
set_cursor_position(15, 8);
printf("%s", MENU_THREE);
set_cursor_position(10, 6);
printf("--->");
}
10. 游戏界面
对前面定义的star_game()
方法进行修改,将状态控制部分提出来即可。
void start_game()
{
struct game *Game = (struct game*)malloc(sizeof(struct game));
Snake *snake = new_born_snake(5, 5);
Game->score = 0;
init_map(Game);
display_map(Game);
display_snake(Game, snake);
grow_food(Game);
int input = no_option;
int last_input = no_option;
while (State == gaming)
{
if (_kbhit())
{
input = _getch();
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
}
else input = no_option;
switch (input)
{
case move_up:
case move_right:
case move_down:
case move_left:
judge_move_input(Game, snake, &input, &last_input);
break;
case esc:
State = in_menu;
break;
case no_option:
default:
snake_move(Game, snake, snake->head->dir);
break;
}
}
free(Game);
}
11.排行界面
如果你的得分可以超过排行榜最后一名的分数,那么会跳出,可以输入名字,可以将得分与玩家名字保存至本地文件rank.txt


int if_can_rank(int score)
{
FILE *fp;
if (fopen_s(&fp, "./rank.txt", "r"))
return 0;
char each_info[15];
int scores[RANK_NUM];
for (int i = 0; i < RANK_NUM*2; i++)
{
fgets(each_info, 15, fp);
if (!( i & 1))
{
scores[i / 2] = atoi(each_info);
}
}
fclose(fp);
return score >= scores[RANK_NUM - 1];
}
void save_score(int score, char* player)
{
FILE *fp = NULL;
if (fopen_s(&fp, "./rank.txt", "r+"))
return;
char each_info[15], new_player[15];
int scores[RANK_NUM];
char names[RANK_NUM][15];
for (int i = 0; i < RANK_NUM * 2 && fgets(each_info, 15, fp) != NULL; i++)
{
if (i & 1)
strcpy_s(*(names + i / 2), 15, each_info);
else scores[i / 2] = atoi(each_info);
}
for (int i = 0; i < RANK_NUM; i++)
{
if (score > scores[i])
{
for (int j = RANK_NUM - 1; j > i; j--)
{
scores[j] = scores[j - 1];
strcpy_s(*(names + j), 15, *(names + j-1));
}
scores[i] = score;
strcpy_s(new_player, 15, player);
strcat_s(new_player, 15, "\n");
strcpy_s(*(names + i), 15, new_player);
break;
}
}
fseek(fp, 0, SEEK_SET);
for (int i = 0; i < RANK_NUM; i++)
{
fprintf_s(fp, "%d\n%s", scores[i], *(names + i));
}
fclose(fp);
}
12.游戏失败界面
如果你的得分没有超过排行榜最后一名的分数,蛇死亡后直接进入游戏失败界面


13.总控制
void main()
{
State = in_menu;
int quit = 0;
while (!quit)
{
switch (State)
{
case in_menu:
system("cls");
menu();
break;
case gaming:
system("cls");
start_game();
break;
case _check_ranking:
system("cls");
rank();
break;
case snake_dead:
show_dead();
break;
case to_quit:
quit = 1;
break;
default:
break;
}
}
}
这样一来,才是比较连贯的游戏体验
13.游戏扩展内容
贪吃蛇小游戏还可以有很多的扩展内容!
- 存档
- 自动寻路
- 多种道具
- 多关卡闯关
- 多人游戏
我将在下一篇博客中实现存档和自动寻路功能,剩下的扩展看时间和心情了哈哈
当然,你也可以自己在此基础版本上扩展!