使用共享模式以高效地支持大量的细粒度对象。
(摘自《游戏编程模式》)
该如何介绍共享模式呢?其实,“共享”一词就给出了答案。顾名思义,共享是指将一份作为共有部分供大多数运用。其实,我个人认为它的本质就是抽象化,即将一组对象的公共部分抽象起来并作为一个实例,这一实例将存储于所有对象中,供其使用。这样的抽象化使得重复的数据大大减少,从而提高了程序的运行效率。
简单的例子
在某个战争游戏中,我们需要将成千上万的士兵渲染至同屏中。使用享元模式便可以提高其运行效率,首先我们要做的就是找出士兵模型的共同点,例子如下:
- Mesh 网格
- Animation 动画
- Weapon 手持的武器
- Attack Target 攻击目标
我们需要将上述的共同点封装成一个类。并在士兵类中添加全局实例的指针——这就使得同一类士兵将公用一个SoldierModel实例。
class SoldierModel
{
public:
Mesh mesh;
Animtion playAnim;
WeaponType wp;
GameObject attackTarget;
}
class Soldier
{
private:
SoldierModel* sm;
Vector3 position;
//...
}
这就是享元模式。在此例子中,由于士兵的规模和公共部分数据较少,因此运行效率提升将不会那么直观。在《游戏编程模式》一书提到的森林渲染的例子将能更直观的体验到享元模式的魅力。
森林之树
在森林场景中,需要进行大量树的渲染。关于树的数据需要载入内存,这对内存是一大考验;除此之外,渲染树的数据也需要从CPU传递至GPU并在GPU进行图形绘制,而我们都知道,I/O一直是制约软件运行效率的一个最重要的因素。因此,在编程中,减少硬件与硬件之间传输数据量成为重中之重。
享元模式就成了很好的工具,我们可以将树分成两个独立的部分,并将所有数目通用的数据放到一个单独的类中。
在CPU向GPU传输数据这一过程中,无论是Direct3D还是OpenGL,都支持实例绘制,即传输两组数据——通用数据和差异化数据即可。
总结
享元模式关键字在于享,把一个对象的公共数据资源抽离成一个实例,因此所有的对象都可以公用这一个实例,减少了计算量。