利用C/C++实现贪吃蛇

利用C/C++实现贪吃蛇

(注意:本文章仅供参考,第一次写博客还请多多指教。理解本文章需要easyx和c++等基础知识,并且需要了解贪吃蛇游戏机制

贪吃蛇机制介绍

相信绝大多数人都曾玩过或者了解过贪吃蛇这款经典的游戏。贪吃蛇顾名思义,就是让蛇尽可能的吃食物。玩家可通过方向键或自定义键来控制蛇头的方向,使它吃到地图出现的随机食物。蛇每吃到一个食物,自身便会增长。当蛇碰到地图的边界或是蛇碰到自身,蛇便会死亡,游戏便结束。

机制大概了解过后,我们将考虑如何实现这类游戏。

设计与分析

首先,我们分析游戏整体结构大概由四个部分构成——界面、地图、蛇、食物。
1、界面:界面能够方便玩家的使用,可让玩家自行选择游戏的开始或结束;通过界面,我们可以设定一些有趣的东西来增加玩家的游戏体验,例如:让玩家选择蛇的速度,以灵活调节游戏难度。
2、地图:地图能为蛇提供活动空间,同时也是蛇位置的坐标轴,方便定位。
3、蛇:蛇是游戏的灵魂。蛇可以定义如下属性:坐标、方向、速度、长度。蛇。蛇的行为有:移动、吃食物、死亡。
4、食物:食物在地图中随机分布,具有坐标(可以尝试去增加颜色属性、大小属性,本文章由于篇幅有限,暂不提供)。

思维导图如下
在这里插入图片描述

首先设计用户界面

//部分函数可以暂时不用考虑
void menu()
{
	initgraph(640, 480);
	int flag = 1;
	while (flag)
	{
		cleardevice();
		outtextxy(280, 180, "贪吃蛇游戏");
		outtextxy(280, 200, "按1开始游戏");
		outtextxy(280, 220, "按2结束游戏");
		//接受输入指令0、1
		char ch = _getch();
		switch (ch)
		{
		//开始
		case '1':
		{
			food F;
			Initfood(F);//食物初始化

			Snake S;
			hatch(S);//蛇初始化
			control_speed(S.speed);//蛇速度控制函数
			drawsnake(S, F);//开始绘制游戏面板
			break;
		}		
		//结束
		case '2':
			flag = 0;
			break;
		default :
			break;
		}
	}
	closegraph();
}

接下来可以通过结构体来定义蛇和食物

typedef struct Snake {
	int speed = 0;//速度
	pair<int, int> coor[MAXSIZE]={};//坐标(x, y)
	int dir;//方向
	int length;//长度
};

typedef struct food {
	pair<int, int> place[MAXSIZE] = {};//坐标
	int score;//分数
};

接下来将食物和蛇进行初始化

void Initfood(food& F)
{
	F.score = 0;
	srand(time(NULL));
	//将食物坐标随机设置
	for (int i = 0; i < 100; i++)
	{
		F.place[i].first = rand() % (640 - R) + R;
		F.place[i].second = rand() % (480 - R) + R;
	}
}

void hatch(Snake &S)
{
	S.length = 3;
	S.dir = 2;
	S.speed = 0;
	//先给出3节身体
	for (int i = 0; i < 3; i++)
	{
		S.coor[i].first = 40 - i * 10;
		S.coor[i].second = 30;
	}
}

再接着设计蛇速度控制函数

void control_speed(int &speed)
{
	cleardevice();
	outtextxy(280, 180, "请选择蛇的速度1-5");
	char ch = _getch();
	speed = ch - '0';
}

最关键的蛇运动机制设计来了,我将分段讲解

1、绘制蛇(蛇将有一系列正方体块组成)

for (int i = 0; i < S.length; i++)
		{
			int x = S.coor[i].first;
			int y = S.coor[i].second;
			//蛇头设为绿色,方便区分
			if (i == 0)
			{
				setfillcolor(GREEN);
				solidrectangle(x - R, y - R, x + R, y + R);
			}
				
			else
			{
				setcolor(WHITE);
				rectangle(x - R, y - R, x + R, y + R);
			}

2、绘制食物(食物由单个正方体组成)

int m = F.place[F.score].first;
int n = F.place[F.score].second;
setfillcolor(WHITE);
solidrectangle(m-R, n-R, m+R, n+R);

3、蛇的运动
像人的运动是通过脚向前迈步来制动整个躯干一样,蛇头同样驱动整个身体。也就是说我们只要控制蛇头的运动方向及前进,躯干便会跟着蛇头一起移动。蛇躯干的每个节点只需要继承上一个节点的位置即可。

void movesnake(Snake& S)
{
//继承坐标
	for (int i = S.length - 1; i > 0; i--)
	{
		S.coor[i].first = S.coor[i - 1].first;
		S.coor[i].second = S.coor[i - 1].second;
	}
	switch (S.dir)
	{
	case 1:
		S.coor[0].second-= 10;
		break;
	case 2:
		S.coor[0].first+=10;
		break;
	case 3:
		S.coor[0].second+=10;
		break;
	case 4:
		S.coor[0].first-=10;
		break;
	default:
		break;
	}
}

4、蛇运动控制
使用方向键改变蛇头的运动方向即可

void control_dir(int& DIR)
{
	char ch = _getch();
	switch (ch)
	{
	case 72:
	case 'W':
	case 'w':
	//这里用if主要是因为蛇运动的机制:当蛇在一某方向运动的途中,不能直接往反方向运动(这样会导致蛇身体重叠),只能通过以当前运动方向为正方向,进行左右移动来改变方向。
		if (DIR != 3)
			DIR = 1;
		break;
	case 77:
	case 'D':
	case 'd':
		if (DIR != 4)
			DIR = 2;
		break;
	case 80:
	case 'S':
	case 's':
		if (DIR != 1)
			DIR = 3;
		break;
	case 75:
	case 'A':
	case 'a':
		if (DIR != 2)
			DIR = 4;
		break;
	default:
		break;
	}
}

4、吃食物
吃完食物的结果:出现下一个食物、蛇变长

void Eating(Snake& S, food& F)
{
	if (S.coor[0].first >= F.place[F.score].first - R&& S.coor[0].first <= F.place[F.score].first + R
		&& S.coor[0].second >= F.place[F.score].second -R&& S.coor[0].second <= F.place[F.score].second+R)
	{
		F.score++;
		S.length++;
	}
}

5、判断是否死亡
死亡判断有两种结果:1、碰壁。2、自身形成回路(自己咬自己)

int isdead(Snake S)
{
	if (S.coor[0].first > 640-R || S.coor[0].first < R || S.coor[0].second>480-R || S.coor[0].second<=R)
		return 1;
	else
	{
		for (int i = 4; i < S.length; i++)
		{
			if (S.coor[0].first >= S.coor[i].first-R&& S.coor[0].first <= S.coor[i].first + R
				&& S.coor[0].second >= S.coor[i].second-R&& S.coor[0].second <= S.coor[i].second+R)
				return 1;
		}
		return 0;
	}
}

蛇的运动机制设计基本大功告成,可以自行在地图上进行修饰,像加入分数栏

void control_dir(int& DIR)
{
	char ch = _getch();
	switch (ch)
	{
	case 72:
	case 'W':
	case 'w':
		if (DIR != 3)
			DIR = 1;
		break;
	case 77:
	case 'D':
	case 'd':
		if (DIR != 4)
			DIR = 2;
		break;
	case 80:
	case 'S':
	case 's':
		if (DIR != 1)
			DIR = 3;
		break;
	case 75:
	case 'A':
	case 'a':
		if (DIR != 2)
			DIR = 4;
		break;
	default:
		break;
	}
}

void movesnake(Snake& S)
{
	for (int i = S.length - 1; i > 0; i--)
	{
		S.coor[i].first = S.coor[i - 1].first;
		S.coor[i].second = S.coor[i - 1].second;
	}
	switch (S.dir)
	{
	case 1:
		S.coor[0].second-= 10;
		break;
	case 2:
		S.coor[0].first+=10;
		break;
	case 3:
		S.coor[0].second+=10;
		break;
	case 4:
		S.coor[0].first-=10;
		break;
	default:
		break;
	}
	
}

int isdead(Snake S)
{
	if (S.coor[0].first > 640-R || S.coor[0].first < R || S.coor[0].second>480-R || S.coor[0].second<=R)
		return 1;
	else
	{
		for (int i = 4; i < S.length; i++)
		{
			if (S.coor[0].first >= S.coor[i].first-R&& S.coor[0].first <= S.coor[i].first + R
				&& S.coor[0].second >= S.coor[i].second-R&& S.coor[0].second <= S.coor[i].second+R)
				return 1;
		}
		return 0;
	}
}

void Eating(Snake& S, food& F)
{
	if (S.coor[0].first >= F.place[F.score].first - R&& S.coor[0].first <= F.place[F.score].first + R
		&& S.coor[0].second >= F.place[F.score].second -R&& S.coor[0].second <= F.place[F.score].second+R)
	{
		F.score++;
		S.length++;
	}
}

void drawsnake(Snake& S, food F)
{
	char out[3] = {};

	BeginBatchDraw();
	while (1)
	{
		cleardevice();
		for (int i = 0; i < S.length; i++)
		{
			int x = S.coor[i].first;
			int y = S.coor[i].second;
			if (i == 0)
			{
				setfillcolor(GREEN);
				solidrectangle(x - R, y - R, x + R, y + R);
			}
				
			else
			{
				setcolor(WHITE);
				rectangle(x - R, y - R, x + R, y + R);
			}
			
		}
		int m = F.place[F.score].first;
		int n = F.place[F.score].second;
		setfillcolor(WHITE);
		solidrectangle(m-R, n-R, m+R, n+R);

		sprintf_s(out, "%d", F.score * S.speed);
		setbkmode(0);
		outtextxy(570, 20, "得分");
		outtextxy(620, 20, out);

		FlushBatchDraw();
		Sleep(200-S.speed*30);

		while(_kbhit())
		{
			control_dir(S.dir);
		}

		movesnake(S);

		Eating(S, F);
		if (isdead(S))
			break;
	}
	cleardevice();
	sprintf_s(out, "%d", F.score * S.speed);
	outtextxy(280, 180, "得分");
	outtextxy(320, 180, out);
	outtextxy(280, 200, "按Enter键继续");

	FlushBatchDraw();
	getchar();
	EndBatchDraw();
}

完整代码如下

#include <iostream>
#include <graphics.h>
#include <conio.h>
#include <time.h>
using namespace std;

#define MAXSIZE 100
#define R 5
typedef struct Snake {
	int speed = 0;//速度
	pair<int, int> coor[MAXSIZE]={};
	int dir = 2;
	int length = 3;
};

typedef struct food {
	pair<int, int> place[MAXSIZE] = {};
	int score;
};

void Initfood(food& F)
{
	F.score = 0;
	srand(time(NULL));
	for (int i = 0; i < 100; i++)
	{
		F.place[i].first = rand() % (640 - R) + R;
		F.place[i].second = rand() % (480 - R) + R;
	}
}

void hatch(Snake &S)
{
	S.length = 3;
	S.dir = 2;
	S.speed = 0;
	for (int i = 0; i < 3; i++)
	{
		S.coor[i].first = 40 - i * 10;
		S.coor[i].second = 30;
	}
}

void control_speed(int &speed)
{
	cleardevice();
	outtextxy(280, 180, "请选择蛇的速度1-5");
	char ch = _getch();
	speed = ch - '0';
}

void control_dir(int& DIR)
{
	char ch = _getch();
	switch (ch)
	{
	case 72:
	case 'W':
	case 'w':
		if (DIR != 3)
			DIR = 1;
		break;
	case 77:
	case 'D':
	case 'd':
		if (DIR != 4)
			DIR = 2;
		break;
	case 80:
	case 'S':
	case 's':
		if (DIR != 1)
			DIR = 3;
		break;
	case 75:
	case 'A':
	case 'a':
		if (DIR != 2)
			DIR = 4;
		break;
	default:
		break;
	}
}

void movesnake(Snake& S)
{
	for (int i = S.length - 1; i > 0; i--)
	{
		S.coor[i].first = S.coor[i - 1].first;
		S.coor[i].second = S.coor[i - 1].second;
	}
	switch (S.dir)
	{
	case 1:
		S.coor[0].second-= 10;
		break;
	case 2:
		S.coor[0].first+=10;
		break;
	case 3:
		S.coor[0].second+=10;
		break;
	case 4:
		S.coor[0].first-=10;
		break;
	default:
		break;
	}
	
}

int isdead(Snake S)
{
	if (S.coor[0].first > 640-R || S.coor[0].first < R || S.coor[0].second>480-R || S.coor[0].second<=R)
		return 1;
	else
	{
		for (int i = 4; i < S.length; i++)
		{
			if (S.coor[0].first >= S.coor[i].first-R&& S.coor[0].first <= S.coor[i].first + R
				&& S.coor[0].second >= S.coor[i].second-R&& S.coor[0].second <= S.coor[i].second+R)
				return 1;
		}
		return 0;
	}
}

void Eating(Snake& S, food& F)
{
	if (S.coor[0].first >= F.place[F.score].first - R&& S.coor[0].first <= F.place[F.score].first + R
		&& S.coor[0].second >= F.place[F.score].second -R&& S.coor[0].second <= F.place[F.score].second+R)
	{
		F.score++;
		S.length++;
	}
}

void drawsnake(Snake& S, food F)
{
	char out[3] = {};

	BeginBatchDraw();
	while (1)
	{
		cleardevice();
		for (int i = 0; i < S.length; i++)
		{
			int x = S.coor[i].first;
			int y = S.coor[i].second;
			if (i == 0)
			{
				setfillcolor(GREEN);
				solidrectangle(x - R, y - R, x + R, y + R);
			}
				
			else
			{
				setcolor(WHITE);
				rectangle(x - R, y - R, x + R, y + R);
			}
			
		}
		int m = F.place[F.score].first;
		int n = F.place[F.score].second;
		setfillcolor(WHITE);
		solidrectangle(m-R, n-R, m+R, n+R);

		sprintf_s(out, "%d", F.score * S.speed);
		setbkmode(0);
		outtextxy(570, 20, "得分");
		outtextxy(620, 20, out);

		FlushBatchDraw();
		Sleep(200-S.speed*30);

		while(_kbhit())
		{
			control_dir(S.dir);
		}

		movesnake(S);

		Eating(S, F);
		if (isdead(S))
			break;
	}
	cleardevice();
	sprintf_s(out, "%d", F.score * S.speed);
	outtextxy(280, 180, "得分");
	outtextxy(320, 180, out);
	outtextxy(280, 200, "按Enter键继续");

	FlushBatchDraw();
	getchar();
	EndBatchDraw();
}

void menu()
{
	initgraph(640, 480);
	int flag = 1;
	while (flag)
	{
		cleardevice();
		outtextxy(280, 180, "贪吃蛇游戏");
		outtextxy(280, 200, "按1开始游戏");
		outtextxy(280, 220, "按2结束游戏");
		char ch = _getch();
		switch (ch)
		{
		case '1':
		{
			food F;
			Initfood(F);

			Snake S;
			hatch(S);
			control_speed(S.speed);
			drawsnake(S, F);
			break;
		}		
		case '2':
			flag = 0;
			break;
		default :
			break;
		}
	}
	closegraph();
}

int main(void)
{
	menu();
	return 0;
}

在这里插入图片描述

——本文章为原创,转载请注明出处——

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值