成果展示
一、部分设计思想 1.用一个资源类去管理资源,采用单例设计模式:①构造函数私有化
②提供一个静态接口(通过类名直接访问资源)
(资源包括:图片音乐)
2.公共需要包含的头文件 common.h#pragma once #include3.静态数据成员要在类外进行初始化。 4.数据和绘图分离 5.整个游戏的逻辑由game完成#include
/改相似代码小技巧:alt+左键选中需要修改的那一部分,ctrl+h进行替换即可。/
6.利用栈,(按空格键)实现悔棋(回退)功能原理:利用栈,每次修改地图的放置之前,都将原地图存一份到栈中。当按下空格键,优先检测empty()的值(防止越界),然后若栈中的值不为空,则用top()将地图赋给getmap()然后pop删除。
二、代码部分:common.h主要包含:需要用到的一些容器的头文件,以及音乐播放、图形库等
#pragma once #include#include
res.h主要包含私有构造函数、静态的构造函数的接口、静态映射map容器来管理资源
#pragma once
#include"common.h"
class Res
{
public:
static map img;
static mapmusic;
static void drawIMG(int x, int y, string str);
static Res* getInstance();
~Res();
private:
Res();
};
res.cpp主要内容:通过构造函数实现对资源之间的映射关系(绑定)
细节:静态数据成员要在类外进行初始化。
#include "res.h" mapRes::img; map 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表示绘制的地图(0135分别对应不同的绘制)
②提供修改地图的接口setValue()、getMap()
③searchPos()用来返回角色所在的坐标({行,列})
④三个判断函数isBox()、isMove()、noBall()分别用来判断当前位置是否是box(篮球)、是否空地、以及是否还有球未进框(用来判断游戏是否需要结束)
#pragma once
#include"common.h"
class Data
{
public:
void setValue(int i, int j, int value); //设置地图中的值
pairsearchPos(); //找任务的坐标
bool isBox(int i, int j);
bool isMove(int i, int j);
bool noBall();
array, 8>& getMap();
protected:
array, 8> map
=
{
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
类型 ③isbox()在进行逻辑判断的时候,4和7都要算,因为当为7的时候(即球在篮筐上的时候也要保证是可以推的)
④isMove()保证0和3都算,因为篮筐在此我们认为属于空地的范畴
#include "data.h"
void Data::setValue(int i, int j, int value)
{
map[i][j] += value;
}
pair Data::searchPos()
{
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};
}
bool Data::isBox(int i, int j)
{
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)
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, 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, 8>>mstack;
};
game.cpp主要内容:①构造函数game()中创建出一个窗口,并播放音乐
②DrawGame()中根据pData中的数据成员中map的值分别对应到绘制相应的图形
③keydown()中主要实现空地移动的逻辑+推球移动的逻辑
细节:①构造函数的参数列表的pData直接new出来
#include "game.h"
#include"data.h"
#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:
putimage(x, y, Res::getInstance()->img["鸡"]);
break;
}
}
}
}
void Game::KeyDown()
{
pairrole = 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);
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);
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);
pData->setValue(role.first + 2, role.second, 4);
}
}
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);
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);
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容器部分的知识.
完整素材见个人主页的资源部分自行下载。



