“二十一天好习惯”第一期-21

前言:

今天继续分享之前推箱子的小项目,同时这也是学校“二十一天的博客打卡”的最后一天,最后一期保证不水,这期全是干货,会做的比之前更细致。我们在之前的基础上一步步来实现这个推箱子这个小项目。

开发环境:

1.这里使用的是VS2013,当然2013往后的版本也行。(特别注意创建文件时后缀是cpp文件)

2.图形库easyx,未安装的可以跳转下面的链接:

https://easyx.cn/https://easyx.cn/https://easyx.cn/

要实现的功能:

这里承接直接上期推箱子的内容接着讲(上期是实现推箱子地图的设计与绘制),上期没看可以跳转下面的链接:

https://blog.csdn.net/xwy777/article/details/121196550?spm=1001.2014.3001.5501https://blog.csdn.net/xwy777/article/details/121196550?spm=1001.2014.3001.5501https://blog.csdn.net/xwy777/article/details/121196550?spm=1001.2014.3001.5501今天要实现的功能就是让人物动起来,并与地图产生交互,并简单设计通关的页面。

具体思路及代码实现:

要实现移动的功能,这里定义一个函数void move(),这里要求人要动,首先得要找到人的位置,因为地图是二维数组,之前有设定人的值为2,所以需要遍历一遍二维数组找到值为2的位置并记录就行了。代码如下:

void move()
{
	int x = 0, y = 0;//记录人的位置
	int i = 0, j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if ((tempMap[i][j] == 2) || tempMap[i][j] == 6)//找人
			{
				x = i;
				y = j;
			}
		}
	}

 接着就是这么通过玩家的键盘输入指令使人物发生移动的问题了,这里要使用Switch函数分别对键盘中控制方向的按键进行分类也就是:‘w’,'a','s','d'.这里需要使用_getch()函数从键盘上获取字符。先假设当玩家按键指令为‘w’也就是往上走的情况。往上走需要对上面地图中的状态进行判断,不难想到:有如下情况1.如果是人的上面是空地或者目的地的状态,则与下面的人进行互换

