推箱子C++(多关卡)

推箱子C++(多关卡)

该项目用到了类和对象,封装,程序设计,并且运用了easyx库。该项目可在vc2010等编译器下运行,用的都是C++98标准(着实是被C98恶心到了,深刻感受到了C++11的好)。

1.下载easyx库

去他们官网下载EasyX,会下载一个可执行(.exe)程序,点击Alt
​​​​下一步。
在这里插入图片描述

选择你要安装的编译器,他就自动帮你配置好了。然后就可以直接使用了

调用这个库需要包含头文件

#include<graphics.h>

源文件后缀必须设为.cpp

2.推箱子原理

推箱子游戏的本质是通过不断改变数组的值,不断的贴图。

游戏设计

先用个简单的列子描述一下
在这里插入图片描述
拿这个二维数组举列,画出的地图是这样的在这里插入图片描述

判断输赢

要想游戏胜利,首先得把球放进框里,然后遍历整个数组,如果还有球(2),就不结束,如果球全消失了。则游戏胜利。
大致思路就是,推一次,遍历判断一次场上是否还有球。,贴一次图

接下来需要确定什么情况下能走动。

无论是人还是球,能走动的情况无非两种,一 前面是空地,二 前面是框
人前面如果是球,那先判断球是否能挪动,如果球能挪动,再挪动人
在这里插入图片描述
人先走,人前面有个球,判断球,球前面是框,满足移动的条件,那挪动球到框的位置,此时球的位置变成了空地,这时人也满足条件,挪动人,人原来的位置变为空地。

改变数组的值

要改变数组的值,是与挪动同时进行的。当球挪进框里,框的位置要加上球的值,球原来的位置要减掉球的值。在这里插入图片描述
人挪动,人当前位置要减掉人的值,挪动的新位置要加上人的值
在这里插入图片描述

3.类和对象大致介绍

类其实是个结构体,只不过这个结构体里面除了成员变量,还有成员函数。
对象就是这个结构体实列化出来的。像struct student lihua;
lihua就是对象。
类比为
类就想到于人类,人有眼睛,嘴巴,头发(成员变量),这些都是人都有的属性。人能吃饭,睡觉(这些行为相当于成员函数)。
对象就是具体到每一个人,张三,李四等。张三可以吃鸡蛋饭,李四可以吃牛肉饭。(调用同一种成员函数实现出不同的结果)其实就是传的函数参数不同,得到的结果也不同
张三眼睛可以是黑色,李四眼睛可以是蓝色。(他们都是有眼睛这一成员属性),颜色只不过是由我们自己设置改变的。

4.代码实现

首先创建一个头文件common.h,里面存放要用到的公共头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<map>
#include<string>
#include<vector>
#include<Windows.h>
#include<conio.h>//按键处理
#include<graphics.h>//图像
using namespace std;
//--------------------------------------------------------------------
//公共头文件
//————————————————————————————————————

接下来再创建一个资源头文件resource.h,里面写一个Res类。用它不断的贴图,由于这个类只用初始化一次,但会不停使用,所以可以把它成员设置为静态成员,但是因为我为了兼容VC2010(C++98标准),里面没有静态成员函数,以及把成员容器设为静态会报错。

class Res
{
public:
	 map<string, IMAGE*> img;//存图片名和图片(通过输入图片名获得图片)数据结构map
	 //map<string, string> music;
	Res()
	{
		//创建图片对象
		img["墙"] = new IMAGE; //1
		img["路"] = new IMAGE; //0
		img["鸡"] = new IMAGE; //5+3 = 8
		img["框"] = new IMAGE; //3
		img["鲲"] = new IMAGE; //7 或 7+3 = 10
		img["球"] = new IMAGE; //5
		//加载图片
		loadimage(img["墙"], L"picture-img//1.bmp");//这里在输入地址前必须加个L,否则就必须在配置属性下将字符型改为多字符
		loadimage(img["路"], L"picture-img//0.bmp");//字符串的实际存储有多种编码格式,
		loadimage(img["鸡"], L"picture-img//8.bmp");//如果默认的编码格式和实际执行的平台不相符就会发生错误。
		loadimage(img["框"], L"picture-img//3.bmp");// 或者改为_T(“abc”) 表示。就是加上 _T(“”)。
		loadimage(img["鲲"], L"picture-img//7.bmp");
		loadimage(img["球"], L"picture-img//5.bmp");
	}

