C++游戏项目——贪吃蛇(逐步编写代码)

#include <iostream>
//using namespace std;
bool GameOver;
const int width = 20;
const int heigth = 20;
int x, y, FruitX, FruitY, score;

void Setup()//初始化设置
{

}
void Draw()//绘画界面
{

}
void Input()//扫描输入
{

}
void Logic()//游戏逻辑
{

}

int main()
{
	Setup();
	while (!GameOver)
	{
		Draw();
		Input();
		Logic();
	}

}

Setup函数

void Setup()//初始化设置
{
    GameOver = false;
	x = width / 2;
	y = heigth / 2;
	FruitX = rand() % width;
	FruitY = rand() % heigth;
	score = 0;
}	

我们需要对蛇头与苹果位置进行初始化,游戏结束标志位置为0,分数置为0。

其中坐标位置如下(i,j),右下角位置即(width,heigth)

(0,0)(0,1)(0,2)(0,3)(0,4)

(1,0)()()()()

(2,0)()()()()

(3,0)()()()()

(4,0)()()()(4,4)

我们让蛇头初始化位置在界面正中间,苹果位置随机出现。

Draw函数

void Draw()//绘画界面
{
	system("cls");

	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
	for (int i = 0; i < heigth; i++)
	{
		for (int j = 0; j < width ; j++)
		{
			if (j == 0)cout << "#";

			if (i == y && j == x)cout << "O";
			else if(i == FruitY && j == FruitX)cout << "A";

			else cout << " ";
			if (j == (width - 1))cout << "#";
		}
		cout << endl;
	}
	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
}

首先我们搭建的墙壁是这样的

###########

#                 #

#                 #

#                 #

###########

为了更新每次画的界面system("cls");清空界面,熟悉matlab的同学应该熟悉这个命令。

第一个for循环打印了第一排的###########,并且进入下一行。

最后一个for循环打印了最后一排的###########,并且进入下一行。

中间两层for循环,对应着打印出的只有头尾两个#的每一行。

外层for循环循环height行,内部循环对每一行的位数判断头尾打印#,其余打印空格,并且进入下一行。内部的两段函数分别打印了蛇头O的位置与苹果A的位置,这两段代码的位置不要搞错,不要与墙壁的代码顺序搞乱,否则会被“ ”覆盖掉。

此时整体代码为

#include <iostream>
using namespace std;
bool GameOver;
const int width = 20;
const int heigth = 20;
int x, y, FruitX, FruitY, score;

void Setup()//初始化设置
{
	GameOver = false;

	x = width / 2;
	y = heigth / 2;
	FruitX = rand() % width;
	FruitY = rand() % heigth;
	score = 0;
}
void Draw()//绘画界面
{
	system("cls");

	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
	for (int i = 0; i < heigth; i++)
	{
		for (int j = 0; j < width ; j++)
		{
			if (j == 0)cout << "#";

			if (i == y && j == x)cout << "O";
			else if(i == FruitY && j == FruitX)cout << "A";

			else cout << " ";
			if (j == (width - 1))cout << "#";
		}
		cout << endl;
	}
	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
}
void Input()//扫描输入
{

}
void Logic()//游戏逻辑
{

}

int main()
{
	Setup();
	while (!GameOver)
	{
		Draw();
		Input();
		Logic();
	}

}

打印效果为

你会发现界面一直在频闪,正是因为主循环不断调用system("cls");刷新界面。刷新速度与CPU你的电脑性能有关,电脑越好闪烁速度越快。

此外刷新速度快意味着游戏中蛇的速度更快难度更大,为减小难度我们可以用<windows.h>中的Sleep();函数延时刷新时间。括号内数字越大延时越大,难度越小。

#include <iostream>
#include <windows.h>

using namespace std;
bool GameOver;
const int width = 20;
const int heigth = 20;
int x, y, FruitX, FruitY, score;

void Setup()//初始化设置
{
	GameOver = false;

	x = width / 2;
	y = heigth / 2;
	FruitX = rand() % width;
	FruitY = rand() % heigth;
	score = 0;
}
void Draw()//绘画界面
{
	system("cls");

	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
	for (int i = 0; i < heigth; i++)
	{
		for (int j = 0; j < width ; j++)
		{
			if (j == 0)cout << "#";

			if (i == y && j == x)cout << "O";
			else if(i == FruitY && j == FruitX)cout << "A";

			else cout << " ";
			if (j == (width - 1))cout << "#";
		}
		cout << endl;
	}
	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
}
void Input()//扫描输入
{

}
void Logic()//游戏逻辑
{

}

int main()
{
	Setup();
	while (!GameOver)
	{
		Draw();
		Input();
		Logic();
        Sleep(20);
	}

}

至此我们的游戏界面算是完事了

Input函数

我们需要对输入的按键进行反馈,引入<conio.h>文件(其他库函数需要自己下载)

