一步一步教你用easyx实现贪吃蛇

这篇博客详细介绍了如何利用Visual Studio 2019集成开发环境和EasyX图形库,从零开始构建一个经典的贪吃蛇游戏。游戏设计包括窗口初始化、蛇和食物对象的定义、用户交互、碰撞检测以及分数显示等功能。通过实例代码展示了游戏的主要逻辑,包括蛇的移动、食物生成、碰撞判断等关键部分,帮助读者理解游戏开发的基本步骤和技术要点。
摘要由CSDN通过智能技术生成

                                                                       无知就是无知,谁也没由权力相信它能够衍生出任何东西。------------弗洛伊德

 

贪吃蛇是我们小时候经常玩的一款经典趣味小游戏,作为技术人员,看着这款游戏,我们内心也经常会有自己实现一款这样小游戏的想法:

    开发工具:visual studio 2019

    开发组件:easyx

 

一、游戏思路

  1.     构建一个窗口画布,大小为800*600
  2.     定义蛇对象,蛇有长度、运动方向、下一运动点、颜色属性。
  3.     定义食物对象,食物即为一个block
  4.     画布中首先初始化蛇和食物,然后画出蛇和食物
  5.     与用户交互,响应按键消息,蛇随按键调整方向,期间吃食物长长
  6.     刷新画布,更新蛇和食物,并进行因素判断,如果碰壁或碰自己游戏结束。

二、代码实现

2.1,头文件

    我们新建一个gsnake.h的头文件,将我们需要声明的变量和函数都声明好,代码如下:

#ifndef __SNAKE_H
#define __SNAKE_H

#include <graphics.h> //包含exayx库函数的头文件
#include <vector>

using namespace std;

#define BLOCK_UNIT 10
#define WIDTH (BLOCK_UNIT*80)
#define HEIGHT (BLOCK_UNIT*60)


/*坐标点结构体*/
struct Point
{
	int x;//x坐标
	int y;//y坐标
};
/*定义蛇结构体*/
struct Snake
{
	vector<Point> sp;//每个蛇节点的坐标
	int len;//蛇长度,即蛇节点个数
	int position;//蛇的方向
	vector<COLORREF> color;//每个蛇节点的颜色
	Point next;//蛇运动的下一个坐标点
}snake;
struct Food
{
	Point fp[5];//一个用来存放屏幕中食物个数坐标数组
	int grade;//食物分数
	int num=1;//食物个数
	COLORREF color[5];//食物颜色
}food;

//蛇前进方向枚举类型
enum position
{
	up,//上
	down,//下
	left,//左
	right//右

};

void init();//初始化操作,包括初始化蛇,初始化食物
void initSnake();//初始化蛇
void initFood(int num);//初始化食物
void drawSnake();//画蛇
void drawFood();//画食物
void keyMsg();//键盘消息
void snakeMove();//蛇移动
void snakeEatFood();//蛇吃食物
void drawGrade();//画分数
bool gameOver();//游戏是否结束
void drawGameOver();//游戏撞墙结束画前蛇状态

#endif // __SNAKE_H



2.2,函数实现

    初始化蛇和食物

    

/*
 * 初始化函数
*/
void init()
{
	initSnake();
    initFood(0);

}

/*
 *  初始化蛇
*/
void initSnake()
{
	Point pxy;//坐标点对象
	pxy.x = 20;
	pxy.y = 0;
	snake.sp.push_back(pxy);//初始化蛇头点
	snake.color.push_back(RGB(rand() % 256, rand() % 256, rand() % 256));//设置第一个节点随机颜色
	pxy.x = 10;
	pxy.y = 0;
	snake.sp.push_back(pxy);//加入第二个蛇节点
	snake.color.push_back(RGB(rand() % 256, rand() % 256, rand() % 256));//设置第二个节点随机颜色
	pxy.x = 0;
	pxy.y = 0;
	snake.sp.push_back(pxy);//加入第三个蛇节点
	snake.color.push_back(RGB(rand() % 256, rand() % 256, rand() % 256));//设置第三个节点随机颜色

	//加入节点后,确定节点个数
	snake.len = 3;
	//初始化运动方向
	snake.position = right;
}
/*
 * 初始化食物,数组中每个食物随机化放置
*/
void initFood(int num)
{
	food.fp[num].x = rand() % 80 * BLOCK_UNIT;//随机生成食物的x坐标点
	food.fp[num].y = rand() % 60 * BLOCK_UNIT;//随机生成食物的y坐标点
	int flag = 1;//标记变量,如果为1代表食物生成在蛇身上,食物需要重新生成
	while (flag)
	{
		for (int i = 0; i < snake.len; i++)//遍历蛇身
		{
			if (food.fp[num].x == snake.sp[i].x && food.fp[num].y == snake.sp[i].y)//如果食物的坐标与其中某个蛇节点坐标一致,说明食物和节点重合
			{
				food.fp[num].x = rand() % 80 * BLOCK_UNIT;//随机生成食物的x坐标点
				food.fp[num].y = rand() % 60 * BLOCK_UNIT;//随机生成食物的y坐标点
				flag = 1;//代表食物与蛇身冲突
				break;//跳出循环
			}
			flag = 0;
		}

	}

}

    画蛇和食物

