欢迎来到本期频道!
在学习C语言的道路上,有些小伙伴可能还没遇到自己喜欢的游戏- -
希望贪吃蛇能够给你带来乐趣- -
一:思考代码实现方向
1.我们要用C语言实现该游戏,就需要明白蛇是维护的对象,因为玩的就是蛇;由于C语言是一门面向过程的编程语言,所以我们主要实现的是对象的运行过程,既蛇的运行过程–例如蛇身的变化,蛇的状态,蛇的方向等。 |
2.由此可以知道,我们需要创建结构体蛇,为了方便维护蛇,该结构体应该包含相关蛇的基本属性–例如蛇身,蛇的方向,食物,蛇的状态等;然后根据蛇的属性来实现蛇的运行过程。 |
二:梳理逻辑
1.写大框架
可以把游戏分为三个阶段,准备阶段-进行阶段-结束阶段。其实这就是大框架。我们在编写代码时,可以先把大框架封装成函数,这样不仅思路清晰,还能梳理逻辑。例如: |
void game()
{
Snake snake={0};
game_start(&snake);
game_run(&snake);
game_end(&snake);
}
#pragma once
typedef struct Snakenode
{
int x;
int y;
wchar_t ch;
struct Snakenode* next;
}Snakenode,* Psnakenode;
typedef enum Dir
{
Up,
Down,
Left,
Right
}Dir;
typedef enum Speed
{
One=1,
Two,
Three,
Four,
Five
}Speed;
typedef enum State
{
Ok,
Kill_Byself,
Kill_Bywall,
End_Normal
}
typedef struct Snake
{
Psnakenode phead;
Dir dir;
Speed speed;
Stata stata;
Psnakenode food;
int sleeptime;
int food_weight;
int grade;
}Snake,*Psnake;
void game_start(Psnake sp);
void game_run(Psnake sp);
void game_end(Psnake sp);
2.写子框架
void game_start(Psnake sp)
{
welcome(sp);
print_wall(sp);
gradeinfo(sp);
}
void game_run(Psnake sp)
{
snake_init(sp);
do
{
snake_modify(sp);
snake_state(sp);
gradeinfo(sp);
}while();
}
void game_end(Psnake sp)
{
snakebody_omit(sp);
}
这里的函数声明可以放在头文件中。
有了这样的框架,是不是发现只剩下函数的实现了,但是好像又写不出来🤔。
因为贯穿整个游戏的基本方法还不知道 :
❶.怎么打印宽字符
❷.怎么隐藏光标
❸.怎么指定位置打印
❹.怎么获取按键信息
Ⅰ怎么打印宽字符
setlocale
#include<locale.h>
setlocale((LC_ALL,"");
wprintf(L"%lc",L'●');
这里需要知道,平时所打印的字符'a' . '1 '是窄字符,而控制台的坐标是以窄字符为单位的,所以宽字符通常占了两个窄字符: |
Ⅱ 怎么隐藏光标
第一步:获取输出设备的句柄,既屏幕的句柄。句柄就是指针,类型为HANDLE,也可以理解成锅铲的手柄。 |
GetStdHandle
#include<windows.h>
HANDLE poutput=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO
GetConsoleCursorInfo
CONSOLE_CURSOR_INFO cursor;
GetConsoleCursorInfo(poutput,&cursor);
SetConsoleCursorInfo
cursor.bVisible = false;
SetConsoleCursorInfo(poutput,&cursor);
Ⅲ 怎么指定位置打印
HANDLE poutput=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition
COORD pos={0,0};
SetConsoleCursorPosition(poutput,pos);
Ⅳ 怎么获取按键信息
GetAsyncKeyState
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
3.从蛇身角度梳理游戏逻辑
三:实现
有了以上的框架与逻辑,相信聪明的小伙伴已经迫不及待了,赶紧动起手吧,VS启动。 |
Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Snake.h"
void game()
{
srand((unsigned int)time(NULL));
char ch = 0;
do
{
system("cls");
Snake snake = { 0 };
Psnake sp = &snake;
Game_Start(sp);
Game_Run(sp);
Game_End(sp);
Setpos(110, 23);
printf("是否再来一把?");
Setpos(110, 25);
printf("(Y/N):");
while (isspace(ch = getchar()));
while (getchar() != '\n');
} while (ch == 'Y' || ch == 'y');
Setpos(0, 46);
}
int main()
{
game();
return 0;
}
Snake.h
#pragma once
#include<stdio.h>
#include<Windows.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>
#include"vld.h"
#define WHITE FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE
#define YELLOW FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN
#define RED FOREGROUND_INTENSITY|FOREGROUND_RED
#define PURPLE FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE
#define BLUE FOREGROUND_INTENSITY|FOREGROUND_BLUE
#define CYAN FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE
#define GREEN FOREGROUND_INTENSITY|FOREGROUND_GREEN
#define SET_COLOR(x) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);
#define SnakeInitLen 5
#define SnodeInitPos_x 12
#define SnodeInitPos_y 10
#define WALL L'◎'
#define BODY L'◆'
#define FOOD L'★'
#define HEAD L'●'
#define INIT_MID_TIME 200
typedef enum State
{
Ok = 1,
Kill_Byself,
Kill_Wall,
End_Normal
}State;
typedef enum Dir
{
Up,
Down,
Left,
Right
}Dir;
typedef enum Speed
{
One = 1,
Two,
Three,
Four,
Five
}Speed;
typedef struct SnakeNode
{
wchar_t ch;
int x;
int y;
struct SnakeNode* next;
}SnakeNode, * Psnakenode;
typedef struct Snake
{
Psnakenode phead;
Dir dir;
Speed speed;
Psnakenode food;
int sleep_time;
int food_weight;
int grade;
State state;
}Snake, * Psnake;
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
void Setpos(int x, int y);
void game();
void Game_Start(Psnake sp);
void Welcom();
void Wall();
void Snake_Init(Psnake sp);
void Snake_Body(Psnake sp);
void Snake_Print(Psnake sp);
void Creat_Food(Psnake sp);
void grade_Info(Psnake sp);
void Game_Run(Psnake sp);
void Snake_New(Psnake sp);
void Snake_State(Psnake sp);
void Game_End(Psnake sp);
Snake.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Snake.h"
void Setpos(int x, int y)
{
HANDLE poutput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(poutput, pos);
}
void Welcom()
{
Setpos(63, 22);
printf("欢迎来到贪吃蛇小游戏!");
Setpos(64, 48);
system("pause");
system("cls");
Setpos(55, 21);
printf("↑,→,↓,←分别代表蛇移动的方向");
Setpos(55, 23);
printf("Space,Esc分别代表暂停/继续和退出");
Setpos(55, 25);
printf("Shift,Ctrl分别代表加速和减速");
Setpos(60, 48);
system("pause");
system("cls");
}
void Wall()
{
wchar_t ch = WALL;
int i = 0;
for (i = 0; i < 45; i++)
{
wprintf(L"%lc", ch);
}
Setpos(0, 44);
for (i = 0; i < 45; i++)
{
wprintf(L"%lc", ch);
}
Setpos(0, 1);
for (i = 0; i < 45 - 2; i++)
{
wprintf(L"%lc\n", ch);
}
for (i = 0; i < 45 - 2; i++)
{
Setpos((45 - 1) * 2, i + 1);
wprintf(L"%lc", ch);
}
}
void Snake_Body(Psnake sp)
{
int i = 0;
Psnakenode phead = NULL;
Psnakenode cur = NULL;
while (i < SnakeInitLen)
{
cur = (Psnakenode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
printf("Snake_Body malloc fail");
return;
}
if (phead == NULL)
{
phead = cur;
phead->x = SnodeInitPos_x;
phead->y = SnodeInitPos_y;
phead->next = NULL;
}
else
{
cur->x = phead->x + 2;
cur->y = phead->y;
cur->next = phead;
phead = cur;
}
cur->ch=BODY;
i++;
}
sp->phead = phead;
phead->ch = HEAD;
phead = cur = NULL;
}
void Snake_Print(Psnake sp)
{
Psnakenode cur = sp->phead->next;
SET_COLOR(CYAN);
Setpos(sp->phead->x, sp->phead->y);
wprintf(L"%lc", sp->phead->ch);
SET_COLOR(YELLOW);
while (cur)
{
Setpos(cur->x, cur->y);
wprintf(L"%lc", cur->ch);
cur = cur->next;
}
}
void Creat_Food(Psnake sp)
{
int x = 0;
int y = 0;
again:
do
{
x = rand() % 85 + 2;
y = rand() % 43 + 1;
} while (x % 2 != 0);
Psnakenode cur = sp->phead;
while (cur)
{
if (cur->x == x && cur->y == y)
goto again;
cur = cur->next;
}
Psnakenode food = (Psnakenode)malloc(sizeof(SnakeNode));
if (food == NULL)
{
perror("CreateFood malloc fail");
return;
}
food->x = x;
food->y = y;
food->next = NULL;
sp->food = food;
food->ch = FOOD;
Setpos(x, y);
wprintf(L"\033[31;5m%lc\033[0m\r", food->ch);
}
void Snake_Init(Psnake sp)
{
Snake_Body(sp);
sp->dir = Right;
Creat_Food(sp);
sp->food_weight = 10;
sp->grade = 0;
sp->speed = One;
sp->state = Ok;
sp->sleep_time = INIT_MID_TIME;
}
void grade_Info(Psnake sp)
{
Setpos(110, 10);
printf("速度:");
SET_COLOR(GREEN);
printf("%d级", sp->speed);
SET_COLOR(WHITE);
Setpos(110, 12);
printf("食物分数:");
SET_COLOR(GREEN);
printf("%-2d", sp->food_weight);
SET_COLOR(WHITE);
Setpos(110, 14);
printf("总分:");
SET_COLOR(GREEN);
printf("%-3d", sp->grade);
SET_COLOR(WHITE);
}
void Game_Start(Psnake sp)
{
setlocale(LC_ALL, "");
system("mode con cols=150 lines=50");
system("title 贪吃蛇");
HANDLE poutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorinfo;
GetConsoleCursorInfo(poutput, &cursorinfo);
cursorinfo.bVisible = false;
SetConsoleCursorInfo(poutput, &cursorinfo);
Welcom();
Wall();
Snake_Init(sp);
Setpos(110, 29);
printf("↑,→,↓,←分别代表蛇移动的方向");
Setpos(110, 34);
printf("Space,Esc分别代表暂停/继续和退出");
Setpos(110, 39);
printf("Shift,Ctrl分别代表加速和减速");
Setpos(140, 47);
printf("@.M_XY");
Snake_Print(sp);
grade_Info(sp);
}
int IF_IsFood(Psnake sp, Psnakenode new)
{
return (sp->food->x == new->x) && (sp->food->y == new->y);
}
void Snake_New(Psnake sp)
{
Psnakenode new = (Psnakenode)malloc(sizeof(SnakeNode));
if (sp->dir == Up)
{
new->x = sp->phead->x;
new->y = sp->phead->y - 1;
}
else if (sp->dir == Down)
{
new->x = sp->phead->x;
new->y = sp->phead->y + 1;
}
else if (sp->dir == Left)
{
new->x = sp->phead->x - 2;
new->y = sp->phead->y;
}
else if (sp->dir == Right)
{
new->x = sp->phead->x + 2;
new->y = sp->phead->y;
}
SET_COLOR(YELLOW);
new->ch = sp->phead->ch;
sp->phead->ch = sp->phead->next->ch;
Setpos(sp->phead->x, sp->phead->y);
printf(" ");
Setpos(sp->phead->x, sp->phead->y);
wprintf(L"%lc", sp->phead->ch);
SET_COLOR(CYAN);
Setpos(new->x, new->y);
wprintf(L"%lc", new->ch);
SET_COLOR(WHITE);
printf("\a\a\a");
int ret = IF_IsFood(sp, new);
if (ret)
{
sp->grade += sp->food_weight;
new->next = sp->phead;
sp->phead = new;
free(sp->food);
Creat_Food(sp);
new = NULL;
return;
}
Psnakenode cur = sp->phead;
while (cur->next->next)
{
cur = cur->next;
}
Setpos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
new->next = sp->phead;
sp->phead = new;
new = cur = NULL;
}
void Snake_State(Psnake sp)
{
Psnakenode cur = sp->phead->next;
while (cur)
{
if (sp->phead->x == cur->x && sp->phead->y == cur->y)
{
sp->state = Kill_Byself;
cur = NULL;
return;
}
cur = cur->next;
}
if (sp->phead->x == 0 ||
sp->phead->x == (45 - 1) * 2 ||
sp->phead->y == 0 ||
sp->phead->y == (45 - 1))
{
sp->state = Kill_Wall;
return;
}
}
void Game_Run(Psnake sp)
{
do
{
if (KEY_PRESS(VK_UP) && sp->dir != Down)
{
sp->dir = Up;
}
else if (KEY_PRESS(VK_DOWN) && sp->dir != Up)
{
sp->dir = Down;
}
else if (KEY_PRESS(VK_LEFT) && sp->dir != Right)
{
sp->dir = Left;
}
else if (KEY_PRESS(VK_RIGHT) && sp->dir != Left)
{
sp->dir = Right;
}
else if (KEY_PRESS(VK_SPACE))
{
Setpos(110, 25);
printf("游戏已暂停");
while (1)
{
if (KEY_PRESS(VK_SPACE))
break;
Sleep(100);
}
Setpos(110, 25);
printf(" ");
}
else if (KEY_PRESS(VK_ESCAPE))
{
sp->state = End_Normal;
}
else if (KEY_PRESS(VK_RSHIFT))
{
if (sp->speed < Five)
{
sp->speed += 1;
sp->food_weight += 3;
sp->sleep_time -= 30;
}
}
else if (KEY_PRESS(VK_RCONTROL))
{
if (sp->speed > One)
{
sp->speed -= 1;
sp->food_weight -= 3;
sp->sleep_time += 30;
}
}
Snake_New(sp);
Snake_State(sp);
grade_Info(sp);
Sleep(sp->sleep_time);
} while (sp->state == Ok);
}
void Game_End(Psnake sp)
{
Psnakenode cur = sp->phead;
while (cur)
{
Psnakenode t = cur;
cur = cur->next;
free(t);
}
free(sp->food);
sp->phead = sp->food = NULL;
SET_COLOR(BLUE);
Setpos(110, 18);
if (sp->state == Kill_Wall)
{
printf("HeHe你撞墙了!");
}
else if (sp->state == Kill_Byself)
{
printf("HiHi你咬到自己了!");
}
else if (sp->state == End_Normal)
{
printf("退出游戏中......\n");
}
Sleep(1000);
SET_COLOR(WHITE);
}