enum Direction
{
	STOP,LEFT,RIGHT,UP,DOWN
};
Direction dir;

 创建枚举,对STOP,LEFT,RIGHT,UP,DOWN分别赋值01234(枚举看笔记10

void Input()//扫描输入
{
	if (_kbhit())
	{
		switch (_getch())
		{
		case 'a':
			dir = LEFT;
			break;
		case 's':
			dir = DOWN;
			break;
		case 'w':
			dir = UP;
			break;
		case 'd':
			dir = RIGHT;
			break;
		case 'x':
			GameOver = true ;
			break;
		}

	}

}

_kbhit()函数检测是否有字符输入,没有则返回空

_getch()函数获取输入字符

将获取的字符转为标记,在这个函数中我们还可以增加更多的功能如x结束游戏,还可以暂停游戏等等。

#include <iostream>
#include <conio.h>
#include <windows.h>
using namespace std;
bool GameOver;
const int width = 20;
const int heigth = 20;
int x, y, FruitX, FruitY, score;

enum Direction
{
	STOP, LEFT, RIGHT, UP, DOWN
};
Direction dir;

void Setup()//初始化设置
{
	GameOver = false;

	x = width / 2;
	y = heigth / 2;
	FruitX = rand() % width;
	FruitY = rand() % heigth;
	score = 0;
}
void Draw()//绘画界面
{
	system("cls");

	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
	for (int i = 0; i < heigth; i++)
	{
		for (int j = 0; j < width ; j++)
		{
			if (j == 0)cout << "#";

			if (i == y && j == x)cout << "O";
			else if (i == FruitY && j == FruitX)cout << "A";

			else cout << " ";
			if (j == (width - 1))cout << "#";
		}
		cout << endl;
	}
	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
}
void Input()//扫描输入
{
	if (_kbhit())
	{
		switch (_getch())
		{
		case 'a':
			dir = LEFT;
			break;
		case 's':
			dir = DOWN;
			break;
		case 'w':
			dir = UP;
			break;
		case 'd':
			dir = RIGHT;
			break;
		case 'x':
			GameOver = true;
			break;
		}

	}

}
void Logic()//游戏逻辑
{

}

int main()
{
	Setup();
	while (!GameOver)
	{
		Draw();
		Input();
		Logic();
		Sleep(20);
	}

}

Logic函数

我们已经有了STOP,LEFT,RIGHT,UP,DOWN标志就可以对蛇头位置进行操作了

void Logic()//游戏逻辑
{

	switch (dir)
	{
	case STOP:
		break;
	case LEFT:
		x--;
		break;
	case RIGHT:
		x++;
		break;
	case UP:
		y--;
		break;
	case DOWN:
		y++;
		break;
	default:
		break;
	}

	//if (x > width || y > heigth || x < 0 || y < 0)
		//GameOver = true;
	if (x > width)x = 0; else if (x < 0)x = width - 1;
	if (y > heigth)y = 0; else if (y < 0)y = heigth - 1;


	if (x == FruitX && y == FruitY)
	{
		score += 10;
		FruitX = rand() % width;
		FruitY = rand() % heigth;

	}
}

第一段语句对蛇头操作,switch语句就是对STOP,LEFT,RIGHT,UP,DOWN不同标志的操作

第二段是两种墙壁操作,注释的代码是蛇头撞墙就死,未注释代码是撞墙从另一测出来(测试时候容易死,所以用未注释做测试,根据情况选择。)

第三段是对苹果,蛇投触碰到苹果即加10分,苹果改变位置。

#include <iostream>
#include <conio.h>
#include <windows.h>
using namespace std;
bool GameOver;
const int width = 20;
const int heigth = 20;
int x, y, FruitX, FruitY, score;

enum Direction
{
	STOP, LEFT, RIGHT, UP, DOWN
};
Direction dir;

void Setup()//初始化设置
{
	GameOver = false;

	x = width / 2;
	y = heigth / 2;
	FruitX = rand() % width;
	FruitY = rand() % heigth;
	score = 0;
}
void Draw()//绘画界面
{
	system("cls");

	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
	for (int i = 0; i < heigth; i++)
	{
		for (int j = 0; j < width ; j++)
		{
			if (j == 0)cout << "#";

			if (i == y && j == x)cout << "O";
			else if (i == FruitY && j == FruitX)cout << "A";

			else cout << " ";
			if (j == (width - 1))cout << "#";
		}
		cout << endl;
	}
	for (int i = 0; i < width ; i++)
	{
		cout << "#";
	}
	cout << endl;
	cout << "SCORE:" << score << endl;
}
void Input()//扫描输入
{
	if (_kbhit())
	{
		switch (_getch())
		{
		case 'a':
			dir = LEFT;
			break;
		case 's':
			dir = DOWN;
			break;
		case 'w':
			dir = UP;
			break;
		case 'd':
			dir = RIGHT;
			break;
		case 'x':
			GameOver = true;
			break;
		}

	}

}
void Logic()//游戏逻辑
{

	switch (dir)
	{
	case STOP:
		break;
	case LEFT:
		x--;
		break;
	case RIGHT:
		x++;
		break;
	case UP:
		y--;
		break;
	case DOWN:
		y++;
		break;
	default:
		break;
	}

	//if (x > width || y > heigth || x < 0 || y < 0)
		//GameOver = true;
	if (x > width)x = 0; else if (x < 0)x = width - 1;
	if (y > heigth)y = 0; else if (y < 0)y = heigth - 1;


	if (x == FruitX && y == FruitY)
	{
		score += 10;
		FruitX = rand() % width;
		FruitY = rand() % heigth;

	}
}

int main()
{
	Setup();
	while (!GameOver)
	{
		Draw();
		Input();
		Logic();
		Sleep(20);
	}

}

 为了显示分数记得在Draw函数后加入输出流显示分数。

到此为止游戏的基本功能已经实现了还差最后的尾巴。

我们来分析一下有Ooooo的蛇

Ooooo按“s”向下运动变为

----------------------

oooo

O

 ----------------------

ooo

o

O

------------------------

oo

o

o

O

----------------------------再次向右按下

o

o

o

oO

-----------------------------

我们看到尾巴存在的位置都是蛇头经过的位置,我们可以用数组保存各个尾巴的位置。

#include <iostream>
#include <conio.h>
#include <windows.h>
using namespace std;
bool GameOver;
const int width = 20;
const int heigth = 20;
int x, y, FruitX, FruitY, score;

enum Direction
{
	STOP, LEFT, RIGHT, UP, DOWN
};
Direction dir;

int TailX[100], TailY[100];
int nTail;

两个数组存放尾巴的XY坐标,并记录长度nTail

回到Draw函数,尾巴的存在也是设定位置,然后被刷新达到动态的效果。

void Draw()//绘画界面
{
	system("cls");

	for (int i = 0; i < width+2; i++)
	{
		cout << "#";
	}
	cout << endl;
	for (int  i = 0; i < heigth; i++)
	{
		for (int j = 0; j < width+2; j++)
		{
			if (j == 0)cout << "#";
			
			if (i == y && j == x)cout << "O";
			else if(i == FruitY && j == FruitX)cout << "A";

			else
			{
				bool print=false;
				for (int k = 0; k < nTail; k++)
				{
					if (TailX[k] == j && TailY[k] == i)
					{
						cout << "o";
						print = true;
					}
				}
				if (!print)
					cout << " ";
			}
			if (j == (width - 1))cout << "#";
		}
		cout << endl;
	}
	for (int i = 0; i < width+2; i++)
	{
		cout << "#";
	}
	cout << endl;
	cout << "SCORE:" << score << endl;
}

设置标志位print将蛇头位置记录给数组并且循环打印,这个代码块的位置依旧在“ ”之上,否则被覆盖。

到Logic函数中考虑尾巴的游戏逻辑。

void Logic()//游戏逻辑
{
	int perX = TailX[0];
	int perY = TailY[0];
	int per2X, per2Y;
	TailX[0] = x;
	TailY[0] = y;
	for (int i = 1; i < nTail; i++)
	{
		per2X = TailX[i];
		per2Y = TailY[i];
		TailX[i] = perX;
		TailY[i] = perY;
		perX = per2X;
		perY = per2Y;
	}

	switch (dir)
	{
	case STOP:
		break;
	case LEFT:
		x--;
		break;
	case RIGHT:
		x++;
		break;
	case UP:
		y--;
		break;
	case DOWN:
		y++;
		break;
	default:
		break;
	}
	//if (x > width || y > heigth || x < 0 || y < 0)
		//GameOver = true;
	if (x > width)x = 0; else if (x < 0)x = width - 1;
	if (y > heigth)y = 0; else if (y < 0)y = heigth - 1;

	for (int i = 0; i < nTail; i++)
	{
		if (TailX[i] == x && TailY[i] == y)
		{
			GameOver = true;
		}
	}
	if (x == FruitX && y == FruitY)
	{
		score += 10;
		FruitX = rand() % width;
		FruitY = rand() % heigth;
		nTail++;
	}
}

第一部分我们将根据尾巴长度,进行循环将蛇头坐标向后传递

第二部分我们检测是否有数组与蛇头坐标一直,一致则结束游戏。

好了,你已经学会如何制作了,去玩吧!!!!你很棒!!!!

(小bug,有一定长度时,蛇头向左移时按下右键立即死亡,与运动方向相反方向都立刻死亡。)

你知道bug的原因是什么嘛?评论区留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

少杰是小白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值