-
学会如何封装
-
学会容器基本使用
-
熟悉多文件开发
-
学会数据与界面分离---封装的方式
公共的头文件 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;
}