2.如果人的上面是箱子或箱子在目的地的状态,则要进行二次判断:如果箱子的上面是空地或者目的地,就可以推箱子,并让箱子的位置上二维数组值-1(箱子是3人是2,箱子-2就是人),并让原本箱子的上面一格的位置二维数组值+3(意思是把箱子推到这个位置),并将人的位置上二维数组值-2(人是2,空地是1,为方便读者理解具体数字表示的状态如下。

1表示墙  2表示人  3表示箱子  4表示目的地  6表示人在目的地上  7表示箱子在目的地上
 0 表示空地

这样根据这样的思路,其他方向的代码照葫芦画瓢的也可以轻松写出来,完整代码如下

void move()
{
	int x = 0, y = 0;//记录人的位置
	int i = 0, j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if ((tempMap[i][j] == 2) || tempMap[i][j] == 6)//找人
			{
				x = i;
				y = j;
			}
		}
	}

	switch (_getch())//_getch()从键盘上获取字符  conio.h
	{
	case 'w':
		 //如果上面是空地、目的地
		if (tempMap[x - 1][y] == 0 || tempMap[x - 1][y] == 4)
		{
			tempMap[x - 1][y] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的上面是箱子、箱子在目的地上
		if (tempMap[x - 1][y] == 3 || tempMap[x - 1][y] == 7)
		{
			//如果箱子的上面是空地、或者目的地,就可以推箱子
			if (tempMap[x - 2][y] == 0 || tempMap[x - 2][y] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x - 1][y] -= 1;
				tempMap[x - 2][y] += 3;
			}
		}
		break;
	case 's':
		//如果下面是空地,如果下面是目的地
		if (tempMap[x + 1][y] == 0||tempMap[x+1][y]==4)
		{
			tempMap[x + 1][y] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的下面是箱子
		if (tempMap[x + 1][y] == 3 || tempMap[x + 1][y]==7)
		{
			//人要满足,推得动箱子得条件:箱子得下面是空地或者是目的地
			if (tempMap[x + 2][y] == 0 || tempMap[x + 2][y] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x + 1][y] -= 1;
				tempMap[x + 2][y] += 3;
			}
		}
		break;
	case 'a':
		//如果左面是空地,如果左面是目的地
		if (tempMap[x][y - 1] == 0 || tempMap[x][y-1]==4)
		{
			tempMap[x][y-1] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的左面是箱子
		if (tempMap[x][y-1] == 3 || tempMap[x][y - 1] == 7)
		{
			//人要满足,推得动箱子得条件:箱子得左面是空地或者是目的地
			if (tempMap[x][y-2] == 0 || tempMap[x][y - 2] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x][y - 1] -= 1;
				tempMap[x][y - 2] += 3;
			}
		}
		break;
	case 'd':
		//如果右面是空地,如果右面是目的地
		if (tempMap[x][y + 1] == 0 || tempMap[x][y + 1] == 4)
		{
			tempMap[x][y + 1] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的右面是箱子
		if (tempMap[x][y + 1] == 3 || tempMap[x][y + 1] == 7)
		{
			//人要满足,推得动箱子得条件:箱子得右面是空地或者是目的地
			if (tempMap[x][y + 2] == 0 || tempMap[x][y + 2] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x][y + 1] -= 1;
				tempMap[x][y + 2] += 3;
			}
		}
		break;
	}
}

 这样移动的函数就写完了。接下来是判断输赢(即如果赢则跳入下一关和通完所有关卡的提示窗口)的代码段,这里也用函数来实现,函数定义为:void isWin()。那么如何判断是否通关呢?其实在脑中想象下很容易能明天当所有箱子处于状态7:也就是所有箱子都在目的地上时,则过关。如果在地图这个歌二维数组中有位置处于状态3箱子时,则没有通关。代码也就是二维数组遍历及判断了。实现如下:

//地图上没有发现箱子-3:所有的箱子都在目的地上
	int i = 0,j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < ROW; j++)
		{
			if (tempMap[i][j] == 3)//表示在空地上发现了箱子,说明肯定没有通关
			{
				return;
			}
		}
	}

判断完过关了,就到了跳关的环节了。这里要用到之前表示关卡的变量level,过了一关level就要++,同时使用initMap()函数加载新地图。如果过了第三关,这里设置为弹出提示框,询问是否返回第一关,是则返回,不是则关闭窗口。这里要用到if(IDYES == MessageBox(GetHWnd(), "是否开始第一关", "提示", MB_YESNO))功能是弹出一个窗口,内容为第二个参数,窗口名为第三个参数,第四个参数为选项YES或NO,如果选了YES则level置0,回到第一关,若为NO,则使用exit(0)函数实现关闭窗口操作,以上操作用简单的ifelse语句即可完成。具体代码实现如下:

void isWin()
{
	//地图上没有发现箱子-3:所有的箱子都在目的地上
	int i = 0,j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < ROW; j++)
		{
			if (tempMap[i][j] == 3)//表示在空地上发现了箱子,说明肯定没有通关
			{
				return;
			}
		}
	}
	//如果整个循环都走了下来,没有箱子在地图上,表示当前关卡通过
	level++;//level值是3的时候,如果继续下一个关卡,会发生三维数组越界
	if (level == 3)
	{
		if (IDYES == MessageBox(GetHWnd(), "是否开始第一关", "提示", MB_YESNO))
		{
			level = 0;//从0开始
		}
		else
		{
			exit(0);//退出程序
		}
	}
	initMap();//加载新地图
}

 如上各个函数功能板块就完成了在按照一定的顺序调用函数就行了,具体怎么来的这里不多加赘述,给读者思考空间,代码如下:

