您的位置:首页 > 其它

游戏中的设计模式:享元模式

2017-04-28 22:59 288 查看
Flyweight Pattern : Use sharing to support large numbers of fine-grained objects efficiently.

导语

  如果一个软件系统在运行时所创建的相同或者相似的对象数量太多,将导致运行代价过高,带来系统资源浪费、性能下降等问题。在游戏开发中,这也是一个值得关注的问题,游戏的性能会直接影响玩家的游戏体验。

  假设在你的游戏中有一片森林,森林是由成千上万棵树形成,每棵树都有这样几个部分:

用来规定树干和树叶的形状的网格模型。

树干和树叶的纹理。

这棵树在树林中的位置。

用来调整尺寸和色调的参数,以使得每棵树看起来都不一样。

如果用下面的代码来实现树的类

public class Tree {
private Mesh mesh_;
private Texture bark_;
private Texture leaves_;
private Vector3 position_;
private double height_;
private double thickness_;
private Color barkTint_;
private Color leafTint_;
}


  用草图来表示就是这样的,每一棵树都有着自己的网格模型,纹理,以及其他数据,也就是说游戏中有一千棵树,你的内存中就有一千棵树的数据、网格模型和纹理。用这些去构成一个森林的话,GPU在一帧内所需处理的东西就太多了。



  事实上,没有多少游戏开发者会这样做。虽然森林里有成千上万棵树,但是其实它们看起来都差不多。它们可能使用了相同的网格模型和纹理。这意味着这些树对象中的大部分属性在它们的实例中都是相同的。



定义

  享元模式:运用共享技术有效地支持大量细粒度对象的复用。

结构

  享元模式结构较为复杂,通常结合工厂模式一起使用,在它的结构图中包含一个享元工厂类,结构如下图



Flyweight(抽象享元类):它通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法设置外部数据(外部状态)。

ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象,并在具体享元类中为内部状态提供了存储空间。通常可以结合单例模式来设计具体享元类,为每一个具体享元对象提供唯一的享元对象。

UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,用户可以将不能被共享的子类设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建。

FlyweightFactory(享元工厂类):享元工厂类用于创建并管理Flyweight对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合。当用户请求一个Flyweight时,FlyweightFactory就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)并将新建的对象存储在享元池中。

实现

  考虑到游戏中的森林会使用相同的网格模型,树干及树叶纹理。我们可以为这些需要共用的属性新建一个类
TreeModel
,这样每一种类型的树就共用一种
TreeModel


public class TreeModel {
private Mesh mesh_;
private Texture bark_;
private Texture leaves_;
}


抽象享元类:

public interface ITree {
void setParam(Vector3 pos, double h, double t, Color bTint, Color lTint);
}


具体享元类:

public class Tree : ITree {
private TreeModel model_;
private Vector3 position_;
private double height_;
private double thickness_;
private Color barkTint_;
private Color leafTint_;

public Tree() {
//default constructor
}

public Tree(TreeModel mod, Vector3 pos, double h, double t, Color bTint, Color lTint) {
model_ = mod;
setParam(pos, h, t, bTint, lTint);
}

//设置树的外部参数
public void setParam(Vector3 pos, double h, double t, Color bTint, Color lTint) {
this.position_ = pos;
this.height_ = h;
this.thickness_ = t;
this.barkTint_ = bTint;
this.leafTint_ = lTint;
}
}


享元工厂类:

public class TreeFactory : System.Object {

private static TreeFactory instance;
//使用Hashtable存储TreeModel,实现享元池
private Hashtable TreeModels = new Hashtable ();

public static PatrolFactory getInstance () {
if (instance == null) {
instance = new PatrolFactory ();
}
return instance;
}

public Tree getTree (string mod, TreeModel mod, Vector3 pos, double h, double t, Color bTint, Color lTint) {
if (TreeModels.ContainsKey(mod)) {
//如果mod存在,则直接在享元池中获取对应的TreeModel,用它创建Tree
TreeModel model_ = (TreeModel)TreeModels[mod];
Tree temp = new Tree(model_, pos, h, t, bTint, lTint);
} else {
//如果mod不存在,先创建一个新的对象添加到享元池中,然后用它创建Tree
TreeModel model_ = new TreeModel();
TreeModels.Add(mod, model_);
Tree temp = new Tree(model_, pos, h, t, bTint, lTint);
}
return temp;
}
}


  这些代码用一张图来表示就像这样,通过把较大的网格模型和纹理图片共享,节约了内存空间,提高游戏性能。



更多

单纯享元模式与复合享元模式……

优缺点

优点:

享元模式可以减少内存中对象的数量,使得相同或者相似的对象在内存中只保存一份,从而可以节约系统资源,提高系统性能

在享元模式中,外部状态相对独立,而且不会影响到其内部状态,这样使享元对象可以子啊不同环境中被共享

缺点:

享元模式使系统变得复杂,需要分离内部状态和外部状态,这让程序的逻辑复杂化

参考:http://gameprogrammingpatterns.com/flyweight.html

ab21
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息