c语言及数据结构实现简单贪吃蛇小游戏

目录

一·贪吃蛇简单介绍:

二·贪吃蛇的实现的开始准备:

2.1:欢迎界面的实现:

2.2地图的绘制:

2.3.1初始化蛇:

2.3.2初始化食物: 

三·贪吃蛇的运行操作:

3.1辅助信息的打印:

3.2蛇的下一步移动操作:

3.2.1判断玩家按键情况:

3.2.2下一步遇到食物:

3.2.3下一步不是食物:

3.2.4蛇移动撞到自己死亡以及撞墙死亡:

四·贪吃蛇的结束操作:

五·源代码展示:

六·通过简单实现贪吃蛇游戏的总结与体会:



124b4d63b6574cd4b5f327b6f1f8e539.gif

3bf9f6f3964f4d028ab427a71656938f.png

游戏效果演示:


 

贪吃蛇新效果:

上色效果

 

e756b87b3ba94cdcb6398d7890f29dc1.png

十进制颜色代码:void color(int c) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
}

普通效果:

pp

一·贪吃蛇简单介绍:

60ddd022f5dd4dcea1658c4428e5ce18.jpeg

想必大家都玩过贪吃蛇吧,这次我们来简单的实现一下它,由于受到知识盲区的限制,只好来粗略的实现一下以及介绍一下对它的具体操作等。

首先,我们设计的这个游戏只有一条蛇母体本身,然后就是在规定的地图里移动,然后在地图所在范围里面会随机的生成不同样式的食物,蛇可以通过吃它来增大分值,每当吃它一次,蛇自身长度就会增加一节,然后为了增加体验感,我们增添了f3与f4来分别作为加速键和减速键,比如我们按f3减速时候,我们难度会增加那么每个食物的分值也会对应增加,当我们按下f4减速时,难度降低,每个食物的分值也会减低。因此也不能无限减速,因此我们通过食物分值来限制了减速。

这便是我们对这个游戏简单思路介绍。

400582d0c22247faa5b92d85d4bfb403.png

二·贪吃蛇的实现的开始准备:

1.在这里我们简单介绍一下这里用到的一些win32api规定的一些操作设置:首先要用到的就是,由于我们需要在界面打印一些游戏内容故需要得到光标位置:

我们可以这么理解弹出来的界面就是一个坐标系:

1ec2ac32d318405ab4f76f89013c641d.jpeg

然而它的x轴是朝向右,但是y轴是朝向下的而且它的x的两个值所对应的距离差不多为一个y值所对应的距离,这些对下面我们布局有很大关系。

void setpos(short x, short y) {
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(houtput, pos);
}

接下来我们调用句柄的相关使用可以创建的这个函数可以直接确定光标所在的位置方便我们后续打印。下面我们在进行操作时候需要输入一定的特殊符号等,这时我们就需要把它改成适应本地

#include<locale.h>
setlocale(LC_ALL, "");

这里我们就可以后面用wprintf打印占两个位置的东西了。

下面我们还需要设置界面的名字以及弹窗的大小并且为了美观把光标位置隐藏于是就有了下面的操作:

system("mode con cols=100 lines=30");
system("title 贪吃蛇小游戏");
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);
CursorInfo.bVisible = false;
SetConsoleCursorInfo(houtput, &CursorInfo);

95eeb69cf88d4cb882711294a740c36f.png

下面我们步入操作的正题:

2.1:欢迎界面的实现:

fd5032d38fbe41d68adac3d56e3f1990.png

ec89472e3ccc4e3d8506a89a0cc96bb1.png

我们为了实现这个界面操作:我们配合光标定位打印以及pause,cls使用来实现暂停以及清屏操作来完成:

void welcomegame() {
	setpos(40, 14);
	wprintf(L"欢迎来到贪吃蛇\n");
	Sleep(1000);
	setpos(40, 15);
	system("pause");
	system("cls");
	setpos(26, 5);
	wprintf(L"用箭头控制移动方向,F3加速,F4减速\n");
	setpos(32, 6);
	wprintf(L"加速可以获得更高的分数\n");
	setpos(44, 20);
	system("pause");
	system("cls");

}

这样就完成了此操作(我们在实现以及测试的时候可能会出现直接终止的情况,那么我们可以使用getchar()来防止它直接终止)。

2.2地图的绘制:

在地图绘制之前我们提前声明一下我们对它定义的操作以及对一些图案的define等:

#define pos_x 24
#define pos_y 5
#define wall L'※'
#define food0 L'▶'
#define food1 L'★'
#define food2 L'☆'
#define food3 L'◆'
#define food4 L'○'
#define food5 L'◇'
#define food6 L'▲'
#define food7 L'□'
#define food8 L'▼'
#define food9 L'◀'
#define body L'◎'
//这里我们定义了不同的食物以及蛇的身体的图案,以及我们蛇头出现的位置pos_x, pos_y等

下面我们还定义了蛇身节点的结构体,以及贪吃蛇游戏里面总的需要的比如状态,分数等一些数据。如:

enum dirction {
	up=1,//这里我们为了后面蛇移动调用的Switch可以正确使用将up赋值为1.
	down,
	left,
	right,
};//这里我们定义了一个枚举类型来存放蛇移动的方向。
enum game_Status{
	ok,
	kill_by_wall,
	kill_by_self,
	end_normal,//这里是游戏的状态
};
typedef struct snakenode {
	int x;
	int y;
	struct snakenode* next;
}snakenode;//这里我们定义了一个蛇身节点的结构体并给它重命名为snakenode,存放坐标以及下一个节点的地址,这样我们用链表的知识完成了蛇的创建。

typedef struct snake {
	snakenode* psnake;//蛇头指针
	snakenode* pfood;//食物指针
	enum game_Status status;//游戏状态
	enum dirction dir;蛇移动的方向
		int foodscore;//每个食物的分数
		int all_score;//游戏的总分
		int sleep_time;//通过游戏的休眠时间来控制蛇移动的快慢

}snake;

下面我们就配合setpos函数定义光标的位置完成了地图打印:

 void print_map() {
	 int i = 0;
	 for (i = 0; i < 29; i++) {
		 wprintf(L"%lc", wall);
	 }
	 setpos(0, 26);
	 for (i = 0; i < 29; i++) {
		 wprintf(L"%lc", wall);
	 }
	 for (i = 1; i <= 25; i++) {
		 setpos(0, i);
		 wprintf(L"%lc", wall);

	 }
	 for (i = 1; i <=25; i++) {
		 setpos(56,i );
		 wprintf(L"%lc", wall);

	 }
	 
}

如:

dfdecac9a2754848af1486d583c0cf4e.png

2.3.1初始化蛇:

void initsnake(snake* ps) {
 int i = 0;
 snakenode* cur = NULL;
 for (i = 0; i < 5; i++) {
	 cur = (snakenode*)malloc(sizeof(snakenode));
	 if (cur == NULL) {
		 perror("malloc:snakenode");
		 return;
	 }
	 else {
		 cur->next = NULL;
		 cur->x = pos_x + 2 * i;
		 cur->y = pos_y;
		 if (ps->psnake==NULL) {
			 ps->psnake = cur;
		 }
		 else {
			 cur->next= ps->psnake;
			 ps->psnake = cur;
		 }
	 }
 }//这里我们完成蛇的创建是基于单链表,蛇初始长度为五个节点,因此我们开辟了五个snakenode的空间并把相关数据放进去再把它们连接起来。
 cur = ps->psnake;
 while (cur) {
	 setpos(cur->x, cur->y);
	 wprintf(L"%lc", body);
	 cur = cur->next;
 }//通过遍历整个链表完成蛇身的打印
 ps->dir = right;
 ps->status = ok;
 ps->foodscore = 10;
 ps->all_score = 0;
 ps->sleep_time = 200;
 //把蛇的初始化状态以及游戏相关初始化设置好
 }

这里我们对游戏以及蛇一些状态设置都要对游戏的一些相关数据操作故我们每次应给函数传递snake类型的自变量并用同类型ps指针接收。 

2.3.2初始化食物: 