int main()
{
	initgraph(500, 500);
	//把图片放进数组中
	loadPic();
	//把当前关卡的图片放进辅助地图中
	initMap();
	//贴图
	drawMap();

	while (1)
	{
		isWin();//判断是否通关
		cleardevice();//清屏
		drawMap();//贴图
		move();//人物移动
		cleardevice();//清屏
		drawMap();//贴图
	}
	closegraph();
	return 0;
}

 到这里就完成了一个推箱子的小项目。整个程序完整代码如下:

#include<easyx.h>//打开图形库
#include<conio.h>
#define ROW 10//行
#define COL 10//列

int map[3][ROW][COL] = {//map是地图
	//1表示墙  2表示人  3表示箱子  4表示目的地  6表示人在目的地上  7表示箱子在目的地上
	//0 表示空地
	{
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 0, 0, 0, 0, 0, 4, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 3, 0, 0, 1,
		1, 0, 3, 0, 4, 0, 2, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 3, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 4, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1
	},
	{
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 4, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 3, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
		1, 4, 3, 0, 2, 0, 0, 3, 4, 1,
		1, 1, 1, 1, 0, 1, 1, 0, 1, 1,
		1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 3, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 4, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1
	},
	{
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 2, 3, 4, 0, 0, 0, 0, 0, 1,
		1, 0, 3, 4, 0, 0, 0, 0, 0, 1,
		1, 0, 3, 4, 0, 0, 3, 4, 0, 1,
		1, 0, 3, 4, 0, 0, 3, 4, 0, 1,
		1, 0, 3, 4, 0, 0, 3, 4, 0, 1,
		1, 0, 0, 0, 0, 0, 3, 4, 0, 1,
		1, 0, 0, 0, 0, 0, 3, 4, 0, 1,
		1, 0, 0, 0, 0, 0, 3, 4, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1
	}
};

int tempMap[ROW][COL] = { 0 };//辅助地图
int level = 0;//关卡

IMAGE img[5];//图片数组,用来保存图片

//加载地图
void initMap()
{
	//把第level个二维数组加载到辅助地图中来
	int i = 0, j = 0;
	for (i = 0; i < 10; i++)
	{
		for (j = 0; j < 10; j++)
		{
			tempMap[i][j] = map[level][i][j];
		}
	}
}

//贴地图
void drawMap()
{
	BeginBatchDraw();//批量贴图
	int i = 0, j = 0;
	for (i = 0; i < 10; i++)//i表示行下标
	{
		for (j = 0; j < 10; j++)//j表示列下标
		{
			switch (tempMap[i][j])//
			{
			case 1:putimage(j * 50, 50 * i, &img[0]); break;//墙
			case 6:
			case 2:putimage(j * 50, 50 * i, &img[1]); break;//人
			case 3:putimage(j * 50, 50 * i, &img[2]); break;//箱子
			case 4:putimage(j * 50, 50 * i, &img[3]); break;//目的地
			case 7:putimage(j * 50, 50 * i, &img[4]); break;//箱子在目的地上
			}
		}
	}
	EndBatchDraw();//结束批量绘制
}

//加载图片 把图片加载进图片数组中
void loadPic()
{
	loadimage(&img[0], "1.jpg", 50, 50);//墙
	loadimage(&img[1], "2.jpg", 50, 50);//人
	loadimage(&img[2], "3.jpg", 50, 50);//箱子
	loadimage(&img[3], "4.jpg", 50, 50);//目的地
	loadimage(&img[4], "7.jpg", 50, 50);//箱子在目的地上
}


