龙空技术网

C/C++实现迷宫游戏(进阶版)!深度优先算法实现案例

C语言编程 9734

前言:

此刻咱们对“wm算法程序”大约比较关心,同学们都想要剖析一些“wm算法程序”的相关内容。那么小编在网摘上收集了一些对于“wm算法程序””的相关知识,希望大家能喜欢,兄弟们一起来学习一下吧!

每天一个C语言小项目,提升你的编程能力!

玩家被困在一个迷宫里,拥有一盏油灯,油灯能够照亮以玩家为中心的一片圆形区域,随着时间的流逝,油灯的照明力会逐渐下降,迷宫内随机分布着一些加油站(黄色的圆角矩形),经过这些加油站能够恢复油灯的照明力,找到地图右下角的终点(绿色圆角矩形)。就算过关。

游戏采用图块(N * N 的正方形)的方式构建地图,且墙壁,地面,玩家,终点采用四个独立的函数绘制,如果想改变地图的风格,只需要修改这些函数的内容即可。

运行效果如下:

迷宫生成采用的深度优先算法,有明显的主路。

完整的游戏源代码如下:

//////////////////////////////////////////////// 程序名称:迷宫//#include <graphics.h>#include <stack>#include <vector>using std::stack;				// 使用STL的栈using std::vector;				// 使用STL的数组容器// 游戏信息#define WIN_WIDTH	400			// 窗口的宽度(单位:像素)#define WIN_HEIGHT	300			// 窗口的高度(单位:像素)// !!注:由于随机生成算法的原因,地图宽高只能为奇数#define GAME_WIDTH	41			// 地图的宽度(单位:块)#define GAME_HEIGHT	51			// 地图的高度(单位:块)#define WALL		1			// 墙壁的数字标记#define GROUND		0			// 地面的数字标记#define FILLSTATE	2			// 加油站的数字标记#define ENDPOS		3			// 终点的数字标记#define MAXVIEW		8.0			// 最大的视野#define MINVIEW		1			// 最小的视野#define FILLNUM		10			// 加油站的数量#define DARKTIME	12			// 视野下降1图块所需的时间// 全局变量列表int		g_BlockSize;			// 块大小int		g_GameMap[GAME_HEIGHT][GAME_WIDTH];	// 地图(宽高单位为块)POINT	g_EndPos;				// 终点位置POINT   g_PlayerPos;			// 玩家在地图上的位置POINT	g_CameraPos;			// 摄像机(屏幕左上角)在地图上的位置IMAGE	g_MapImage;				// 地图的图片(由于地图是固定的,在不改变缩放的情况下只需要绘制一次)double	g_ViewArray;			// 视野UINT	g_BeginTime;			// 游戏开始时的时间UINT	g_LastFillTime;			// 上次为油灯加油的时间// 函数列表void initGame();				// 初始化游戏void endGame();					// 结束游戏void draw();					// 绘制函数bool upDate();					// 数据更新函数void absDelay(int delay);		// 绝对延迟bool canMove(POINT pos);		// 判断某个位置是否可以移动void computeCameraPos();		// 计算摄像机在地图上的位置void rePaintMap();				// 重绘地图void drawWall(POINT pos);		// 绘制墙壁图块的函数void drawGround(POINT pos);		// 绘制地面图块的函数void drawFillState(POINT pos);	// 绘制油灯图块的函数void drawEndPos(POINT pos);		// 绘制终点void drawPlayer();				// 绘制人物的函数void drawView();				// 绘制视野int main(){	initGame();	while (1)	{		if (!upDate()) break;	// 更新		draw();					// 绘制		absDelay(16);			// 绝对延迟 16 毫秒,控制每秒 60 帧	}	endGame();	return 0;}void initGame(){	g_BlockSize = 32;			// 初始图块大小为 32 个像素	srand(GetTickCount());		// 初始化随机数生成	// 初始化间隔室	for (int i = 0; i < GAME_HEIGHT; i++)	{		for (int j = 0; j < GAME_WIDTH; j++)		{			if (i % 2 == 0 || j % 2 == 0)	// 奇数行奇数列设为墙壁				g_GameMap[i][j] = WALL;			else				g_GameMap[i][j] = GROUND;		}	}	// 随机生成地图(使用深度优先遍历)	stack<POINT> stepStack;		// 步骤栈	vector<POINT>  stepPoint;	// 四周的点	POINT nowPoint;				// 当前步的所在点	stepStack.push({ 1,1 });	// 写入初始点 (1,1) 作为起点	nowPoint = { 1,1 };	g_GameMap[1][1] = 0xFFFF;	// 标记这个点	while (!stepStack.empty())	// 只要步骤栈不空就继续循环	{		// 得到四周的点		POINT tempPoint;		for (int i = -1; i <= 1; i += 2)		{			tempPoint = { nowPoint.x,nowPoint.y + i * 2 };	// 计算点			// 判断坐标是否合法			if (tempPoint.x >= 0 && tempPoint.x <= GAME_WIDTH - 1 &&				tempPoint.y >= 0 && tempPoint.y <= GAME_HEIGHT - 1 &&				g_GameMap[tempPoint.y][tempPoint.x] != 0xFFFF)			{				stepPoint.push_back(tempPoint);			}			tempPoint = { nowPoint.x + i * 2 ,nowPoint.y };	// 计算点			// 判断坐标是否合法			if (tempPoint.x >= 0 && tempPoint.x <= GAME_WIDTH - 1 &&				tempPoint.y >= 0 && tempPoint.y <= GAME_HEIGHT - 1 &&				g_GameMap[tempPoint.y][tempPoint.x] != 0xFFFF)			{				stepPoint.push_back(tempPoint);			}		}		// 根据周围点的量选择操作		if (stepPoint.empty())				// 如果周围点都被遍历过了		{			stepStack.pop();				// 出栈当前点			if (!stepStack.empty())				nowPoint = stepStack.top();	// 更新当前点		}		else		{			stepStack.push(stepPoint[rand() % stepPoint.size()]);	// 入栈当前点			g_GameMap[(nowPoint.y + stepStack.top().y) / 2][(nowPoint.x + stepStack.top().x) / 2] = 0;	// 打通墙壁			nowPoint = stepStack.top();		// 更新当前点			g_GameMap[nowPoint.y][nowPoint.x] = 0xFFFF;				// 标记当前点		}		stepPoint.clear();					// 清空周围点以便下一次循环	}	// 清洗标记点	for (int i = 0; i < GAME_HEIGHT; i++)	{		for (int j = 0; j < GAME_WIDTH; j++)		{			if (g_GameMap[i][j] == 0xFFFF)				g_GameMap[i][j] = 0;		}	}	// 随机生成加油站的位置	for (int i = 0; i < FILLNUM; i++)	{		POINT fillPoint = { rand() % GAME_WIDTH,rand() % GAME_HEIGHT };		// 保证在空地生成加油站		while (g_GameMap[fillPoint.y][fillPoint.x] != GROUND)			fillPoint = { rand() % GAME_WIDTH,rand() % GAME_HEIGHT };		// 标记油灯		g_GameMap[fillPoint.y][fillPoint.x] = FILLSTATE;	}	g_GameMap[GAME_HEIGHT - 2][GAME_WIDTH - 2] = ENDPOS;		// 标记终点	g_EndPos = { GAME_WIDTH - 2,GAME_HEIGHT - 2 };				// 确定终点位置	g_ViewArray = MAXVIEW;				// 初始视野是最大的	g_BeginTime = GetTickCount();		// 开始计时	g_LastFillTime = GetTickCount();	// 油灯加油的时间	rePaintMap();						// 绘制地图	g_PlayerPos = { g_BlockSize * 3 / 2,g_BlockSize * 3 / 2 };	// 初始化人的位置	computeCameraPos();					// 计算摄像机的位置	initgraph(WIN_WIDTH, WIN_HEIGHT);	// 初始化画布	setbkmode(TRANSPARENT);				// 设置背景为透明	BeginBatchDraw();					// 开始缓冲绘制}void endGame(){	EndBatchDraw();						// 结束缓冲绘制	closegraph();						// 关闭画布}void draw(){	// 清空设备	cleardevice();	// 绘制视野	drawView();	// 绘制人	drawPlayer();	// 绘制时间	TCHAR timeStr[256];	int loseTime = GetTickCount() - g_BeginTime;	// 计算流失的时间	_stprintf_s(timeStr, _T("游戏时间:%02d:%02d"), loseTime / 1000 / 60, loseTime / 1000 % 60);	settextcolor(RGB(140, 140, 140));	outtextxy((WIN_WIDTH - textwidth(timeStr)) / 2, 3, timeStr);	FlushBatchDraw();	// 刷新屏幕}bool upDate(){	POINT nextPos = g_PlayerPos;		// 下一个位置	// 计算下一个位置	if (GetKeyState(VK_UP) & 0x8000)	nextPos.y -= 2;	if (GetKeyState(VK_DOWN) & 0x8000)	nextPos.y += 2;	if (GetKeyState(VK_LEFT) & 0x8000)	nextPos.x -= 2;	if (GetKeyState(VK_RIGHT) & 0x8000)	nextPos.x += 2;	// 如果下一个位置不合法	if (!canMove(nextPos))	{		if (canMove({ g_PlayerPos.x, nextPos.y }))		// y 轴移动合法			nextPos = { g_PlayerPos.x, nextPos.y };		else if (canMove({ nextPos.x, g_PlayerPos.y }))	// x 轴移动合法			nextPos = { nextPos.x, g_PlayerPos.y };		else											// 都不合法			nextPos = g_PlayerPos;	}	// 如果是油灯则更新时间	if (g_GameMap[nextPos.y / g_BlockSize][nextPos.x / g_BlockSize] == FILLSTATE)		g_LastFillTime = GetTickCount();	// 如果是终点则通关	else if (g_GameMap[nextPos.y / g_BlockSize][nextPos.x / g_BlockSize] == ENDPOS)	{		outtextxy(WIN_WIDTH / 2 - 40, WIN_HEIGHT / 2 - 12, _T("恭喜过关!"));		FlushBatchDraw();		Sleep(1000);		return false;	}	g_PlayerPos = nextPos;						// 更新位置	computeCameraPos();							// 计算摄像机的位置	// 根据时间缩减视野	static unsigned int lastTime = GetTickCount();	int loseTime = GetTickCount() - g_LastFillTime;			// 计算流失的时间	g_ViewArray = MAXVIEW - loseTime / 1000.0 / DARKTIME;	// 每一段时间油灯的照明力会下降一个图块	if (g_ViewArray < MINVIEW) g_ViewArray = MINVIEW;	// 处理鼠标消息	MOUSEMSG mouseMsg;							// 鼠标信息	int lastBlockSize = g_BlockSize;			// 保存原本的大小	while (MouseHit())	{		mouseMsg = GetMouseMsg();		if (mouseMsg.uMsg = WM_MOUSEWHEEL)		// 滚轮消息		{			g_BlockSize += mouseMsg.wheel / 120;		}	}	// 如果没有滚轮消息就退出	if (lastBlockSize == g_BlockSize) return true;	// 处理滚轮消息	if (g_BlockSize >= 10 && g_BlockSize <= 50)	// 块大小没有达到极限值	{		// 保证缩放后的地图不会比窗口小		if (GAME_WIDTH * g_BlockSize < WIN_WIDTH ||			GAME_HEIGHT * g_BlockSize < WIN_HEIGHT)			g_BlockSize = lastBlockSize;		rePaintMap();							// 重绘地图		// 重新计算玩家在地图上的位置		POINT mapPos = { g_PlayerPos.x / lastBlockSize,g_PlayerPos.y / lastBlockSize };	// 计算在地图上的位置		g_PlayerPos.x = mapPos.x * g_BlockSize + g_BlockSize / 2;	// 计算映射后的位置		g_PlayerPos.y = mapPos.y * g_BlockSize + g_BlockSize / 2;	// 计算映射后的位置		computeCameraPos();						// 重新计算摄像机位置	}	// 保证图块不会过大和过小	if (g_BlockSize < 10) g_BlockSize = 10;	if (g_BlockSize > 50) g_BlockSize = 50;	return true;}void absDelay(int delay){	static int curtime = GetTickCount();	static int pretime = GetTickCount();	while (curtime - pretime < delay)	{		curtime = GetTickCount();		Sleep(1);	}	pretime = curtime;}bool canMove(POINT pos){	// 只要外接矩形的四个顶点不在墙壁内就必定合法	return	g_GameMap[(pos.y - 3) / g_BlockSize][(pos.x - 3) / g_BlockSize] != WALL &&		g_GameMap[(pos.y + 3) / g_BlockSize][(pos.x + 3) / g_BlockSize] != WALL &&		g_GameMap[(pos.y - 3) / g_BlockSize][(pos.x + 3) / g_BlockSize] != WALL &&		g_GameMap[(pos.y + 3) / g_BlockSize][(pos.x - 3) / g_BlockSize] != WALL;}void computeCameraPos(){	// 以人物位置为中心计算摄像机的理论位置	g_CameraPos.x = g_PlayerPos.x - WIN_WIDTH / 2;	g_CameraPos.y = g_PlayerPos.y - WIN_HEIGHT / 2;	// 防止摄像机越界	if (g_CameraPos.x < 0)										g_CameraPos.x = 0;	if (g_CameraPos.y < 0)										g_CameraPos.y = 0;	if (g_CameraPos.x > GAME_WIDTH * g_BlockSize - WIN_WIDTH)	g_CameraPos.x = GAME_WIDTH * g_BlockSize - WIN_WIDTH;	if (g_CameraPos.y > GAME_HEIGHT * g_BlockSize - WIN_HEIGHT)	g_CameraPos.y = GAME_HEIGHT * g_BlockSize - WIN_HEIGHT;}void rePaintMap(){	g_MapImage.Resize(GAME_WIDTH * g_BlockSize, GAME_HEIGHT * g_BlockSize);	// 重置地图图片大小	SetWorkingImage(&g_MapImage);								// 设置地图图片为当前工作图片	for (int i = 0; i < GAME_HEIGHT; i++)	{		for (int j = 0; j < GAME_WIDTH; j++)		{			switch (g_GameMap[i][j])			{			case WALL:				drawWall({ j*g_BlockSize,i*g_BlockSize });		// 绘制墙壁				break;			case FILLSTATE:				drawFillState({ j*g_BlockSize,i*g_BlockSize });	// 绘制加油站				break;			case GROUND:				drawGround({ j*g_BlockSize,i*g_BlockSize });	// 绘制地面				break;			case ENDPOS:				drawEndPos({ j*g_BlockSize,i*g_BlockSize });				break;			}		}	}	SetWorkingImage();	// 复位工作图片}void drawWall(POINT pos){	setfillcolor(RGB(254, 109, 19));	solidrectangle(pos.x, pos.y, pos.x + g_BlockSize, pos.y + g_BlockSize);}void drawGround(POINT pos){	setfillcolor(RGB(255, 255, 255));	solidrectangle(pos.x, pos.y, pos.x + g_BlockSize, pos.y + g_BlockSize);}void drawFillState(POINT pos){	drawGround(pos);	// 绘制圆角矩形	pos.x += g_BlockSize / 5;	pos.y += g_BlockSize / 5;	setfillcolor(RGB(252, 213, 11));	solidroundrect(pos.x, pos.y, pos.x + g_BlockSize / 5 * 3, pos.y + g_BlockSize / 5 * 3, g_BlockSize / 8, g_BlockSize / 8);}void drawEndPos(POINT pos){	drawGround(pos);	// 绘制圆角矩形	pos.x += g_BlockSize / 5;	pos.y += g_BlockSize / 5;	setfillcolor(RGB(87, 116, 48));	solidroundrect(pos.x, pos.y, pos.x + g_BlockSize / 5 * 3, pos.y + g_BlockSize / 5 * 3, g_BlockSize / 8, g_BlockSize / 8);}void drawPlayer(){	setfillcolor(RGB(252, 213, 11));	solidcircle(g_PlayerPos.x - g_CameraPos.x, g_PlayerPos.y - g_CameraPos.y, 3);}void drawView(){	// 锁定视野	HRGN viewArr;	int r = int(g_BlockSize * g_ViewArray + 0.5);	// 计算视野半径	POINT orgin = g_PlayerPos;	orgin.x -= g_CameraPos.x;						// 计算在屏幕上的位置	orgin.y -= g_CameraPos.y;						// 计算在屏幕上的位置	viewArr = CreateEllipticRgn(orgin.x - r, orgin.y - r, orgin.x + r, orgin.y + r);	// 创建一个圆形的区域	setcliprgn(viewArr);							// 锁定区域	// 绘制地图	putimage(0, 0, WIN_WIDTH, WIN_HEIGHT, &g_MapImage, g_CameraPos.x, g_CameraPos.y);	// 删除区域	DeleteObject(viewArr);	// 消除区域	setcliprgn(NULL);}

大家赶紧去动手试试吧!

此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C++项目案例,帮助大家在学习C语言的道路上披荆斩棘!

编程学习书籍分享:

编程学习视频分享:

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!

标签: #wm算法程序