栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【无标题】

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【无标题】

基于EasyX库的C语言小游戏

文章目录
  • 基于EasyX库的C语言小游戏
  • 前言
  • 游戏实录
  • 需求与技术难点分析
    • 地图与蛇的生成
    • 蛇的移动
    • 游戏失败
  • 源码


前言

上一篇文章我们已经做过了见缝插针的小游戏,这次再利用EasyX库做一个贪吃蛇的游戏。

游戏实录

需求与技术难点分析 地图与蛇的生成

利用二维数组,这时我们就可以想到,二维数组的值的多态性,值的多态决定了地图里不止一种的表示,我们可以想到用0表示空地,>0的为蛇身。

同时,一个像素点太小了,我们可以用宏定义的方法手动设置像素块

#define BLOCK_SIZE 20 // 每个小格子的长宽大小 
#define HEIGHT 30 // 高度上一共30个小格子
#define WIDTH 40 //  宽度上一共40个小格子 
蛇的移动

我们从键盘获取蛇的移动方向,同时初始方向为右,为了防止出现因为前一步向上后一步向下而导致的自己咬自己的bug,我们除了定义moveDirection的同时,还定义moveDirection_old变量,用于避免这种现象的产生。

同时因为蛇本身会按照初始方向移动,键盘输入是在某一个时间点进行,因此我们将这两个事件封装为两个更新函数,一个与输入无关,另一个与输入有关。

蛇的移动本质上是数组的更新,我们在第一步设置的蛇身的值>0,由此我们可以发挥一下,将蛇头的值设置为1,将蛇身的值设置为蛇的长度,因此,数组中值最大的元素下标就对应着蛇尾,值最小的元素下标就对应着蛇头。而蛇更新的时候本质上移动的只有头和尾(如果吃到了食物的话,就只用移动头)。

游戏失败

游戏失败分为两种情况:

  • 蛇头碰壁
    这种情况我们只需要将蛇头坐标与边界值相比较就行
  • 咬到了蛇身
    我们需要将下一步对应的蛇头坐标对应数组中的值进行检查,判断是否大于零,大于0则咬到了自身
源码
#include   
#include 
#include 
#define BLOCK_SIZE 20 // 每个小格子的长宽大小 
#define HEIGHT 30 // 高度上一共30个小格子
#define WIDTH 40 //  宽度上一共40个小格子 

// 全局变量定义
int Blocks[HEIGHT][WIDTH] = { 0 }; //  二维数组,用于记录所有的游戏数据
char moveDirection_old, moveDirection;  //  小蛇移动方向
int food_i, food_j; //  食物的位置
int isFailure = 0; //  是否游戏失败

void moveSnake() //  移动小蛇及相关处理函数
{
	int i, j;
	for (i = 0; i < HEIGHT; i++) // 对行遍历 
		for (j = 0; j < WIDTH; j++) // 对列遍历
			if (Blocks[i][j] > 0) // 大于0的为小蛇元素 
				Blocks[i][j]++; //  让其+1
	int oldTail_i, oldTail_j, oldHead_i, oldHead_j; // 定义变量,存储旧蛇尾、旧蛇头坐标  
	int max = 0; // 用于记录最大值 
	for (i = 0; i < HEIGHT; i++) //  对行列遍历
	{
		for (j = 0; j < WIDTH; j++)
		{
			if (max < Blocks[i][j]) //  如果当前元素值比max大
			{
				max = Blocks[i][j]; // 更新max的值
				oldTail_i = i; //  记录最大值的坐标,就是旧蛇尾的位置
				oldTail_j = j; //  
			}
			if (Blocks[i][j] == 2) // 找到数值为2 
			{
				oldHead_i = i; //  数值为2恰好是旧蛇头的位置
				oldHead_j = j; //  
			}
		}
	}
	int newHead_i = oldHead_i; //  设定变量存储新蛇头的位置
	int newHead_j = oldHead_j;

	//  根据用户按键,设定新蛇头的位置
	if (moveDirection == 'w') // 向上移动
		newHead_i = oldHead_i - 1;
	else if (moveDirection == 's') // 向下移动
		newHead_i = oldHead_i + 1;
	else if (moveDirection == 'a') // 向左移动
		newHead_j = oldHead_j - 1;
	else if (moveDirection == 'd') // 向右移动
		newHead_j = oldHead_j + 1;

	//  如果蛇头超出边界,或者蛇头碰到蛇身,游戏失败
	if (newHead_i >= HEIGHT || newHead_i < 0 || newHead_j >= WIDTH || newHead_j < 0
		|| Blocks[newHead_i][newHead_j]>0)
	{
		isFailure = 1; //  游戏失败
		return; // 函数返回
	}

	Blocks[newHead_i][newHead_j] = 1;  // 新蛇头位置数值为1	
	if (newHead_i == food_i && newHead_j == food_j) //  如果新蛇头正好碰到食物
	{
		food_i = rand() % (HEIGHT) ; //  食物重新随机位置
		food_j = rand() % (WIDTH ); //  
		// 不对旧蛇尾处理,相当于蛇的长度+1
	}
	else // 新蛇头没有碰到食物
		Blocks[oldTail_i][oldTail_j] = 0; // 旧蛇尾变成空白,不吃食物时保持蛇的长度不变
}