初始化食物我们应注意:1.由于蛇能吃到食物故我们判断的就是蛇一节身子和食物的坐标能相同,故也就是食物的横坐标必须是2的倍数。2.其次就是食物出现的范围应该不仅在地图里面而且不能和蛇身子重合。

​
 void  set_food(snake* ps) {
	 int x = 0;
	 int y = 0;
 again:
	 do{ 
		 x = rand() % 53 + 2;
		 y = rand() % 25 + 1;
	} while (x % 2 != 0);//这里我们调用srand函数来随机生成坐标控制在x=0~54,y=1~25
	snakenode*cur= ps->psnake ;
	   while (cur) {
		if (x==cur->x  && y==cur->y ) {
			goto again;
		}
		cur = cur->next;//遍历蛇身节点防止食物与之重合
	}
	  snakenode* pd = NULL;
	 pd  = (snakenode*)malloc(sizeof(snakenode));
	 if (pd == NULL) {
		 perror("malloc:set_food");
		 return;
	 
	 }
	 else {
		 pd->next = NULL;
		 pd->x = x;
		 pd->y = y;
		 ps->pfood = pd;
		 setpos(x, y);

		 if (y % 10 == 0) {
			 wprintf(L"%lc", food0);
		 }
		 else if (y%10 == 1) {
			 wprintf(L"%lc", food1);
		 }
		 else if (y % 10== 2) {
			 wprintf(L"%lc", food2);
		 }
		 else if (y % 10 == 3) {
			 wprintf(L"%lc", food3);
		 }
		 else if (y % 10 == 4) {
			 wprintf(L"%lc", food4);
		 }
		 else if (y % 10 == 5) {
			 wprintf(L"%lc", food5);
		 }
		 else if (y % 10 == 6) {
			 wprintf(L"%lc", food6);
		 }
		 else if (y % 10 == 7) {
			 wprintf(L"%lc", food7);
		 }
		 else if (y % 10 == 8) {
			 wprintf(L"%lc", food8);
		 }
		 else if (y % 10 == 9) {
			 wprintf(L"%lc", food9);
		 }//根据生成的坐标y值不同来防止不同的食物
	 }
	
}

​

三·贪吃蛇的运行操作:

我们要明白蛇要想动起来我们可以看成一个循环把玩家的每一步操作对蛇所产生的影响串起来,通过计算休眠时间来及时调整,因此我们对整个循环可以从第一步开始考虑,也就是它的移动情况以及下一个碰到食物该怎么坐或者不是食物又该怎么坐,在下面我们来深度剖析一下:

3.1辅助信息的打印:

为了游戏界面美观以及起到提示作用故我们进行了一下操作:

setpos(64, 10);
printf("总分数:%d",ps->all_score);
setpos(64, 11);
printf("每个食物分数:%3d", ps->foodscore);
void printhelpinfo() {

	setpos(64, 14);
	wprintf(L"蛇不能穿墙哦");
	setpos(64, 15);
	wprintf(L"可以使用箭头键盘来控制蛇移动的方向哦");
	setpos(64, 16);
	wprintf(L"按fn+F3加速,fn+F4减速哟");
	setpos(64, 17);
	wprintf(L"按ESC可以快速退出游戏,空格暂停哦");
	setpos(90,19);
	wprintf(L"@羑悻");
	
}

效果如下:

94158f76b7d148eaac1a6f6b4de648bb.png

3.2蛇的下一步移动操作:

这里我们分情况列出蛇移动的第一步该怎么样,因此这里我们需要周到的想到众多情况才能防止游戏出现不必要的bug。这里蛇的每一步应该是状态为ok才能一直循环也就是蛇还活着。

	snakenode* smove = (snakenode*)malloc(sizeof(snakenode));
	if (smove == NULL) {
		perror("snakemove:malloc");
		return;
	}
	else {
		switch (ps->dir) {
		case up:
			smove->x = ps->psnake->x;
			smove->y= ps->psnake->y-1;
			break;

		case down:
			smove->x = ps->psnake->x;
			smove->y = ps->psnake->y +1;
			break;

		case right:
			smove->x = ps->psnake->x+2;
			smove->y = ps->psnake->y ;
			break;
		case left:
			smove->x = ps->psnake->x -2;
			smove->y = ps->psnake->y;
			break;
		}
//因为我们会判断出玩家按的键是什么来通过蛇头确定下一个坐标的位置
		if (nextisfood(smove,ps)) {
			eatfood(smove,ps);
		}
		else {
			noeatfood(smove,ps);
		}
		killbywall(ps);
		killbyself(ps);
		Sleep(ps->sleep_time);

		
		
	}
}

