Easyx-----c++实现推箱子

  • 学会如何封装

  • 学会容器基本使用

  • 熟悉多文件开发

  • 学会数据与界面分离---封装的方式

公共的头文件 common.h 

//标准库中的大部分头文件
#pragma once
#include <iostream>
#include <map>
#include <array>
#include <stack>         //做回退
#include <string>        //字符串处理
#include <conio.h>       //按键处理
#include <graphics.h>
#include <mmsystem.h>    //音乐
#pragma comment(lib,"winmm.lib") //静态库
using namespace std;

管理资源 res.h ---单例设计模式

#pragma once
/*资源文件不需要重复new一个新对象<整个项目只需要有一份资源即可> ---> 提供一个
静态接口创建一个对象出来 ---> 需要的资源:图片/音乐文件 */
#include "common.h"
//单例设计模式
//1.构造函数私有化
//2.提供一个静态接口
class Res
{ 
public:
	static map<string, IMAGE*> img;  //资源文件外部需要直接使用,定义为static属性,不需要对象
	static map<string, string> music;//音乐
	static Res* getInstance();       //提供公有接口,返回类的对象 
	~Res();	//析构函数正常
private:
	Res();	//构造函数私有化
};

 res.cpp

#include "res.h"
map<string, IMAGE*> Res::img;    //静态数据成员在类外初始化,类名限定
map<string, string> Res::music;
//选中创建定义和声明
Res* Res::getInstance()
{
	static Res* res = new Res;/*单例模式:构建一个对象,这个对象要整体都能够使用,用成员函数
                      的方式去创建对象给类外使用,所有的资源都是用这一个对象,没有其他对象*/
	return res;               //返回对象
}
Res::~Res()
{
	delete img["墙"];    //析构函数负责释放对象
	delete img["路"];
	delete img["球"];
	delete img["框"];
	delete img["人"];
	delete img["鸡"];
}
Res::Res(){      /*构造函数私有化如何在类外使用? <在提供的公有接口中,提供静态成员函数,返
                   回类的对象,外部依然可以使用这个对象> */
//构造函数初始化资源---指针
	img["墙"] = new IMAGE;  //1
	img["路"] = new IMAGE;	//0
	img["球"] = new IMAGE;	//4
	img["框"] = new IMAGE;  //3
	img["人"] = new IMAGE;  //5 8
	img["鸡"] = new IMAGE;  //7
//加载图片
	loadimage(img["墙"], "res//img//1.bmp");//图片资源 路径 项目属性多字节 
	loadimage(img["路"], "res//img//0.bmp");
	loadimage(img["球"], "res//img//4.bmp");
	loadimage(img["框"], "res//img//3.bmp");
	loadimage(img["人"], "res//img//5.bmp");
	loadimage(img["鸡"], "res//img//7.bmp");
	music["背景"] = "res//music//back.mp3"; //音乐资源
	music["移动"] = "res//music//Boxmove.WAV";
}

数据处理 二维数组描述地图 data.h

#pragma once
#include "common.h"
class Data
{
public:
	void setValue(int i, int j, int value);//设置数据(地图中的值)protected属性提供公有接口
/*数据查找 找人物的坐标(用pair类型存储不需要定义结构体) 或用tuple存储 first:行数 second:列数*/
	pair<int, int> searchPos();           
	bool isBox(int i, int j);    //判断当前位置(行/列)是不是箱子
	bool isMove(int i, int j);   //判断当前位置(行/列)是否可以移动
	bool noBall();               //没有球游戏结束
	array<array<int, 8>, 8>& getMap();//在外面画图要访问数据,提供外部访问接口        
protected:
	array<array<int, 8>, 8> map= //8*8的地图二维数组类中初始化(只有1关)用数字描述整张地图
	{
		1,1,1,1,1,1,1,1,         //推箱子界面的改变本质是数组中的数据发生改变
		1,3,4,0,0,4,3,1,
		1,0,1,1,0,1,1,1,
		1,0,0,0,5,0,0,1,
		1,0,1,1,0,4,1,1,
		1,0,1,0,0,3,0,1,
		1,3,4,0,0,0,0,1,
		1,1,1,1,1,1,1,1
	};
};

data.cpp

#include "data.h"
void Data::setValue(int i, int j, int value)
{
	map[i][j] += value;           //人从篮筐走开,为了能还原篮筐 
}
pair<int, int> Data::searchPos()
{
	//人的位置    5和8的位置  
	for (int i = 0; i < 8; i++)  //二维数组的查找 为什么有8? 人站在篮筐上,要显示人
	{
		for (int j = 0; j < 8; j++)
		{
			if (map[i][j] == 5 || map[i][j] == 8)
				return { i,j };/*返回当前行/列  列表数据可以直接初始化容器,对于标准
                                 库中的容器可以直接返回列表数据 会自动把它创建成对象*/
		}
	}
	return {-1,-1}; //没找到返回-1,因为数组下标没有-1,没找到不能不返回,函数必须要有返回值
}
bool Data::isBox(int i, int j)
{
	//当前位置是球或者球在目的地上
    //球也有两种状态: 7:在篮筐(目的地)中 4:正常
	if (map[i][j] == 4 || map[i][j] == 7)
		return true;
	return false;
}
bool Data::isMove(int i, int j)
{
	//判断当前位置是不是空地
    //空地有两种情况: 3:是篮筐(目的地) 0:正常
	if (map[i][j] == 0 || map[i][j] == 3)
		return true;
	return false;
}
bool Data::noBall()       
{
	for (int i = 0; i < 8; i++) 
	{
		for (int j = 0; j < 8; j++) 
		{
			if (map[i][j] == 4)    //游戏结束判断--->找一下有没有球
				return true;
		}
	}
	return false;
}
array<array<int, 8>, 8>& Data::getMap()
{
	// TODO: 在此处插入 return 语句
	return map;
}

