Day16:C++之STL应用篇(推箱子cxk限定)

目录

一、部分设计思想

1.用一个资源类去管理资源,采用单例设计模式:

2.公共需要包含的头文件 common.h

3.静态数据成员要在类外进行初始化。

4.数据和绘图分离

5.整个游戏的逻辑由game完成

6.利用栈,(按空格键)实现悔棋(回退)功能

二、代码部分:

common.h

 res.h

 res.cpp

 data.h

 data.cpp

 game.h

game.cpp

 box.cpp(main)

 三、写在最后


成果展示

一、部分设计思想

1.用一个资源类去管理资源,采用单例设计模式:

构造函数私有化

②提供一个静态接口(通过类名直接访问资源)

(资源包括:图片\音乐)

2.公共需要包含的头文件 common.h

#pragma once
#include<iostream>
#include<map>
#include<array>
#include<stack>
#include<conio.h>
#include<graphics.h>
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")

3.静态数据成员要在类外进行初始化。

4.数据和绘图分离

5.整个游戏的逻辑由game完成

/改相似代码小技巧:alt+左键选中需要修改的那一部分,ctrl+h进行替换即可。/

6.利用栈,(按空格键)实现悔棋(回退)功能

            原理:利用栈,每次修改地图的放置之前,都将原地图存一份到栈中。当按下空格键,优先检测empty()的值(防止越界),然后若栈中的值不为空,则用top()将地图赋给getmap()然后pop删除。

二、代码部分:

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

        主要包含私有构造函数、静态的构造函数的接口、静态映射map容器来管理资源

#pragma once
#include"common.h"

class Res
{
public:
	static map<string, IMAGE*> img;
	static map<string, string>music;
	static void drawIMG(int x, int y, string str);
	static Res* getInstance();
	~Res();
private:
	Res();
};

 res.cpp

        主要内容:通过构造函数实现对资源之间的映射关系(绑定)

        细节:静态数据成员要在类外进行初始化。

#include "res.h"

/*静态数据成员要在类外初始化*/
map<string, IMAGE*>Res::img;
map<string, string>Res::music;

void Res::drawIMG(int x, int y, string str)
{
    putimage(x, y, getInstance()->img[str]);
}

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;//0
    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

        主要内容:①数据成员map表示绘制的地图(0\1\3\5分别对应不同的绘制)

                       ②提供修改地图的接口setValue()、getMap()

                       ③searchPos()用来返回角色所在的坐标({行,列})

                       ④三个判断函数isBox()、isMove()、noBall()分别用来判断当前位置是否是box(篮球)、是否空地、以及是否还有球未进框(用来判断游戏是否需要结束)

#pragma once
#include"common.h"

class Data
{
public:
	void setValue(int i, int j, int value);			//设置地图中的值
	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,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,0,1,1,
		1,0,1,0,0,4,0,1,
		1,3,4,0,0,3,0,1,
		1,1,1,0,0,0,1,1
	};

};

 data.cpp

        主要内容:相关成员函数的实现过程。

               细节:①setValue的时候采用+=的算法,就是为了好让人从篮筐中走开时,还原篮筐

                       ②pair键值对的返回类型的写法{ i, j }就已经表示一个pair<int,int>类型

                       ③isbox()在进行逻辑判断的时候,4和7都要算,因为当为7的时候(即球在篮筐上的时候也要保证是可以推的)

                       ④isMove()保证0和3都算,因为篮筐在此我们认为属于空地的范畴                      

#include "data.h"

void Data::setValue(int i, int j, int value)
{
	map[i][j] += value;/*注意:这里为何是+=而非=,因为人从篮筐走开,是需要还原篮筐的。*/
}