加减速的设置操作:

这里由于f3与f4来分别作为加速键和减速键,比如我们按f3减速时候,我们难度会增加那么每个食物的分值也会对应增加,当我们按下f4减速时,难度降低,每个食物的分值也会减低。因此也不能无限减速,因此我们通过食物分值来限制了减速,这样来适当增加游戏的乐趣。

​
else if (judgkeypush(VK_F3)) {
	if (ps->sleep_time > 80) {
		ps->sleep_time -= 30;
		ps->foodscore += 2;//这里我们每加速一次那么休眠时间就会减少,难度也会增大,那么我们设置的食物分数就让它增加两分。
	}
}
else if (judgkeypush(VK_F4)) {
	if (ps->foodscore > 2) {
		ps->sleep_time += 30;
		ps->foodscore -= 2;//这里我们每减速一次那么操作就会变得容易一些,因此我们让食物分值也减少2。
	}
}

​

3.2.1判断玩家按键情况:

为了能方便比较玩家按的哪个键因此我们创建了一个宏来专门判断:

#define judgkeypush(vk) ( (GetAsyncKeyState(vk)&1)?1:0)//我们这里又调用了系统自己的函数,作用:如果你按了某个键,他有规定的数值,当传给GetAsyncKeyState这个函数,按了就返回的值二进制末位是1,否则是0.

//这里我们在判断按键的时候比如按了↑就不能按↓类似操作完成对它的控制
if (judgkeypush(VK_UP) && ps->dir != down) {
	ps->dir=up;
}

else if(judgkeypush(VK_DOWN) && ps->dir != up) {
	ps->dir = down;
}
else if  (judgkeypush(VK_LEFT) && ps->dir != right) {
	ps->dir = left;
}
else if (judgkeypush(VK_RIGHT) && ps->dir != left) {
	ps->dir = right;
}
else if (judgkeypush(VK_的SPACE)) {
	snake_pause();//这里掉用的函数是通过死循环的休眠来满足暂停的,如果想暂停只需要判断空格是否被按过来break循环
//
//void snake_pause() {
//	while (1) {
//		Sleep(200);
//		if (judgkeypush(VK_SPACE)) {
//			break;
//		}
//	}
//}



}
else if (judgkeypush(VK_ESCAPE)) {
	ps->status = end_normal;
}
else if (judgkeypush(VK_F3)) {
	if (ps->sleep_time > 80) {
		ps->sleep_time -= 30;
		ps->foodscore += 2;
	}
}
else if (judgkeypush(VK_F4)) {
	if (ps->foodscore > 2) {
		ps->sleep_time += 30;
		ps->foodscore -= 2;
	}
}

3.2.2下一步遇到食物:

int nextisfood(snakenode*p, snake* ps) {
	if (ps->pfood->x == p->x && ps->pfood->y == p->y) {
		return 1;
	}
	else {
		return 0;
	}
 }//这里我们需要比较生成的食物坐标和蛇下一步移动的坐标来判断是否相同
void eatfood(snakenode* p, snake* ps) {
	ps->pfood->next = ps->psnake;
	ps->psnake = ps->pfood;
	free(p);
	p = NULL;
	snakenode* cur = ps->psnake;
	while (cur) {
		setpos(cur->x, cur->y);
		wprintf(L"%lc", body);
		cur = cur->next;
	}
	ps->all_score += ps->foodscore;
	set_food(ps);
}//如果是食物的话我们蛇就要把它吃掉并且延长蛇自身一个节点长度那么我们只需把食物这个地方的节点跟蛇头连接起来再把它当做新的蛇头再次打印蛇身即可,并且吃了它后我们分数也要做相应的改变,并重新生成新的食物