/*
 * 画蛇函数
*/
void drawSnake()
{
	for (int i = 0; i < snake.len; i++)//遍历蛇身
	{
		setfillcolor(snake.color[i]);//设置每个蛇节点的颜色
		fillrectangle(snake.sp[i].x, snake.sp[i].y, snake.sp[i].x + BLOCK_UNIT, snake.sp[i].y + BLOCK_UNIT);//画蛇节点方块
	}
}

/*
 * 画食物
*/
void drawFood()
{
	for (int i = 0; i < food.num; i++)//遍历食物个数
	{
		setfillcolor(food.color[i] = RGB(rand() % 256, rand() % 256, rand() % 256));//为每个食物随机生成颜色
		fillrectangle(food.fp[i].x, food.fp[i].y, food.fp[i].x + BLOCK_UNIT, food.fp[i].y + BLOCK_UNIT);//画食物块
	}
}

    按键交互

/*
 * 按键交互,实现蛇随键盘方向键移动
*/
void keyMsg()
{
	char userKey = _getch();//获取键盘输入字符键
	if (userKey == -32)			// 表明这是方向键
		userKey = -_getch();	// 获取具体方向,并避免与其他字母的 ASCII 冲突
	switch (userKey)//判断按键
	{
	case 'w':
	case 'W':
	case -72://方向上键
		if (snake.position != down)
		{
			snake.position = up;
		}
		break;
	case 's':
	case 'S':
	case -80://方向下键
		if (snake.position != up)
		{
			snake.position = down;
		}
		break;
	case 'a':
	case 'A':
	case -75://方向左键
		if (snake.position != right)
		{
			snake.position = left;
		}
		break;
	case 'd':
	case 'D':
	case -77://方向右键
		if (snake.position != left)
		{
			snake.position = right;
		}
		break;
	}
}

    移动蛇

/*
 * 移动蛇身,通过设置vector里面n=n-1方式来实现蛇身子前移动
*/
void snakeMove()
{
	//因为设置n=n-1,所以需要提前保存好尾节点
	snake.next = snake.sp[snake.len - 1];
	//通过n=n-1前移蛇身子
	for (int i = snake.len - 1; i >= 1; i--)
	{
		snake.sp[i] = snake.sp[i - 1];
	}
	//移动1至n号节点信息后,根据移动方向,移动蛇的头部
	switch (snake.position)
	{
	case right:
		snake.sp[0].x += BLOCK_UNIT;//如果向右移动,则x坐标增加一个方块单位
		break;
	case left:
		snake.sp[0].x -= BLOCK_UNIT;//如果向左移动,则x坐标减少一个方块单位
		break;
	case up:
		snake.sp[0].y -= BLOCK_UNIT;//如果向上移动,则y坐标减少一个方块单位
		break;
	case down:
		snake.sp[0].y += BLOCK_UNIT;//如果向下移动,则y坐标增加一个方块单位
		break;
	}
}

    蛇吃食物

/*
 * 蛇头节点与食物节点重合,则代表吃到食物,食物加入蛇身上,那么蛇身变长
*/
void snakeEatFood()
{
	for (int i = 0; i < food.num; i++)//遍历食物
	{
		if (snake.sp[0].x == food.fp[i].x && snake.sp[0].y == food.fp[i].y)//如果某个食物与蛇头重合,代表吃食物
		{
			snake.len++;//蛇身子长度增加
			snake.sp.push_back(snake.next);//把食物加入预留的尾部节点
			snake.color.push_back(food.color[i]);//根据食物颜色设置蛇尾食物颜色
			food.grade += 100;//分数加100
			initFood(i);//吃掉食物,那么就新增加一个食物
			if (food.num < 5 && food.grade % 500 == 0 && food.grade != 0)//每得500分,增加一个食物,但是食物总数不超过5个
			{
				food.num++;
				initFood(food.num - 1);
			}
			break;
		}
	}
}

    显示分数

