一、定义
享元模式的定义:
运用共享技术来有效的支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式主要包含以下角色:
(1)抽象享元角色:所有具体享元类的基类,为具体享元规范需要实现的公共接口。
(2)具体享元角色:实现抽象享元角色中所规定的的接口。
(3)非享元角色:是不可以共享的外部状态。
(4)享元工厂角色:负责创建和管理享元角色。
其结构如下图所示:
该部分主要参考:享元模式(详解版),详情请点击该链接。
二、创建世界功能
如果我们希望创建一个游戏世界,那个可以能会有草地、丘陵、河流等很多地形。为了方便我们便基于区块来建立地形表:将世界划分为由微小区块组成的巨大网格,每个区块都被一种地形覆盖。
地形有以下特性:
(1)移动开销
(2)是否可通行
(3)用于渲染的纹理。
由此,我们直接声明一个Terrain类,来保存这些数据。
在创建地形表的时候,如果我们选择1000*1000的地形,为每个位置都创建一个地形对象,便会占用大量的时间和内存(创建对象消耗的时间)。此外,我们可以发现这些创建的地形对象大部分都是重复的,其唯一的区别是地形在哪里。使用享元的术语来说,就是地形的所有状态都是“上下文无关的”。因此对于这些地形对象,我们进行共享。使用享元模式和不使用享元模式的性能差距还是很大的。
我们计算一下使用享元模式和不使用享元模式,64位系统下,100*100的地图的花销上的不同。
如:
共有部分介绍:
Terrain类的三个属性分别为int、bool、char三种类型,其大小为:8字节。
创建一个Terrain对象,要0.01ms。
地形表中有10000个指针,总大小为:80000字节。
不使用享元模式:
创建10000个对象,占用内存:80000字节,时间消耗:100ms
使用享元模式:
由于本文中只有三种地形,因此只需3个对象,占用内存:24字节,时间消耗:0.03ms。
可以看见在100100的地图上,使用享元和不使用享元的差距都如此之大,如果是10001000的地图呢?10000*10000呢?
详情请点击以下链接:
游戏设计模式-享元模式
三、具体代码实现
3.1、前置准备
导入头文件和设置地图大小。
/*
作者:lbb
版本:v0.1
更改时间:2021/10/21
*/
#include<iostream>
//世界大小
const int HEIGHT=10;
const int WIDTH=20;
3.2、地形类
声明三个属性特性,提供获取属性函数。具体享元类。
//地形类
class Terrain{
public:
//构造函数
Terrain(int movementCost,bool isWater,const char texture):movementCost_(movementCost),isWater_(isWater),texture_(texture){}
//获取属性函数
int GetMovementCost(){return movementCost_;}
int IsWater() const {return isWater_;}
const char& GetTexture()const{return texture_;}
private:
int movementCost_;//移动花费
bool isWater_;//是否是墙
char texture_;//贴图
};
3.3、世界类
初始化三种地形,随机生成世界地形,存储地形表。变相的享元工厂类。
//世界类
class World{
public:
//创建时初始化三种地形
World():grassTerrain_(1,false,' '),
hillTerrain_(2,false,'^'),
riverTerrain_(3,true,'w')
{
GenerateTerrain();
}
//生成世界地形
void GenerateTerrain();
//获取(x,y)的地区
const Terrain& GetTile(int x,int y)const;
private:
Terrain grassTerrain_;//草原地形
Terrain hillTerrain_;//丘陵地形
Terrain riverTerrain_;//河流地形
Terrain* tiles_[HEIGHT][WIDTH];//世界地形数组
};
void World::GenerateTerrain(){
for(int i=0;i<HEIGHT;i++)
{
for(int j=0;j<WIDTH;j++)
{
if(rand()%10==0){
tiles_[i][j]=&hillTerrain_;
}
else{
tiles_[i][j]=&grassTerrain_;
}
}
}
int i=rand()%HEIGHT;
for(int j=0;j<WIDTH;j++)
{
tiles_[(i-1)%HEIGHT][j]=&riverTerrain_;
tiles_[i][j]=&riverTerrain_;
tiles_[(i+1)%HEIGHT][j]=&riverTerrain_;
}
}
const Terrain& World::GetTile(int x,int y) const{
return *tiles_[x][y];
}
3.4、主函数
输出游戏世界
int main(void)
{
World myWorld;
//输出世界
for(int i=0;i<HEIGHT;i++)
{
for(int j=0;j<WIDTH;j++)
{
std::cout<<myWorld.GetTile(i,j).GetTexture();
}
std::cout<<std::endl;
}
return 0;
}
四、输出结果
输出截图如下: