贪吃蛇(基于 EasyX 图形库)

目录

游戏开发基本框架

一、游戏初始化

二、显示游戏画面

三、 与输入无关的更新

3.1 - 小蛇移动

3.2 - 控制小蛇的移动速度

四、 与输入有关的更新

五、源代码



游戏开发基本框架

int main()
{
	GameInit();  // 游戏初始化
	while (1)
	{
		Show();  // 显示游戏画面
		UpdateWithoutInput();  // 和输入无关的更新
		UpdateWithInput();  // 和输入有关的更新
	}
	return 0;
}


一、游戏初始化

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

#define ROW 30
#define COL 40
#define BLOCK_SIZE 20

int board[ROW][COL] = { 0 };  // 创建一个二维数组用来存储游戏画面上每个格子的信息
char move_direction;  // 小蛇的移动方向
int food_i, food_j;  // 食物的位置
int is_failure;  // 判断游戏是否失败

void GameInit()
{
	srand((unsigned int)time(NULL));  // 设置随机数种子
	initgraph(COL * BLOCK_SIZE, ROW * BLOCK_SIZE);  // 初始化绘画窗口
	setlinecolor(RGB(200, 200, 200));  // 设置每个格子边框的画线颜色
	int i = 0;
	int j = 0;
	// 1. 当游戏重新开始时,需要将每个格子的信息重新初始化为 0
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] = 0;
		}
	}
	// 2. 分别生成蛇头和蛇身
	board[ROW / 2][COL / 2] = 1;  // 1 表示蛇头
	for (i = 1; i <= 4; i++)
	{
		board[ROW / 2][COL / 2 - i] = i + 1;  // 蛇头后的蛇身依次为 2、3、4、5 等正整数
	}
	// 3. 对几个全局变量进行初始化
	move_direction = 'D';  // 向右移动
	food_i = rand() % ROW;
	food_j = rand() % COL;
	is_failure = 0;
	// 4. 开始批量绘图
	BeginBatchDraw();
}

1. 每个格子的边框颜色:

2. 0 表示空,即绘制灰色的方格;1 表示蛇头,蛇头后的蛇身依次为 2、3、4、5 等正整数,即绘制彩色的方格:  


二、显示游戏画面

void Show()
{
	int i = 0;
	int j = 0;
	// 1. 显示
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] > 0)
			{
				setfillcolor(HSVtoRGB(board[i][j] * 10, 1, 1));  // 绘制彩色方格
			}
			else
			{
				setfillcolor(RGB(150, 150, 150));  // 绘制灰色方格
			}
			fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
		}
	}
	setfillcolor(RGB(0, 255, 0));  // 绘制绿色食物
	fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
	// 2. 判断是否失败
	if (is_failure)
	{
		int ret = MessageBox(NULL, L"Game Over! Again?", L"提示", MB_OKCANCEL | MB_SYSTEMMODAL);
		if (ret == IDOK)
		{
			GameInit();  // 游戏重新初始化
		}
        else if (ret == IDCANCEL)
		{
			exit(0);
		}
	}
	// 3. 执行未完成的绘制任务
	FlushBatchDraw();  
}

1. 灰色方格的填充颜色:

2. fillrectangle 函数:

这个函数使用当前线形和当前填充样式绘制有外框的填充矩形。

void fillrectangle(
    int left,
    int top,
    int right,
    int bottom
);

参数

  • left:矩形左部 x 坐标。

  • top:矩形顶部 y 坐标。

  • right:矩形右部 x 坐标。

  • bottom:矩形底部 y 坐标。

3. 游戏画面:

  


三、 与输入无关的更新

3.1 - 小蛇移动

