目录
效果图

要使用的东西。
1.链表,2.结构体,3.系统操作。
创建界面
了解界面的生成和输入
先本地化环境用于在控制台输出中文setlocale(LC_ALL, "");要包含#include <locale.h>
要使用 system函数,要包括头文件windows.h。
c
void cj() {
setlocale(LC_ALL, ""); // 设置本地化环境
system("mode con cols=100 lines=40"); // 调整控制台窗口尺寸
system("pause"); // 防止窗口自动关闭
}
查看光标发现光标的长宽不匹配,调整cols和lines,来判断那个是长的。
发现调整cols后长变了所以cols是窗口的长。
调整后发现2个长等于一个宽,所以我们设置时要设置2的倍数长
设置我们的游戏界面为25*25,加上边框长乘2,54*27,再加上提示,36长。凑个整
100*30.

发现输出都从左上角开始,所以需要定位光标位置来输出,后续的内容也要运用光标的定位,所以我们要定义一个函数来表示光标定位。
c
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
// 获取控制台输出句柄
// 使用GetStdHandle(STD_OUTPUT_HANDLE)获取标准输出设备句柄
COORD pos = {x, y}; // 定义光标位置坐标(左上角为原点(0,0))
SetConsoleCursorPosition(houtput, pos); // 将光标定位到指定坐标
void dw(int x, int y) {
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = {x, y};
SetConsoleCursorPosition(houtput, pos);
}

构建欢迎界面,(简介版)
dw(40, 15);
wprintf(L"欢迎来到贪吃蛇游戏");//用wprintf来输出宽字符,L表示后面的是宽字符,如中文等。
dw(40, 16);
system("pause");

到下一个界面我们要清空当前界面。
system("cls");来清空界面
cpp
system("cls"); // 清屏操作
dw(40, 16); // 调用dw函数,参数为40和16
system("pause");// 暂停程序执行

下一步设置我们的提示
按W.S.A.D移动,F4加速,F5减速,space暂停,Esc退出。
定位要的位置输出数据,调整位置让它 看起居中。
cpp
dw(24, 15);
wprintf(L"按W.S.A.D移动,F4加速,F5减速,space暂停,Esc退出。");
dw(40, 16);
printf("加速能得到更高的分数");
dw(40, 17);
system("pause");

构建游戏界面
构建蛇的活动范围墙
发现高度不够扩高一点到40;下移墙,在顶上标识提示词表明游戏界面。

cpp
system("cls");
int i = 0, j = 0;
for (i = 3; i < 30; i++)
{
dw(0, i);
for (j = 0; j < 27; j++) {
if (i == 3 || i == 29 || j == 0 || j == 26)
wprintf(L"墙");
else
wprintf(L" ");
}
printf("\n");
}
dw(14, 1);
wprintf(L"游戏界面");
dw(0, 32);
system("pause");

右边加入提示词
cpp
dw(60, 6);
wprintf(L"时间:%.2f", sj);
dw(60, 10);
wprintf(L"不能撞墙,不能碰到自己");
dw(60, 11);
wprintf(L"按W.S.A.D移动\n");
dw(60, 12);
wprintf(L"F4加速,F5减速\n");
dw(60, 13);
wprintf(L"space暂停,Esc退出");
dw(60, 14);
wprintf(L"fish_xk制作");

