游戏编程模式:轻量级(Flyweight)模式(Part I)

        雾气升起了,显露出一片茂密的成熟生态的森林。数不胜数的古老的铁杉树耸立在你面前,形成由绿叶组成的大教堂。树叶组成的着色玻璃穹顶将阳光弄碎成一条条金色的迷雾。在巨大的树干之间,你可以看出广袤的森林在远处渐渐模糊。

        这就是我们作为游戏开发人员所梦想的超出现实的设定,而像这样的场景通常因这样一种其名字可能不能够再谦虚的模式而变得可能:轻量级(Flyweight)。

1、Forest for the Trees

        我可以仅仅使用几句话来描述一个杂草丛生的森林地带,但是实际地实时地实现它就是另一码事了。当你得到充满屏幕的由一棵棵树组成的整片森林的时候,一个图形程序员看到的是上百万个多边形,并且他们得要每1/60秒把这些多边形扔进GPU里。

        我们在谈论成千上万棵树,每一棵都有一个详细的包含着上千个多边形的表面形状(geometry)。即使你有足够大的内存来描述这片森林,为了渲染它,这些数据还得make its way over the bus from the CPU to the GPU.

        每一棵树都有与之相关的一堆数据:

  • 定义树干、树枝和树叶的形状的、由多边形组成的网格。
  • 树皮和树叶的贴图。
  • 它在森林中的位置和朝向。
  • 类似尺寸和着色等使得每棵树看起来不一样的调节参数。

        如果你想要用代码打个草稿的话,你会得到类似下面的东西:

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

        这数据很大,并且网格和贴图尤其地大。由这些对象组成的整个森林太大了,以至于不能够在一帧的时间里扔进GPU里。幸运的是,有一个确立已久的(time-honored)窍门来处理这个问题。

        主要的观察结论是,即使森林中可能有上千棵树,它们大部分看起来很相似。它们可能都使用同样的网格和贴图。这意味着这些对象的大部分字段在所有的实例中都是一样的

        如果你打算出钱给艺术家们为一片森林中的每一棵树单独建模的话,那么要么你是疯子,要么你是亿万富翁。

游戏编程模式:轻量级(Flyweight)模式(Part I)_第1张图片

        注意小盒子中的东西对于每一棵树来说都是一样的。

        我们可以通过将上述对象切成两半的方式对其显式地建模。首先,我们拿出所有的树都共有的数据,将其放在另外一个类里面:

class TreeModel
{
private:
  Mesh mesh_;
  Texture bark_;
  Texture leaves_;
};
        游戏只需要一份上述数据,毕竟没有理由让同一个网格和贴图在内存中加载几千次。然后,世界中每一个树的实例有一个对那个共享的TreeModel的引用。然后在Tree类中留下的就是依赖于实例的状态了:
class Tree
{
private:
  TreeModel* model_;
  Vector position_;
  double height_;
  double thickness_;
  Color barkTint_;
  Color leafTint_;
};

        你可以这样将其形象化:

游戏编程模式:轻量级(Flyweight)模式(Part I)_第2张图片

        这看上去很像Type Object模式。二者都涉及将一个对象的部分状态委托(delegate给某个被很多实例所共享的其他对象。然而,这两种模式的目的不一样。

        对于一个type object,目的是通过将“类型”提升(lift)到你自己的对象模型中,以最小化你必须要定义的类的数目。你从中获得的内存共享是一个额外奖励。而轻量级模式则是完全关于效率的。

        这很好,对于在主内存(main memory)中存储东西不错,但是这对于渲染来说无济于事。在森林出现在屏幕上之前,它得努力地走到GPU里面。我们需要以一种图形卡能够理解的方式来表达这种资源共享。


你可能感兴趣的:(游戏开发,C++,设计模式)