void SnakeMove()
{
	int i = 0;
	int j = 0;
	// 1. 让所有大于 0 的元素加 1
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] > 0)
			{
				board[i][j] += 1;
			}
		}
	}
	// 2. 找到旧蛇尾和旧蛇头的位置
	int old_tail_i, old_tail_j;  // 旧蛇尾的位置
	int old_head_i, old_head_j;  // 旧蛇头的位置
	int max = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] > max)
			{
				max = board[i][j];
				old_tail_i = i;
				old_tail_j = j;
			}
			if (board[i][j] == 2)
			{
				old_head_i = i;
				old_head_j = j;
			}
		}
	}
	// 3. 根据移动方向找到新蛇头的位置
	int new_head_i = old_head_i;
	int	new_head_j = old_head_j;
	if (move_direction == 'A')  // 向左移动
	{
		new_head_j = old_head_j - 1;
	}
	else if (move_direction == 'D')  // 向右移动
	{
		new_head_j = old_head_j + 1;
	}
	else if (move_direction == 'W')  // 向上移动
	{
		new_head_i = old_head_i - 1;
	}
	else if (move_direction == 'S')  // 向下移动
	{
		new_head_i = old_head_i + 1;
	}
	// 4. 判断游戏是否失败,即越过边界或者蛇头与蛇身发生碰撞
	if (new_head_i < 0 || new_head_i >= ROW || 
        new_head_j < 0 || new_head_j >= COL || 
		board[new_head_i][new_head_j] > 0)
	{
		is_failure = 1;
		return;
	}
	// 5. 设置新蛇头以及处理旧蛇尾
	board[new_head_i][new_head_j] = 1;
	if (new_head_i == food_i && new_head_j == food_j)  // 此时不对旧蛇尾进行处理
	{
		food_i = rand() % ROW;
		food_j = rand() % COL;
	}
	else 
	{
		board[old_tail_i][old_tail_j] = 0;  // 将旧蛇尾设置为灰方格
	}
}

 1. 小蛇移动(例如向右移动):

 2. 小蛇吃食物:

3.2 - 控制小蛇的移动速度

void UpdateWithoutInput()
{
	if (is_failure)
	{
		return;
	}
	static int wait = 1;  // 控制小蛇的移动速度
	wait++;
	if (wait == 10)
	{
		SnakeMove();  // 小蛇移动
		wait = 1;
	}
}

Sleep 函数虽然也可以控制小蛇的移动速度,但是 Sleep 函数会让整个程序暂停,因此使用一个静态变量 wait 来控制小蛇的移动速度


四、 与输入有关的更新

void UpdateWithInput()
{
	// 控制小蛇移动的方向
	ExMessage msg;
	if (peekmessage(&msg, EM_KEY) && is_failure == 0)
	{
		switch (msg.vkcode)  // vscode,即 virtual key code,虚拟键码
		{
		case VK_UP:  // 上
		case 0x57:  // W 键
			move_direction = 'W';
			break;
		case VK_DOWN:  // 下
		case 0x53:  // S 键
			move_direction = 'S';
			break;
		case VK_LEFT:  // 左
		case 0x41:  // A 键
			move_direction = 'A';
			break;
		case VK_RIGHT:  // 右
		case 0x44:  // D 键
			move_direction = 'D';
			break;
		}
		SnakeMove();
	}
}


五、源代码

#define _CRT_SECURE_NO_WARNINGS 1

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

#define ROW 30
#define COL 40
#define BLOCK_SIZE 20

int board[ROW][COL] = { 0 };  // 创建一个二维数组用来存储游戏画面上每个格子的信息
char move_direction;  // 小蛇的移动方向
int food_i, food_j;  // 食物的位置
int is_failure;  // 判断游戏是否失败


// 一、游戏初始化(仅执行一次)
void GameInit()
{
	srand((unsigned int)time(NULL));  // 设置随机数种子
	initgraph(COL * BLOCK_SIZE, ROW * BLOCK_SIZE);  // 初始化绘画窗口
	setlinecolor(RGB(200, 200, 200));  // 设置每个格子边框的画线颜色
	int i = 0;
	int j = 0;
	// 1. 当游戏重新开始时,需要将每个格子的信息重新初始化为 0
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] = 0;
		}
	}
	// 2. 分别生成蛇头和蛇身
	board[ROW / 2][COL / 2] = 1;  // 1 表示蛇头
	for (i = 1; i <= 4; i++)
	{
		board[ROW / 2][COL / 2 - i] = i + 1;  // 蛇头后的蛇身依次为 2、3、4、5 等正整数
	}
	// 3. 对几个全局变量进行初始化
	move_direction = 'D';  // 向右移动
	food_i = rand() % ROW;
	food_j = rand() % COL;
	is_failure = 0;
	// 4. 开始批量绘图
	BeginBatchDraw();
}