3.2.3下一步不是食物:

void noeatfood(snakenode* p, snake* ps) {
	p->next = ps->psnake;
	ps->psnake = p;
	snakenode* cur = ps->psnake;
	while (cur->next->next!=NULL) {
		setpos(cur->x, cur->y);
		wprintf(L"%lc", body);
		cur = cur->next;
	}
	setpos(cur->x, cur->y);
	wprintf(L"%lc", body);
	setpos(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;
//如果我们走的下一步不是食物的话我们蛇的自身长度就应该不变,因此我们还可以照样把食物节点指针指向蛇头,然后让其成为新的蛇头,完成连接再次打印,这时我们就要把蛇尾去掉一个节点就是把它释放掉让新的蛇尾节点指针为NULL,把我们之前在蛇尾打印的蛇身让其打印空格,完成地图的恢复。

}

3.2.4蛇移动撞到自己死亡以及撞墙死亡:

这里蛇在运行起来的死亡分为两种,一个是撞到墙再一个就是撞到自己那么我们根据这两种情况来实现对代码的编辑:

void killbywall(snake* ps) {
	if (ps->psnake->x == 0 || ps->psnake->x == 56 || ps->psnake->y == 0 || ps->psnake->y == 26) {
		ps->status = kill_by_wall;
	}
 }//蛇如果撞到墙也就是蛇头坐标不能和边界坐标重合
void killbyself(snake* ps) {
	snakenode* cur = ps->psnake->next;
	while (cur) {
		if (cur->x == ps->psnake->x && cur->y == ps->psnake->y) {
			ps->status = kill_by_self;
			break;
		}//同理这里也就是不能和自身除了蛇头之外节点坐标重合。
		cur = cur->next;
	}
 }

下面我们来看一下相关效果:

撞墙:

efc5b01dd67b4ebda1f373a44dd9b850.png

撞到自己:

86cf89eb3c4148feb93a0f5c3a6ae394.png

四·贪吃蛇的结束操作:

由于我们设置了蛇每次运动的状态放在了枚举类型变量里面,因此我们可以根据蛇的情况来打印相关的信息提示等,然后游戏结束方便销毁创建的空间等。

void endgame(snake* ps) {
	setpos(24, 12);
	switch (ps->status) {
	case kill_by_wall:
		wprintf(L"你撞到墙死亡了!!!");
		break;
	case kill_by_self:
		wprintf(L"你撞到自己了!!!");
		break;
	case end_normal:
		wprintf(L"%ls",L"你主动退出游戏了!!!");
		break;
			
	}
	snakenode* cur = ps->psnake;
	snakenode* del = NULL;
	while (cur) {
		del = cur;
		cur = cur->next;
		free(del);//销毁malloc开辟空间
		
	}
}

然后为了游戏及结束后玩家还能接着玩这里我们调用了写游戏经常会用的do while来完成:

do {
	test();
	Sleep(1000);
	system("cls");
	setpos(20, 15);
	printf("是否需要再来一局???(y/n)");
	 ch = getchar();
	 while (getchar() != '\n');
} while (ch == 'y' || ch == 'Y');

测试函数:

​
void test() {
	snake bs = { 0 };
	game_start(&bs);
	game_run(&bs);
	endgame(&bs);

​}

游戏所用到的函数声明:

void game_start(snake*ps);//开始
void setpos(short x, short y);
void welcomegame();
 void print_map();
 void initsnake(snake* ps);
 void  set_food(snake* ps);
 void game_run(snake* ps);//运行
 void snake_pause();
 void printhelpinfo();
 int nextisfood(snakenode* p, snake* ps);
 void eatfood(snakenode* p, snake* ps);
 void noeatfood(snakenode* p, snake* ps);
 void killbywall(snake* ps);
 void killbyself(snake* ps);
 void endgame(snake* ps);//结束

这里我们的游戏相关介绍与操作就完成的差不多了。

效果图:

2c06d410137345da820833e958ed1dcd.png

五·源代码展示:

snake.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<stdbool.h>
#include<time.h>
#define pos_x 24
#define pos_y 5
#define wall L'※'
#define food0 L'▶'
#define food1 L'★'
#define food2 L'☆'
#define food3 L'◆'
#define food4 L'○'
#define food5 L'◇'
#define food6 L'▲'
#define food7 L'□'
#define food8 L'▼'
#define food9 L'◀'
#define body L'◎'


enum dirction {
	up=1,
	down,
	left,
	right,
};
enum game_Status{
	ok,
	kill_by_wall,
	kill_by_self,
	end_normal,
};
typedef struct snakenode {
	int x;
	int y;
	struct snakenode* next;
}snakenode;

typedef struct snake {
	snakenode* psnake;
	snakenode* pfood;
	enum game_Status status;
	enum dirction dir;
		int foodscore;
		int all_score;
		int sleep_time;

}snake;
void game_start(snake*ps);//开始
void setpos(short x, short y);
void welcomegame();
 void print_map();
 void initsnake(snake* ps);
 void  set_food(snake* ps);
 void game_run(snake* ps);//运行
 void snake_pause();
 void printhelpinfo();
 int nextisfood(snakenode* p, snake* ps);
 void eatfood(snakenode* p, snake* ps);
 void noeatfood(snakenode* p, snake* ps);
 void killbywall(snake* ps);
 void killbyself(snake* ps);
 void endgame(snake* ps);//结束

snake.c

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"

void setpos(short x, short y) {
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(houtput, pos);
}
void welcomegame() {
	setpos(40, 14);
	wprintf(L"欢迎来到贪吃蛇\n");
	Sleep(1000);
	setpos(40, 15);
	system("pause");
	system("cls");
	setpos(26, 5);
	wprintf(L"用箭头控制移动方向,F3加速,F4减速\n");
	setpos(32, 6);
	wprintf(L"加速可以获得更高的分数\n");
	setpos(44, 20);
	system("pause");
	system("cls");

}
 void print_map() {
	 int i = 0;
	 for (i = 0; i < 29; i++) {
		 wprintf(L"%lc", wall);
	 }
	 setpos(0, 26);
	 for (i = 0; i < 29; i++) {
		 wprintf(L"%lc", wall);
	 }
	 for (i = 1; i <= 25; i++) {
		 setpos(0, i);
		 wprintf(L"%lc", wall);

	 }
	 for (i = 1; i <=25; i++) {
		 setpos(56,i );
		 wprintf(L"%lc", wall);

	 }
	 
}
 void initsnake(snake* ps) {
	 int i = 0;
	 snakenode* cur = NULL;
	 for (i = 0; i < 5; i++) {
		 cur = (snakenode*)malloc(sizeof(snakenode));
		 if (cur == NULL) {
			 perror("malloc:snakenode");
			 return;
		 }
		 else {
			 cur->next = NULL;
			 cur->x = pos_x + 2 * i;
			 cur->y = pos_y;
			 if (ps->psnake==NULL) {
				 ps->psnake = cur;
			 }
			 else {
				 cur->next= ps->psnake;
				 ps->psnake = cur;
			 }
		 }
	 }
	 cur = ps->psnake;
	 while (cur) {
		 setpos(cur->x, cur->y);
		 wprintf(L"%lc", body);
		 cur = cur->next;
	 }
	 ps->dir = right;
	 ps->status = ok;
	 ps->foodscore = 10;
	 ps->all_score = 0;
	 ps->sleep_time = 200;
	 
  }

 void  set_food(snake* ps) {
	 int x = 0;
	 int y = 0;
 again:
	 do{ 
		 x = rand() % 53 + 2;
		 y = rand() % 25 + 1;
	} while (x % 2 != 0);
	snakenode*cur= ps->psnake ;
	   while (cur) {
		if (x==cur->x  && y==cur->y ) {
			goto again;
		}
		cur = cur->next;
	}
	  snakenode* pd = NULL;
	 pd  = (snakenode*)malloc(sizeof(snakenode));
	 if (pd == NULL) {
		 perror("malloc:set_food");
		 return;
	 
	 }
	 else {
		 pd->next = NULL;
		 pd->x = x;
		 pd->y = y;
		 ps->pfood = pd;
		 setpos(x, y);

		 if (y % 10 == 0) {
			 wprintf(L"%lc", food0);
		 }
		 else if (y%10 == 1) {
			 wprintf(L"%lc", food1);
		 }
		 else if (y % 10== 2) {
			 wprintf(L"%lc", food2);
		 }
		 else if (y % 10 == 3) {
			 wprintf(L"%lc", food3);
		 }
		 else if (y % 10 == 4) {
			 wprintf(L"%lc", food4);
		 }
		 else if (y % 10 == 5) {
			 wprintf(L"%lc", food5);
		 }
		 else if (y % 10 == 6) {
			 wprintf(L"%lc", food6);
		 }
		 else if (y % 10 == 7) {
			 wprintf(L"%lc", food7);
		 }
		 else if (y % 10 == 8) {
			 wprintf(L"%lc", food8);
		 }
		 else if (y % 10 == 9) {
			 wprintf(L"%lc", food9);
		 }
	 }
	
}


void game_start(snake*ps) {
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇小游戏");
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(houtput, &CursorInfo);
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(houtput, &CursorInfo);
	welcomegame();
	print_map();
	initsnake(ps);
	set_food(ps);
}
void printhelpinfo() {

	setpos(64, 14);
	wprintf(L"蛇不能穿墙哦");
	setpos(64, 15);
	wprintf(L"可以使用箭头键盘来控制蛇移动的方向哦");
	setpos(64, 16);
	wprintf(L"按fn+F3加速,fn+F4减速哟");
	setpos(64, 17);
	wprintf(L"按ESC可以快速退出游戏,空格暂停哦");
	setpos(90,19);
	wprintf(L"@羑悻");
	
}
#define judgkeypush(vk) ( (GetAsyncKeyState(vk)&1)?1:0)
void snake_pause() {
	while (1) {
		Sleep(200);
		if (judgkeypush(VK_SPACE)) {
			break;
		}
	}
}
int nextisfood(snakenode*p, snake* ps) {
	if (ps->pfood->x == p->x && ps->pfood->y == p->y) {
		return 1;
	}
	else {
		return 0;
	}
 }
void eatfood(snakenode* p, snake* ps) {
	ps->pfood->next = ps->psnake;
	ps->psnake = ps->pfood;
	free(p);
	p = NULL;
	snakenode* cur = ps->psnake;
	while (cur) {
		setpos(cur->x, cur->y);
		wprintf(L"%lc", body);
		cur = cur->next;
	}
	ps->all_score += ps->foodscore;
	set_food(ps);
}
void noeatfood(snakenode* p, snake* ps) {
	p->next = ps->psnake;
	ps->psnake = p;
	snakenode* cur = ps->psnake;
	while (cur->next->next!=NULL) {
		setpos(cur->x, cur->y);
		wprintf(L"%lc", body);
		cur = cur->next;
	}
	setpos(cur->x, cur->y);
	wprintf(L"%lc", body);
	setpos(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;


}
void killbywall(snake* ps) {
	if (ps->psnake->x == 0 || ps->psnake->x == 56 || ps->psnake->y == 0 || ps->psnake->y == 26) {
		ps->status = kill_by_wall;
	}
 }
void killbyself(snake* ps) {
	snakenode* cur = ps->psnake->next;
	while (cur) {
		if (cur->x == ps->psnake->x && cur->y == ps->psnake->y) {
			ps->status = kill_by_self;
			break;
		}
		cur = cur->next;
	}
 }

void snakemove(snake* ps) {
	snakenode* smove = (snakenode*)malloc(sizeof(snakenode));
	if (smove == NULL) {
		perror("snakemove:malloc");
		return;
	}
	else {
		switch (ps->dir) {
		case up:
			smove->x = ps->psnake->x;
			smove->y= ps->psnake->y-1;
			break;

		case down:
			smove->x = ps->psnake->x;
			smove->y = ps->psnake->y +1;
			break;

		case right:
			smove->x = ps->psnake->x+2;
			smove->y = ps->psnake->y ;
			break;
		case left:
			smove->x = ps->psnake->x -2;
			smove->y = ps->psnake->y;
			break;
		}
		if (nextisfood(smove,ps)) {
			eatfood(smove,ps);
		}
		else {
			noeatfood(smove,ps);
		}
		killbywall(ps);
		killbyself(ps);
		Sleep(ps->sleep_time);

		
		
	}
}
void game_run(snake* ps) {
	printhelpinfo();
	do {
		setpos(64, 10);
		printf("总分数:%d",ps->all_score);
		setpos(64, 11);
		printf("每个食物分数:%3d", ps->foodscore);
		if (judgkeypush(VK_UP) && ps->dir != down) {
			ps->dir=up;
		}

		else if(judgkeypush(VK_DOWN) && ps->dir != up) {
			ps->dir = down;
		}
		else if  (judgkeypush(VK_LEFT) && ps->dir != right) {
			ps->dir = left;
		}
		else if (judgkeypush(VK_RIGHT) && ps->dir != left) {
			ps->dir = right;
		}
		else if (judgkeypush(VK_SPACE)) {
			snake_pause();
		}
		else if (judgkeypush(VK_ESCAPE)) {
			ps->status = end_normal;
		}
		else if (judgkeypush(VK_F3)) {
			if (ps->sleep_time > 80) {
				ps->sleep_time -= 30;
				ps->foodscore += 2;
			}
		}
		else if (judgkeypush(VK_F4)) {
			if (ps->foodscore > 2) {
				ps->sleep_time += 30;
				ps->foodscore -= 2;
			}
		}
		snakemove(ps);


	} while (ps->status==ok);
}
void endgame(snake* ps) {
	setpos(24, 12);
	switch (ps->status) {
	case kill_by_wall:
		wprintf(L"你撞到墙死亡了!!!");
		break;
	case kill_by_self:
		wprintf(L"你撞到自己了!!!");
		break;
	case end_normal:
		wprintf(L"%ls",L"你主动退出游戏了!!!");
		break;
			
	}
	snakenode* cur = ps->psnake;
	snakenode* del = NULL;
	while (cur) {
		del = cur;
		cur = cur->next;
		free(del);
		
	}
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"
#include<locale.h>
void test() {
	snake bs = { 0 };
	game_start(&bs);
	game_run(&bs);
	endgame(&bs);
}
int main() {

	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	/*test();
	setpos(20, 15);
	printf("是否需要再来一局??(y/n)");*/
	char ch = 0;
	do {
		test();
		Sleep(1000);
		system("cls");
		setpos(20, 15);
		printf("是否需要再来一局???(y/n)");
		 ch = getchar();
		 while (getchar() != '\n');
	} while (ch == 'y' || ch == 'Y');

	/*getchar();*/

	/*wprintf(L"%ls\n", L"我");
	printf("%d%d", 4,5);*/
	return 0;
}

六·通过简单实现贪吃蛇游戏的总结与体会:

通过此次对这个的构造以及代码的编写,让我感受到了一个看似很简单的一个小游戏却有很复杂的逻辑以及实现操作,当完成它时候首先我们要构思,想想自己就是玩家每一步对应会遇到什么情况该如何用代码去把它实现,不放过每一处细节的设置以防止出现bug,当然在写这个游戏时候避免不了bug的出现,出现不可怕,可怕的是你写了好几百行代码突然运行调试的时候出现bug而且要从这几百行代码里面去一一安排断点去调试,监视这就是一件很麻烦的事情,当然我也是遇到了找出的错误回事函数名字的一个字母写错了,然而却浪费了很长时间,因此我们在以后写的时候一定要仔细然后每写一点就测试一下,养成好习惯以免不必要的麻烦发生。然而,当我看到最后效果的时候,让我想我了那些断断续续的调试,思考,改调没有白费,为的就是将它呈现除了,回想起从hello world 到现在确实看到了进步,每一次挑战都是成长的阶梯,每一次失败都是成长的垫脚石,无论前路多么曲折,我都会保持对知识的那份渴望,对技术的那份钻研。不断超越前一次的自我,攀登最高的山峰,翻山越岭,只为遇见更加优秀的自己。

53356df689644f68bdf3d5a48ca98969.gif

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羑悻的小杀马特.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值