void startup()  //  初始化函数
{
	int i;
	Blocks[HEIGHT / 2][WIDTH / 2] = 1; // 画面中间画蛇头,数字为1
	for (i = 1; i <= 4; i++) //  向左依次4个蛇身,数值依次为2,3,4,5
		Blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;
	moveDirection = moveDirection_old= 'd';	 //  初始向右移动
	food_i = rand() % (HEIGHT) ; //  初始化随机食物位置
	food_j = rand() % (WIDTH ); //  
	initgraph(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE); //  新开画面
	setlinecolor(RGB(200, 200, 200)); // 设置线条颜色
	BeginBatchDraw(); // 开始批量绘制
}

void show()  // 绘制函数
{
	cleardevice(); // 清屏
	int i, j;
	for (i = 0; i < HEIGHT; i++) //  对二维数组所有元素遍历
	{
		for (j = 0; j < WIDTH; j++)
		{
			if (Blocks[i][j] > 0) // 元素大于0表示是蛇,这里让蛇的身体颜色色调渐变
				setfillcolor(HSVtoRGB(Blocks[i][j] * 10, 0.9, 1));
			else
				setfillcolor(RGB(150, 150, 150)); // 元素为0表示为空,颜色为灰色
			// 在对应位置处,以对应颜色绘制小方格
			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);
	if (isFailure) //  如果游戏失败
	{
		setbkmode(TRANSPARENT); // 文字字体透明    
		settextcolor(RGB(255, 0, 0));// 设定文字颜色
		settextstyle(80, 0, _T("宋体")); //  设定文字大小、样式
		outtextxy(240, 220, _T("游戏失败")); //  输出文字内容
	}
	FlushBatchDraw(); // 批量绘制
}

void updateWithoutInput() // 与输入无关的更新函数
{
	if (isFailure) //  如果游戏失败,函数返回
		return;
	static int waitIndex = 1; // 静态局部变量,初始化时为1
	waitIndex++; // 每一帧+1
	if (waitIndex == 10) // 如果等于10才执行,这样小蛇每隔10帧移动一次
	{
		moveSnake(); //  调用小蛇移动函数
		waitIndex = 1; // 再变成1
	}
}

void updateWithInput()  // 和输入有关的更新函数
{
	if (_kbhit() && isFailure == 0)  //  如果有按键输入,并且不失败
	{
		char input = _getch(); //  获得按键输入
		switch (input) {     //注意防止自咬现象(即前后两次运动方向相反)
		case 'a':
			moveDirection = input;  
			if (moveDirection_old != 'd') {
				moveSnake();
				moveDirection_old = moveDirection;
			}
				
			else moveDirection = moveDirection_old;
			break;
		case 'w':
			moveDirection = input;  
			if (moveDirection_old != 's') {
				moveSnake();		
				moveDirection_old = moveDirection;
			}
			else moveDirection = moveDirection_old;
			break;
		case 's':
			moveDirection = input;  
			if (moveDirection_old != 'w') {
				moveSnake();
				moveDirection_old = moveDirection;
			}
			else moveDirection = moveDirection_old;
			break;
		case 'd':
			moveDirection = input;  
			if (moveDirection_old != 'a') {
				moveSnake();
				moveDirection_old = moveDirection;
			}
			else moveDirection = moveDirection_old;
			break;
		}
	}
}

int main() //  主函数
{
	startup();  // 初始化函数,仅执行一次	
	while (1)   // 一直循环
	{
		show();  // 进行绘制
		updateWithoutInput(); // 和输入无关的更新 
		updateWithInput();    // 和输入有关的更新
	}
	return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/997369.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号