发现函数太长了,分别封装成函数。
cpp
float sj = 0;
void dw(int x, int y) {
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
void help() {
dw(60, 6);
wprintf(L"时间:%.2f", sj);
dw(60, 10);
wprintf(L"不能撞墙,不能碰到自己");
dw(60, 11);
wprintf(L"按W.S.A.D移动\n");
dw(60, 12);
wprintf(L"F4加速,F5减速\n");
dw(60, 13);
wprintf(L"space暂停,Esc退出");
dw(60, 14);
wprintf(L"fish_xk制作");
system("pause");
}
void ks() {
setlocale(LC_ALL, "");
system("mode con cols=100 lines=40");
dw(40, 15);
wprintf(L"欢迎来到贪吃蛇游戏");
dw(40, 16);
system("pause");
system("cls");
}
void zj() {
dw(24, 15);
wprintf(L"按W.S.A.D移动,F4加速,F5减速,space暂停,Esc退出。");
dw(40, 16);
printf("加速能得到更高的分数");
dw(40, 17);
system("pause");
system("cls");
}
void yxjm() {
int i = 0, j = 0;
for (i = 3; i < 30; i++)
{
dw(0, i);
for (j = 0; j < 27; j++) {
if (i == 3 || i == 29 || j == 0 || j == 26)
wprintf(L"墙");
else
wprintf(L" ");
}
printf("\n");
}
dw(22, 1);
wprintf(L"游戏界面");
dw(0, 32);
}
void cj() {
ks();
zj();
yxjm();
help();
}
这样就完成了所有的界面的制作。
游戏本体
创建蛇
蛇由一个个节点组成,用链表来创建。重命名一下方便使用
cpp
typedef struct snakebody {
struct tcs* next;
int x;
int y;
}snake,st;
有蛇,有果子,有速度。用一个结构体来表示。
蛇可以用头节点来表示。
果可以看作一个在外的蛇身。
cpp
typedef struct tcs{
snake* head;
int v;
snake* guo;
}tcs;
还要有状态,方向,总分数,单个果子的权重。
cpp
typedef struct snakebody{
struct tcs* next;
int x;
int y;
}snake;
enum gamezt {
OK=1,
FAIL,
DIE_BYWALL,
DIE_BYSELF
};
enum snakefx {
UP = 1,
DOWN,
LEFT,
RIGHT
};
typedef struct tcs{
snake* head;//蛇
snake* guo;//果
int v;//速度
enum gamezt zt;//状态
enum snakefx fx;//方向
int food_weight;//单个分数
int score;//总分数
}tcs;
初始化一下蛇的身体。
尾插链表来初始蛇身
cpp
void push(snake**ps,int x,int y) {//插入蛇的节点
snake* news = (snake*)malloc(sizeof(snake));//开辟节点
news->next = NULL;
news->x = x;
news->y = y;
if (*ps == NULL) {
*ps = news;
}
else
{
snake* pos = *ps;
while (pos->next) {//链接到末尾
pos = pos->next;
}
pos->next = news;
}
}
snake* body = (snake*)malloc(sizeof(snake));
body = NULL;
int i;
for (i = 22; i < 27; i = i + 1) {
push(&body,22+(i-22)*2, 16);
}
ps->head = body;
print(ps);

cpp
void print(snake*ps) {//打印蛇
assert(ps);
snake* cur = ps;
while (cur) {//遍历链表
dw(cur->x, cur->y);
wprintf(L"蛇");
cur = cur->next;
}
}
tcs* cjsnake() {//返回蛇的头位置
tcs*ps = (tcs*)malloc(sizeof(tcs));
snake* body = (snake*)malloc(sizeof(snake));
body = NULL;
int i;
for (i = 22; i < 27; i = i + 1) {//循环添加5个节点
push(&body,22+(i-22)*2, 16);
}
ps->head = body;
print(ps->head);//打印开始的蛇身
cjguo(ps);
ps->v = 200;
ps->zt = OK;
ps->fx = LEFE;
ps->score = 0;
ps->food_weight = 10;
return ps;
}
下一步生成果子,要有随机数要包含#include<time.h>
设置随机数
cpp
srand(time(NULL))
void cjguo(tcs* ps) {
snake* news = (snake*)malloc(sizeof(snake));
news->next = NULL;
snake* cur = ps->head;
ag:
news->x = (rand()%25+1+1)*2;//第一个+1是保证1到25,后一个+1,是不能是墙
news->y = (rand() % 25 + 1+4 );
cur = ps->head;
while (cur){
if (cur->x == news->x && cur->y == news->y)//如果和蛇重叠重新生成。
goto ag;
cur = cur->next;
}
ps->guo = news;
dw(news->x, news->y);
wprintf(L"果");//打印果的位置
}

设置其他数据
cpp
ps->v = 200;//速度
ps->zt = OK;//状态
ps->fx = LEFT;//方向
ps->score = 0;//成绩
ps->food_weight = 10;//每个果子的分数
创建完毕
蛇的行走
cpp
while (cur->zt == OK) {
dw(60, 6);
wprintf(L"时间:%.2f秒", sj);
dw(60, 8);
printf("得分:%d", cur->score);
dw(60, 9);
printf("食物分值:%d", cur->food_weight);
snakedong(cur);//行动的函数
sj+=cur->v/1000.0;//行动时间
Sleep(cur->v);//行动间隔
}
设置循环判断游戏状态。可以再加入时间
设置程序的休息时间,来实现运动间隔。
要控制程序,需要读取输入信息,所以定义一个读取的宏
cpp
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1)?1:0)
读取一个虚拟键盘值,判断是否按过。
cpp
if (KEY_PRESS(0x53) && snake->fx != UP) {//识别W
snake->fx = DOWN;
}
else if (KEY_PRESS(0x57) && snake->fx != DOWN) {//识别S
snake->fx = UP;
}
else if (KEY_PRESS(0x41) && snake->fx != RIGHT) {//识别A
snake->fx = LEFE;
}
else if (KEY_PRESS(0x44) && snake->fx != LEFE) {//识别B
snake->fx = RIGHT;
}
else if (KEY_PRESS(VK_SPACE)) {//识别空格
while (1) {
Sleep(200);
if (KEY_PRESS(VK_SPACE)) {//循环停止游戏
break;
}
}
}
else if (KEY_PRESS(VK_ESCAPE)) {//识别ESC
snake->zt = END_NORMAL;
}
else if (KEY_PRESS(VK_F4)) {//识别F4
snake->v = snake->v - 10;
snake->food_weight = snake->food_weight + 5;
}
else if (KEY_PRESS(VK_F5)) {//识别F5
snake->v = snake->v + 10;
snake->food_weight = snake->food_weight - 5;
}
加入一个全局变量防止加速过头
根据蛇的方向来改变蛇的身体坐标
cpp
snake* toupush(snake*ps,int x,int y) {//插入头部,到达的节点
snake* news = (snake*)malloc(sizeof(snake));
news->next = ps;
news->x = x;
news->y = y;
dw(news->x, news->y);
wprintf(L"蛇");
ps = news;
return ps;
}
snake* pop(snake* ps) {//没吃到果子,删除多出来的节点
snake* tail = ps;
while (tail->next->next) {
tail = tail->next;
}
dw(tail->next->x, tail->next->y);
wprintf(L" ");
free(tail->next);
tail->next = NULL;
return ps;
}
if (snake->fx == UP) {//向上
snake->head=toupush(snake->head, snake->head->x, snake->head->y - 1);
}
else if (snake->fx == DOWN) {//向下
snake->head=toupush(snake->head, snake->head->x, snake->head->y + 1);
}
else if (snake->fx == LEFE) {//向左
snake->head=toupush(snake->head, snake->head->x-2, snake->head->y );
}
else if (snake->fx == RIGHT) {//向右
snake->head=toupush(snake->head, snake->head->x+2, snake->head->y );
}
if (snake->head->x != snake->guo->x || snake->head->y != snake->guo->y)//是否吃到果子
snake->head=pop(snake->head);//没吃到,删除尾节点
else {
free(snake->guo);//释放果子的节点
snake->guo = NULL;
cjguo(snake);//重新生成果子
snake->score += snake->food_weight;//加分
}
吃到果子就不删尾,分数加权重,没吃到就删尾。
编写失败条件。
cpp
if (snake->head->x == 0 || snake->head->x == 52 || snake->head->y == 4 || snake->head->y == 29)//到达墙体
snake->zt = DIE_BYWALL;//改变状态
st* self = snake->head->next;
while (self) {//遍历自身,看是否相交
if (snake->head->x == self->x && snake->head->y == self->y)
snake->zt = DIE_BYSELF;//如相交更新状态
self = self->next;
}
结束后输出分数
cpp
system("cls");
dw(40, 15);
if (cur->zt == DIE_BYSELF)
wprintf(L"失败于撞到自己");
if (cur->zt == DIE_BYWALL)
wprintf(L"失败于撞墙");
if (cur->zt == END_NORMAL)
wprintf(L"正常退出");
dw(40, 16);
wprintf(L"你坚持的时间是%.2f秒", sj);
dw(40, 17);
wprintf(L"你的得分是%d",cur->score);
隐藏光标
cpp
void gbyc() {
//获取标准输出设备的句柄;
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//定义一个光标结构体
CONSOLE_CURSOR_INFO cursor_info = { 0 };
//获取houtput句柄相关的控制台上的光标信息,存放cursor_info
GetConsoleCursorInfo(houtput, &cursor_info);
//设置吧光标的占比
cursor_info.bVisible = false;//光标消失
//设置和houtput句柄相关的控制台的光标信息
SetConsoleCursorInfo(houtput, &cursor_info);
}
void ck() {
//创建窗口
setlocale(LC_ALL, "");
COORD pos = { 0,0 };
system("mode con cols=100 lines=40");
system("title.贪吃蛇");
gbyc();
//打印开始界面
}
到这里已经全部完成了。