game.h      游戏逻辑由game.h/game.cpp完成

#pragma once
#include "common.h"
class Data;         //头文件用到类型,前项声明一下,不要用包含头文件的方式 
class Game 
{
public:
	Game();         //构造函数
	void DrawGame();//画地图
	void KeyDown(); //按键处理(玩游戏)
	bool GameOver();//游戏结束
protected:
	Data* pData;    //游戏需要用到数据,包含数据(类)的指针 前项声明有一个类即可
	stack<array<array<int, 8>, 8>> mstack;
};

game.cpp

#include "game.h"
#include "data.h"            //尽量在.cpp中互相包含,不要在.h中互相包含
#include "res.h"             //用到资源
Game::Game():pData(new Data) //构造函数,new数据
{
	initgraph(64 * 8, 64 * 8);    //创建窗口    8行,8列
	string open = "open " + Res::getInstance()->music["背景"];
	mciSendString(open.c_str(), 0, 0, 0);
	string play = "play " + Res::getInstance()->music["背景"];
	mciSendString(play.c_str(), 0, 0, 0);
}
void Game::DrawGame()        //根据数组去改变绘制
{
	for (int i = 0; i < 8; i++) 
	{
		for (int j = 0; j < 8; j++) 
		{
			int x = 64 * j;    //贴图坐标
			int y = 64 * i;
			switch(pData->getMap()[i][j])    //返回二维数组名,下标访问
			{
			case 0:				//路         //通过返回的对象指针,调用数据成员 贴图
				putimage(x, y, Res::getInstance()->img["路"]);
				break;
			case 1:				//墙         //绘制(把资源分开) <位置 哪一张图片>
				putimage(x, y, Res::getInstance()->img["墙"]);
				break;
			case 3:				//框
				putimage(x, y, Res::getInstance()->img["框"]);
				break;
			case 4:				//球
				putimage(x, y, Res::getInstance()->img["球"]);
				break;
			case 5:				//人
			case 8:
				putimage(x, y, Res::getInstance()->img["人"]);
				break;
			case 7:				//鸡
				putimage(x, y, Res::getInstance()->img["鸡"]);
				break;
			}
		}
	}
}
void Game::KeyDown()                                //按键处理
{
	pair<int, int> role = pData->searchPos();		//得到人物的坐标
	char userKey = _getch();                        //获取用户按键
//有按键按下,就有移动音乐    在播放前关闭
	string close = "close " + Res::getInstance()->music["移动"];
	mciSendString(close.c_str(), 0, 0, 0);
	string open = "open " + Res::getInstance()->music["移动"];
	mciSendString(open.c_str(), 0, 0, 0);
	string play = "play " + Res::getInstance()->music["移动"];
	mciSendString(play.c_str(), 0, 0, 0);
	switch (userKey)
	{
	case 'w':
	case 'W':
	case 72:
		mstack.push(pData->getMap());					//整个地图入栈
		if (pData->isMove(role.first - 1, role.second)) //判断是不是空地/篮筐--->能走
		{            
			pData->setValue(role.first, role.second, -5);    //原来位置-5
			pData->setValue(role.first - 1, role.second, 5); //新位置+5
		}                          //判断当前位置相邻位置是不是球,是球需要移动它
		if (pData->isBox(role.first - 1, role.second))
		{
			if (pData->isMove(role.first - 2, role.second))
			{
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first - 1, role.second, 1);
				pData->setValue(role.first - 2, role.second, 4);
			}
		}
		break;
	case 's':
	case 'S':
	case 80:
		mstack.push(pData->getMap());					//整个地图入栈
		if (pData->isMove(role.first + 1, role.second)) //上下左右移动
		{
			pData->setValue(role.first, role.second, -5);
			pData->setValue(role.first + 1, role.second, 5);
		}
		if (pData->isBox(role.first + 1, role.second))
		{
			if (pData->isMove(role.first + 2, role.second))
			{
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first + 1, role.second, 1);
				pData->setValue(role.first + 2, role.second, 4);
			}
		}
		break;
	case 'a':
	case 'A':
	case 75:
		mstack.push(pData->getMap());					//整个地图入栈
		if (pData->isMove(role.first, role.second - 1))
		{
			pData->setValue(role.first, role.second, -5);
			pData->setValue(role.first, role.second - 1, 5);
		}
		if (pData->isBox(role.first, role.second - 1))
		{
			if (pData->isMove(role.first, role.second - 2))
			{
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first, role.second - 1, 1);
				pData->setValue(role.first, role.second - 2, 4);
			}
		}
		break;
	case 'D':
	case 'd':
	case 77:
		mstack.push(pData->getMap());					//整个地图入栈
		if (pData->isMove(role.first, role.second + 1))
		{
			pData->setValue(role.first, role.second, -5);
			pData->setValue(role.first, role.second + 1, 5);
		}
		if (pData->isBox(role.first, role.second + 1))
		{
			if (pData->isMove(role.first, role.second + 2))
			{
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first, role.second + 1, 1);
				pData->setValue(role.first, role.second + 2, 4);

			}
		}
		break;
	case ' ':
		if (!mstack.empty()) 
		{
			pData->getMap() = mstack.top();
			mstack.pop();
		}
		break;    //做回退
	}	
}
bool Game::GameOver()
{
	return pData->noBall();
}
/*通过类的成员函数返回的对象指针调用数据成员 静态成员函数访问数据必须要通过对象去指定*/

 box.cpp        主函数部分

#include "game.h"
int main() 
{
	Game game;
	while (1) 
	{
		game.DrawGame();
		if (!game.GameOver())
			break;
		game.KeyDown();
	}
	return 0;
}

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiuqiuyaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值