总看别人写贪吃蛇,自己也来写一个吧

贪吃蛇是一个流行度非常高的游戏,玩法十分简单,从最初的方块机上就有这个游戏了,后来出现的手机中也是加入了这个游戏,无聊的时候可以打发时间玩一会。这个游戏可谓是非常的简单,容易上手,所以直到现在,还能看到它的身影。大多数学习编程的同学在学习编程语言后,也都会想着写一个自己的游戏,可能大部分同学也是抱着这样的想法开始学习的编程,那么简单的贪吃蛇游戏就成了很好的练手项目,所以可以在网上看到很多各种编程语言的贪吃蛇游戏代码。

每当看到网上这些贪吃蛇代码的时候,就觉得也不是很难,逻辑也简单,看看就会了,所以也没有实际动手去编写一下(不知道有没有人和我一样)。可是每当有时间的时候,看一些练手项目的时候大概率会看到贪吃蛇这个游戏,然后就会看到各路大神编写的代码,有100多行完成的,有200多行完成的,还有14行就完成的,就感觉挺厉害的。这个时候我就在想,我自己写这个游戏的话,需要多少行代码能够完成呢?反正14行我是搞不定的,估计200行左右还是可能的。正好最近时间上比较闲,所以就动手来写一下,也不要总看不练。

代码是使用C++进行编写的,图形界面绘制选择的是EasyX。EasyX Graphics Library 是针对 Visual C++ 的免费绘图库,支持 VC6.0 ~ VC2022,简单易用,官网地址是:EasyX Graphics Library for C++,有兴趣的同学可以去看一下。至于需求逻辑方面就没有什么好说的了。那么接下来,直接上代码。

#include <graphics.h>
#include <time.h>
#include <conio.h>

#include <vector>
#include <array>

#define WIN_SIZE 600
#define BLOCK_SIZE 40
#define SPEED 100
#define LEN (WIN_SIZE / BLOCK_SIZE)

// 声明节点结构体,用于表示蛇身体和食物的坐标
struct Node
{
	int x;
	int y;
};

// 声明方向
enum Direction
{
	UP, DOWN, LEFT, RIGHT
};

// 声明数组值类型
enum BlockType
{
	// 0:空白,1:蛇身,2:食物
	NONE = 0, SNAKE, FOOD
};

// 绘制指定节点并设置所在二维数组
void drawNode(const Node& node, std::array<std::array<UINT8, LEN>, LEN>& arr,
	BlockType type = SNAKE)
{
	setfillcolor(RED);
	fillrectangle(node.x * BLOCK_SIZE, node.y * BLOCK_SIZE,
		node.x * BLOCK_SIZE + BLOCK_SIZE, node.y * BLOCK_SIZE + BLOCK_SIZE);
	arr[node.y][node.x] = type;
}

// 清除指定节点并清除二维数组对应数据
void clearNode(const Node& node, std::array<std::array<UINT8, LEN>, LEN>& arr)
{
	setfillcolor(WHITE);
	fillrectangle(node.x * BLOCK_SIZE, node.y * BLOCK_SIZE,
		node.x * BLOCK_SIZE + BLOCK_SIZE, node.y * BLOCK_SIZE + BLOCK_SIZE);
	arr[node.y][node.x] = NONE;
}

// 绘制界面方格
void drawBlock()
{
	setbkcolor(WHITE);
	setlinecolor(GREEN);
	cleardevice();

	for (int i = BLOCK_SIZE; i <= WIN_SIZE; i += BLOCK_SIZE)
	{
		line(i, 0, i, WIN_SIZE);
		line(0, i, WIN_SIZE, i);
	}
}

// 绘制蛇身并设置蛇身所在二维数组
void drawSnake(const std::vector<Node>& snake, std::array<std::array<UINT8, LEN>, LEN>& arr)
{
	for (Node node : snake)
	{
		drawNode(node, arr);
	}
}

// 随机生成食物
void randFood(std::array<std::array<UINT8, LEN>, LEN>& arr)
{
	int x, y;
	do
	{
		x = rand() % LEN;
		y = rand() % LEN;
	} while (arr[y][x] == SNAKE);
	drawNode({x, y}, arr, FOOD);
}

// 移动,如果碰到蛇身或墙壁则游戏结束
bool move(std::vector<Node>& snake, std::array<std::array<UINT8, LEN>, LEN>& arr,
	Direction& direction)
{
	Node first = snake.back();
	Node second = snake.at(snake.size() - 2);
	Node node = first;
	switch (direction)
	{
	case UP:
		node.y--;
		break;
	case DOWN:
		node.y++;
		break;
	case LEFT:
		node.x--;
		break;
	case RIGHT:
		node.x++;
		break;
	}
	// 判断是否可以移动,移动方向不能为当前方向的反方向
	if (!((node.x == second.x && (direction == LEFT || direction == RIGHT)) ||
		(node.y == second.y && (direction == UP || direction == DOWN))))
	{
		if (node.x < 0 || node.x >= LEN ||
			node.y < 0 || node.y >= LEN ||
			arr[node.y][node.x] == SNAKE)
		{
			// 装到墙壁或蛇身则游戏结束
			return false;
		}
		else if (arr[node.y][node.x] == FOOD)
		{
			arr[node.y][node.x] == SNAKE;
			snake.push_back(node);
			randFood(arr);
		}
		else
		{
			arr[node.y][node.x] == SNAKE;
			snake.push_back(node);
			clearNode(snake.front(), arr);
			snake.erase(snake.begin());
			drawNode(node, arr);
		}
	}

	return true;
}

// 游戏
void game()
{
	using namespace std;

	// 蛇身
	vector<Node> snake = { {0, 0}, {1, 0}, {2, 0} };
	// 二维数组,记录蛇身和食物是否在某位置上
	array<array<UINT8, LEN>, LEN> arr;
	for (auto& ar : arr) {
		memset(&ar, NONE, size(ar));
	}
	// 默认起始向右移动
	Direction direction = RIGHT;

	drawBlock();
	drawSnake(snake, arr);

	randFood(arr);

	// 定义消息变量
	ExMessage m;
	while (move(snake, arr, direction))
	{
		// 获取一条按键消息
		peekmessage(&m, EX_KEY);
		switch (m.vkcode)
		{
		case VK_UP:
			direction = direction == DOWN ? DOWN : UP;
			break;
		case VK_DOWN:
			direction = direction == UP ? UP : DOWN;
			break;
		case VK_LEFT:
			direction = direction == RIGHT ? RIGHT : LEFT;
			break;
		case VK_RIGHT:
			direction = direction == LEFT ? LEFT : RIGHT;
			break;
		}

		Sleep(150);
	}

	settextstyle(36, 0, _T("Consolas"));
	settextcolor(RED);
	outtextxy(100, 250, "GAME OVER,按s键开始游戏");
}

int main()
{
	// 创建绘图窗口
	initgraph(WIN_SIZE, WIN_SIZE);
	// 设置随机种子
	srand((unsigned)time(NULL));

	while (true)
	{
		game();
		while ('s' != _getch());
	}

	closegraph();
	return 0;
}

代码量和我开始预计的差不多,200行出头,程序应该还有可以优化的地方。这里贴出代码是为了给大家提供一个思路,有兴趣的也可以自己尝试着写一写这个贪吃蛇,看看用多少行代码可以完成,也是对自己编程能力的锻炼。最后,让我们看一下编写的这个贪吃蛇的运行效果。

欢迎关注我的公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值