// 二、显示游戏画面
void Show()
{
	int i = 0;
	int j = 0;
	// 1. 显示
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] > 0)
			{
				setfillcolor(HSVtoRGB(board[i][j] * 10, 1, 1));  // 绘制彩色方格
			}
			else
			{
				setfillcolor(RGB(150, 150, 150));  // 绘制灰色方格
			}
			fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
		}
	}
	setfillcolor(RGB(0, 255, 0));  // 绘制绿色食物
	fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, 
		(food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
	// 2. 判断是否失败
	if (is_failure)
	{
		int ret = MessageBox(NULL, L"Game Over! Again?", L"提示", MB_OKCANCEL | MB_SYSTEMMODAL);
		if (ret == IDOK)
		{
			GameInit();  // 游戏重新初始化
		}
		else if (ret == IDCANCEL)
		{
			exit(0);
		}
	}
	// 3. 执行未完成的绘制任务
	FlushBatchDraw();  
}


// 三、和输入无关的更新
void SnakeMove()
{
	int i = 0;
	int j = 0;
	// 1. 让所有大于 0 的元素加 1
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] > 0)
			{
				board[i][j] += 1;
			}
		}
	}
	// 2. 找到旧蛇尾和旧蛇头的位置
	int old_tail_i, old_tail_j;  // 旧蛇尾的位置
	int old_head_i, old_head_j;  // 旧蛇头的位置
	int max = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] > max)
			{
				max = board[i][j];
				old_tail_i = i;
				old_tail_j = j;
			}
			if (board[i][j] == 2)
			{
				old_head_i = i;
				old_head_j = j;
			}
		}
	}
	// 3. 根据移动方向找到新蛇头的位置
	int new_head_i = old_head_i;
	int	new_head_j = old_head_j;
	if (move_direction == 'A')  // 向左移动
	{
		new_head_j = old_head_j - 1;
	}
	else if (move_direction == 'D')  // 向右移动
	{
		new_head_j = old_head_j + 1;
	}
	else if (move_direction == 'W')  // 向上移动
	{
		new_head_i = old_head_i - 1;
	}
	else if (move_direction == 'S')  // 向下移动
	{
		new_head_i = old_head_i + 1;
	}
	// 4. 判断游戏是否失败,即越过边界或者蛇头与蛇身发生碰撞
	if (new_head_i < 0 || new_head_i >= ROW || new_head_j < 0 || new_head_j >= COL
		|| board[new_head_i][new_head_j] > 0)
	{
		is_failure = 1;
		return;
	}
	// 5. 设置新蛇头以及处理旧蛇尾
	board[new_head_i][new_head_j] = 1;
	if (new_head_i == food_i && new_head_j == food_j)  // 此时不对旧蛇尾进行处理
	{
		food_i = rand() % ROW;
		food_j = rand() % COL;
	}
	else 
	{
		board[old_tail_i][old_tail_j] = 0;  // 将旧蛇尾设置为灰方格
	}
}

void UpdateWithoutInput()
{
	if (is_failure)
	{
		return;
	}
	static int wait = 1;  // 控制小蛇的移动速度
	wait++;
	if (wait == 12)
	{
		SnakeMove();  // 小蛇移动
		wait = 1;
	}
}


// 四、和输入有关的更新
void UpdateWithInput()
{
	// 控制小蛇移动的方向
	ExMessage msg;
	if (peekmessage(&msg, EM_KEY) && is_failure == 0)
	{
		switch (msg.vkcode)  // vscode,即 virtual key code,虚拟键码
		{
		case VK_UP:  // 上
		case 0x57:  // W 键
			move_direction = 'W';
			break;
		case VK_DOWN:  // 下
		case 0x53:  // S 键
			move_direction = 'S';
			break;
		case VK_LEFT:  // 左
		case 0x41:  // A 键
			move_direction = 'A';
			break;
		case VK_RIGHT:  // 右
		case 0x44:  // D 键
			move_direction = 'D';
			break;
		}
		SnakeMove();
	}
}


// 主函数
int main()
{
	GameInit();  // 游戏初始化
	while (1)
	{
		Show();  // 显示游戏画面
		UpdateWithoutInput();  // 和输入无关的更新
		UpdateWithInput();  // 和输入有关的更新
	}
	return 0;
}

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值