成果展示

①构造函数私有化
②提供一个静态接口(通过类名直接访问资源)
(资源包括:图片\音乐)
- #pragma once
- #include<iostream>
- #include<map>
- #include<array>
- #include<stack>
- #include<conio.h>
- #include<graphics.h>
- #include<mmsystem.h>
- #pragma comment(lib,"winmm.lib")
/改相似代码小技巧:alt+左键选中需要修改的那一部分,ctrl+h进行替换即可。/
原理:利用栈,每次修改地图的放置之前,都将原地图存一份到栈中。当按下空格键,优先检测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容器部分的知识.
完整素材见个人主页的资源部分自行下载。