/*
 * 根据食物分数显示游戏获取得分数
 *
*/

void drawGrade()
{
	TCHAR grade[20] = ""; //一个字符串数组
	sprintf_s(grade, "score:%d", food.grade);//写入分数
	outtextxy(650, 50, grade);//画布写出分数
}

    判断是否游戏结束

/*
 * 根据蛇头是否碰到墙壁和蛇头是否碰到蛇身来判断游戏是否结束
*/
bool gameOver()
{
	if (snake.sp[0].y <= -10 && snake.position == up)//上移过程中碰到墙壁
		return true;
	if (snake.sp[0].y >= (HEIGHT + BLOCK_UNIT) && snake.position == down)//下移过程碰到墙壁
		return true;
	if (snake.sp[0].x >= (WIDTH + BLOCK_UNIT) && snake.position == right)//右移过程碰到墙壁
		return true;
	if (snake.sp[0].x <= -10 && snake.position == left)//左移过程碰到墙壁
		return true;

	//如果蛇头碰到蛇身
	for (int i = 1; i < snake.len; i++)
	{
		//如果右移与蛇身子任何一个节点交叉重叠,说明碰到蛇身自己
		if (snake.position == right && snake.sp[0].x + 10 >= snake.sp[i].x && snake.sp[0].x  <= snake.sp[i].x  && snake.sp[0].y == snake.sp[i].y)
			return true;
		//如果左移与蛇身子任何一个节点交叉重叠,说明碰到蛇身自己
		if (snake.position == left && snake.sp[0].x <= snake.sp[i].x + 10 && snake.sp[0].x >= snake[i].x && snake.sp[0].y == snake.sp[i].y)
			return true;
		//如果上移与蛇身子任何一个节点交叉重叠,说明碰到蛇身自己
		if (snake.position == up && snake.sp[0].x == snake.sp[i].x && snake.sp[0].y <= snake.sp[i].y + 10 && snake.sp[0].y >= snake.sp[i].y)
			return true;
		//如果下移与蛇身子任何一个节点交叉重叠,说明碰到蛇身自己
		if (snake.position == down && snake.sp[0].x == snake.sp[i].x && snake.sp[0].y + 10 >= snake.sp[i].y && snake.sp[0].y <= snake.sp[i].y)
			return true;
	}
	return false;
}

    游戏撞墙结束,则恢复撞墙前状态

//撞墙后,重画蛇撞前状态
void drawGameOver()
{
	if ((snake.sp[0].x >= WIDTH && snake.position == right) || (snake.sp[0].x <= -10 && snake.position == left) || (snake.sp[0].y <= -10 && snake.position == up) || (snake.sp[0].y >= HEIGHT && snake.position == down))//根据撞墙条件判断
	{
		for (int i = 0; i < snake.len - 2; i++)//遍历蛇节点
		{
			snake.sp[i] = snake.sp[i + 1];//重置蛇节点

		}
		snake.sp[snake.len - 1] = snake.next;//补上蛇尾
		drawSnake();//画出蛇
	}
}

2.3,主函数完成,游戏完成

    至此,贪吃蛇游戏运行所需函数都已完成,接下来我们只要在主函数中自上而下完成各个函数的调用,即可完成整个游戏:

/*
   主函数,实现游戏全部流程
*/
int main()
{
	//初始化窗口画布大小
	initgraph(WIDTH, HEIGHT);
	//设置窗口背景颜色
	setbkcolor(RGB(243, 183, 72));
	srand((unsigned)time(NULL));
	settextcolor(BLUE);//设置字体颜色
	setbkmode(TRANSPARENT);			// 设置文字输出模式为透明
	//初始化蛇和食物
	init();
	drawSnake();//画蛇
	while (!gameOver())//判断是否游戏结束
	{
		Sleep(150);//休眠0.15秒
		BeginBatchDraw();
		cleardevice();
		if (_kbhit())
		{
			keyMsg();
		}
		snakeMove();
		snakeEatFood();
		drawFood();
		drawSnake();
		drawGrade();
		EndBatchDraw();
	}
	drawGameOver();
	_getch();
}

 

                                                                           喜欢的朋友可以扫描以下二维码进行关注,公众号将经常更新文章:

                                                                                                                       

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值