//人物移动
void move()
{
	int x = 0, y = 0;//记录人的位置
	int i = 0, j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if ((tempMap[i][j] == 2) || tempMap[i][j] == 6)//找人
			{
				x = i;
				y = j;
			}
		}
	}

	switch (_getch())//_getch()从键盘上获取字符  conio.h
	{
	case 'w':
		 //如果上面是空地、目的地
		if (tempMap[x - 1][y] == 0 || tempMap[x - 1][y] == 4)
		{
			tempMap[x - 1][y] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的上面是箱子、箱子在目的地上
		if (tempMap[x - 1][y] == 3 || tempMap[x - 1][y] == 7)
		{
			//如果箱子的上面是空地、或者目的地,就可以推箱子
			if (tempMap[x - 2][y] == 0 || tempMap[x - 2][y] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x - 1][y] -= 1;
				tempMap[x - 2][y] += 3;
			}
		}
		break;
	case 's':
		//如果下面是空地,如果下面是目的地
		if (tempMap[x + 1][y] == 0||tempMap[x+1][y]==4)
		{
			tempMap[x + 1][y] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的下面是箱子
		if (tempMap[x + 1][y] == 3 || tempMap[x + 1][y]==7)
		{
			//人要满足,推得动箱子得条件:箱子得下面是空地或者是目的地
			if (tempMap[x + 2][y] == 0 || tempMap[x + 2][y] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x + 1][y] -= 1;
				tempMap[x + 2][y] += 3;
			}
		}
		break;
	case 'a':
		//如果左面是空地,如果左面是目的地
		if (tempMap[x][y - 1] == 0 || tempMap[x][y-1]==4)
		{
			tempMap[x][y-1] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的左面是箱子
		if (tempMap[x][y-1] == 3 || tempMap[x][y - 1] == 7)
		{
			//人要满足,推得动箱子得条件:箱子得左面是空地或者是目的地
			if (tempMap[x][y-2] == 0 || tempMap[x][y - 2] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x][y - 1] -= 1;
				tempMap[x][y - 2] += 3;
			}
		}
		break;
	case 'd':
		//如果右面是空地,如果右面是目的地
		if (tempMap[x][y + 1] == 0 || tempMap[x][y + 1] == 4)
		{
			tempMap[x][y + 1] += 2;
			tempMap[x][y] -= 2;
		}
		//如果人的右面是箱子
		if (tempMap[x][y + 1] == 3 || tempMap[x][y + 1] == 7)
		{
			//人要满足,推得动箱子得条件:箱子得右面是空地或者是目的地
			if (tempMap[x][y + 2] == 0 || tempMap[x][y + 2] == 4)
			{
				tempMap[x][y] -= 2;
				tempMap[x][y + 1] -= 1;
				tempMap[x][y + 2] += 3;
			}
		}
		break;
	}
}
void isWin()
{
	//地图上没有发现箱子-3:所有的箱子都在目的地上
	int i = 0,j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < ROW; j++)
		{
			if (tempMap[i][j] == 3)//表示在空地上发现了箱子,说明肯定没有通关
			{
				return;
			}
		}
	}
	//如果整个循环都走了下来,没有箱子在地图上,表示当前关卡通过
	level++;//level值是3的时候,如果继续下一个关卡,会发生三维数组越界
	if (level == 3)
	{
		if (IDYES == MessageBox(GetHWnd(), "是否开始第一关", "提示", MB_YESNO))
		{
			level = 0;//从0开始
		}
		else
		{
			exit(0);//退出程序
		}
	}
	initMap();//加载新地图
}


int main()
{
	initgraph(500, 500);
	//把图片放进数组中
	loadPic();
	//把当前关卡的图片放进辅助地图中
	initMap();
	//贴图
	drawMap();

	while (1)
	{
		isWin();//判断是否通关
		cleardevice();//清屏
		drawMap();//贴图
		move();//人物移动
		cleardevice();//清屏
		drawMap();//贴图
	}
	closegraph();
	return 0;
}

小结:

如果你能耐心看到这里觉得有帮助的话不妨给我点个赞(这次一定),码字不易,您的支持是我创作的最大动力。下一期可能要推后几天了充分准备下来分享做简易版贪吃蛇的小项目,会拆分的更细一点,不会像这篇这么冗长,最后如果本篇有什么错误的地方,欢迎在评论区指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值