	//Res* GetRes()
	//{
	//	
	//	//构建一个对象。这个对象整体成员都能使用
	//	
	//}
	//释放
	void DeleteRes()
	{
		
	}
	~Res() {
		delete img["墙"];
		delete img["路"];
		delete img["鸡"];
		delete img["框"];
		delete img["鲲"];
		delete img["球"];
	}

	
};

我这里用到了map这个容易,map本质是一颗搜索二叉树的封装。他的数据结构是这样的
在这里插入图片描述
_first记录的是第一个传入的值,这里我们输入汉字“鸡”
_second记录传入图片鸡的地址
于是当我们输入“鸡”这个汉字时,它会先在这颗二叉树里面找"鸡“这个汉字(是与_first比较),找到后,它会返回_second这张图片的地址。
在这里插入图片描述
我这里是把墙用1来表示,路用0来表示,球用5来表示,框用3来表示,鸡就是(球+框)8,人有两种状态,站在空地上7,站在框位置(7+3)。
一个游戏肯定要存数据,于是我们便写个Data类来存放地图数据,顺道在这个类中实现
判断是否可以移动,判断是否赢了,设置改变数组值的函数,判断人要移动的那个位置是否是球。
对了我们最开始是不知道人事在什么位置的,所以我们还得写个找人函数,获取人的x,y值。

先把data类的代码贴出来
data.h

#pragma once
#include"common.h"
class Data
{
	public:
	bool IsMove(int i,int j,vector<vector<int>>& Mapn);//判断是否能移动
	bool NoBall(vector<vector<int>>& Mapn);//判断是否还有球,判断游戏输赢
	bool IsBall(int i,int j, vector<vector<int>>& Mapn);
	void SetValue(int i, int j, int value, vector<vector<int>>& Mapn);
	pair<int,int> SearchPerson(vector<vector<int>>& Mapn);
	vector<vector<int>>& GetMapn(int i)//相当于返回指针
	{
		return MapArr[i];
	}
	Data()
{
	InitMap();
	MapArr.resize(5);
	MapArr[0] = MapArr1;
	MapArr[1] = MapArr2;
	MapArr[2] = MapArr3;
	MapArr[3] = MapArr4;
	MapArr[4] = MapArr5;
}
	/*int aMap1[14][16] = {
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0},
		{0,0,0,0,1,1,1,0,0,0,0,1,0,0,0,0},
		{0,0,0,1,1,3,5,0,1,1,0,1,1,0,0,0},
		{0,0,0,1,0,3,5,7,5,0,0,3,1,0,0,0},
		{0,0,0,1,0,3,0,5,0,5,3,1,1,0,0,0},
		{0,0,0,1,1,1,1,1,1,0,0,1,0,0,0,0},
		{0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	};*/
	
	void InitMap();
	
private:
	vector<vector<vector<int>>> MapArr;//int[][][]
	vector<vector<int>> MapArr1;//int[][]
	vector<vector<int>> MapArr2;
	vector<vector<int>> MapArr3;
	vector<vector<int>> MapArr4;
	vector<vector<int>> MapArr5;

};
地图

在这里插入图片描述
这个vector可以理解一维数组。
于是我创建5个二维数组存放地图。
MapArr存放二维数组的地址。
在这里插入图片描述
这里就是将int[][]数据放入vector<vector>中,(原本我是可以对vector处理像使用int[][]一样初始化的,但由于按照C++98标准,于是便麻烦多了)。
当把五个地图初始化好后,再将这五个地图地址放入MapArr中
在这里插入图片描述

判断输赢

在这里插入图片描述
传入地图关卡地址,然后对这个地图遍历一遍,如果没有球5,就是赢了。
在这里插入图片描述

是否可以移动

无论是人还是球,只有要移动的位置是空地0或者框3才能移动。
在这里插入图片描述

是否是球

在这里插入图片描述

设置值