pair<int, int> Data::searchPos()
{
	/*5和8->显示人   ques:为何8也是人,因为人可能是站在篮筐上,此时map中对应的值对应8*/
	for (int i = 0; i < 8; i++)
	{
		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)
{/*球=箱子*/

	/*当前位置是箱子or箱子在篮筐上*/
	if (map[i][j] == 4 || map[i][j] == 7)
		return true;
	
	return false;
}

bool Data::isMove(int i, int j)
{
	/*判断当前位置是不是空地*/
	if (map[i][j] == 0 || map[i][j] == 3)  /*0是空地,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()
{

	return map;//通过接口访问map
}

 game.h

        主要内容:构造函数、绘制函数、按键捕捉函数、判断游戏结束+

                                Data类型的指针、一个8*8的栈

               细节:在头文件中用到的自定义类型,尽量使用前向声明的方式(防止交叉包含)

#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

        主要内容:①构造函数game()中创建出一个窗口,并播放音乐

                        ②DrawGame()中根据pData中的数据成员中map的值分别对应到绘制相应的图形

                        ③keydown()中主要实现空地移动的逻辑+推球移动的逻辑

               细节:①构造函数的参数列表的pData直接new出来

#include "game.h"
#include"data.h"/*不要在头文件中互相包含(用前向声明)!!!,尽量在.cpp中互相包含*/
#include"res.h"

Game::Game():pData(new Data)
{
	initgraph(64 * 8, 64 * 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:				/*3+4=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["移动"]+"wait";
	mciSendString(play.c_str(), 0, 0, 0);

	/*注:此处是不用while(1)持续画的,只有当按键后,才需要改变*/
	switch (userkey)
	{
	case 'w':
	case 'W':
	case 72:
		if (pData->isMove(role.first - 1, role.second))		//检测行-1的位置(即向上走)是否为空地
		{
			mstack.push(pData->getMap());
			pData->setValue(role.first, role.second ,- 5);	//原来的位置要-5,显示空地or篮筐
			pData->setValue(role.first - 1, role.second, 5);//w向上移动,+5显示人物
		}
		if (pData->isBox(role.first - 1, role.second))		/*若行进方向有箱子*/
		{
			if (pData->isMove(role.first - 2, role.second))
			{
				mstack.push(pData->getMap());
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first-1, role.second, 1);/*为啥是1:球走-4  人来+5  =1*/
				pData->setValue(role.first - 2, role.second, 4);
			}
		}
		break;
	case 'S':
	case 's':
	case 80:
		if (pData->isMove(role.first + 1, role.second))
		{
			mstack.push(pData->getMap());
			pData->setValue(role.first, role.second, -5);	//原来的位置要-5,显示空地or篮筐
			pData->setValue(role.first +1, role.second, 5);//S向down移动,+5显示人物
		}
		if (pData->isBox(role.first + 1, role.second))
		{
			if (pData->isMove(role.first + 2, role.second))
			{
				mstack.push(pData->getMap());
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first + 1, role.second, 1);/*为啥是1,球走-4  人来+5  =1*/
				pData->setValue(role.first + 2, role.second, 4);
			}
		}														/*改相似代码小技巧:alt+左键选中需要修改的那一部分,ctrl+h进行替换即可。*/
		break;
	case 'a':
	case 'A':
	case 75:
		if (pData->isMove(role.first , role.second-1))
		{
			mstack.push(pData->getMap());
			pData->setValue(role.first, role.second, -5);	//原来的位置要-5,显示空地or篮筐
			pData->setValue(role.first , role.second-1, 5);//a向left移动,+5显示人物
		}
		if (pData->isBox(role.first, role.second-1))
		{
			if (pData->isMove(role.first , role.second-2))
			{
				mstack.push(pData->getMap());
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first , role.second-1, 1);/*为啥是1,球走-4  人来+5  =1*/
				pData->setValue(role.first , role.second-2, 4);
			}
		}
		break;
	case 'd':
	case 'D':
	case 77:
		if (pData->isMove(role.first , role.second+1))
		{
			mstack.push(pData->getMap());
			pData->setValue(role.first, role.second, -5);	//原来的位置要-5,显示空地or篮筐
			pData->setValue(role.first , role.second+1, 5);//d向right移动,+5显示人物
		}
		if (pData->isBox(role.first , role.second+1))
		{
			if (pData->isMove(role.first , role.second+2))
			{
				mstack.push(pData->getMap());
				pData->setValue(role.first, role.second, -5);
				pData->setValue(role.first , role.second+1, 1);/*为啥是1,球走-4  人来+5  =1*/
				pData->setValue(role.first , role.second+2, 4);
			}
		}
		break;
	case ' ':													/*当空格键被按下*/
		if (!mstack.empty())
		{
			pData->getMap() = mstack.top();
			mstack.pop();
		}
	}
}

bool Game::GameOver()
{
	return pData->noBall();
}

 box.cpp(main)

#include"game.h"
int main()
{
	Game game;
	game.DrawGame();
	while (1)
	{
		game.DrawGame();
		if (!game.GameOver())
			break;
		game.KeyDown();
	}
	cout << "恭喜你,通关了!" << endl;
	return 0;
}

 三、写在最后

        巩固了stl容器部分的知识.

                完整素材见个人主页的资源部分自行下载。   ​​​  ​
C++推箱子https://download.csdn.net/download/zjjaibc/85765465?spm=1001.2014.3001.5503

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_Ocean__

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

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

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

打赏作者

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

抵扣说明:

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

余额充值