在这里插入图片描述

找人的位置

也是遍历一遍,找到人,返回坐标。这里人有两种状态,7或10
在这里插入图片描述
众所周知,返回值只能返回一个值,于是我们设计一个结构体,这个结构体有两个变量。一个变量放x,一个变量放y。
在这里插入图片描述

获取地图

在这里插入图片描述

资源,数据既然完成,接下来就是游戏逻辑。

游戏逻辑

创建一个游戏类,这个类成员一个是资源,一个是数据。
在这里插入图片描述
这两个类的指针,以便获取他们的成员变量及成员函数
接下来就是开辟两块空间,以及初始化画布
在这里插入图片描述
刚进入游戏我们肯定要贴图,接下来就是按键移动,移动一次后判断是否结束
在这里插入图片描述

贴图函数
void Game::DrawMap(vector<vector<int>>& Mapn)
{
	
	for (int i = 0; i < 14; i++)
	{
		for (int j = 0; j < 16; j++)
		{
			int x = j * 64;//因为easyx横纵坐标是以右下角为原点
			int y = i * 64;

			switch(Mapn[i][j])//传那张地图的指针过来,并根据数据贴图
			{
			case 0:
				putimage(x, y, res->img["路"]);
				break;
			case 1:
				putimage(x, y, res->img["墙"]);
				break;
			case 8:
				putimage(x, y, res->img["鸡"]);
				break;
			case 3:
				putimage(x, y, res->img["框"]);
				break;
			case 7:
			case 10:
				putimage(x, y, res->img["鲲"]);
				break;
			case 5:
				putimage(x, y, res->img["球"]);
				break;
			}
		}
	}
	char s[] = "按0返回主界面";
    outtextxy(10, 20, s);
}

依旧是一次遍历,这里还可以用if实现,但if看起来太乱了,于是我用了switch
这里要注意一点x,y。因为easyx画布坐标是
在这里插入图片描述
所以x = j*64图片大小,有 y = i*64;
这里大小根据贴图图片大小设置,查看图片大小则是选中图片,右键属性里面查看。

按键移动

这步是玩游戏的核心
首先得把地图传过来
在这里插入图片描述
引用(有点像传指针)
然后调用找人函数,获取角色坐标。
用_getch()获取移动值,为什么用_getch(),不用getchar()
因为getchar()函数在读入一个字符时必须按一下Enter键,代码才会继续运行。
获得字符后进行以下判断
在这里插入图片描述
首先先判断人是否可以移动,调用移动函数。如果能移动,改变数组里面的值。
在这里插入图片描述
如果不能移动,说明前面有墙,有墙不执行操作,有球,就先判断球是否能移动,球能移动的话,先移动球,在这里插入图片描述
再移动人,移动伴随着改变值。
在这里插入图片描述
这里返回一个bool值是为了,当用户输入0时,返回主界面
在这里插入图片描述

判断游戏是否结束

直接调用Noball函数就行
在这里插入图片描述

主界面

在这里插入图片描述
最初写成这样就可以直接玩第一关,但我们写的是多关卡,于是要有一个看得过去的界面。
先写一个菜单函数
在这里插入图片描述
在这里插入图片描述
子菜单
在这里插入图片描述
这里我着重写一下多关卡菜单的核心代码
在这里插入图片描述
这个就是从第一关开始玩,
这个BeginGame我套了两层循环,第一层循环是依次执行每个地图。
第二层循环是判断每个地图是否通关
在这里插入图片描述
已知KeyDown按0返回真,于是我们设置一个标志位flag在第一层循环里判断,用户是否主动跳出。在这里插入图片描述
在这里插入图片描述
这里核心是如果用户不主动退出,当选择关卡时会依次从选择的关卡往下开始。因为会返回一个false,switchl里面的if(),就不会执行那个break.但如果用户按0,则会返回true,然后跳出这个switch显示菜单。这里是环环相扣的。
到此为止,推箱子多关卡代码全部完成。
在这里插入图片描述
在这里插入图片描述
游戏截图
游戏图片及源码我上传到了推箱子

本人C++菜鸡一个,如有错误,还请斧正

感谢阅读

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值