游戏编程模式:组件模式

组件模式。

模式动机:

允许单一的实体跨越多个领域而不会导致这些领域彼此耦合。

单一实体跨越了多个领域。为了保持领域之间相互分离,将每部分代码放入各自的组件类中。 实体被简化为组件的容器

使用场景:

组件通常在定义游戏实体的核心部分中使用,但它们在其他地方也有用。 这个模式应用在在如下情况中:

  • 有一个涉及了多个领域的类,而你想保持这些领域互相隔离。
  • 一个类正在变大而且越来越难以使用。
  • 想要能定义一系列分享不同能力的类,但是使用继承无法让你精确选取要重用的部分。

设计决策:

对象如何获取组件:

  • 如果对象创建组件:

    • 这保证了对象总是能拿到需要的组件。 你永远不必担心某人忘记连接正确的组件然后破坏了整个游戏。容器类自己会处理这个问题。
    • 重新设置对象比较困难。 这个模式的强力特性之一就是只需重新组合组件就可以创建新的对象如果对象总是用硬编码的组件组装自己,我们就无法利用这个特性。
  • 如果外部代码提供组件:

    • 对象更加灵活。 我们可以提供不同的组件,这样就能改变对象的行为。 通过共用组件,对象变成了组件容器,我们可以为不同目的一遍又一遍地重用它。
    • 对象可以与具体的组件类型解耦。

组件之间如何通信:

  • 通过修改容器对象的状态:

    • 保持了组件解耦。 当我们的InputComponent设置了Bjorn的速度,而后PhysicsComponent使用它, 这两个组件都不知道对方的存在。在它们的理解中,Bjorn的速度是被黑魔法改变的。

    • 需要将组件分享的任何数据存储在容器类中。 通常状态只在几个组件间共享。比如,动画组件和渲染组件需要共享图形专用的信息。 将信息存入容器类会让所有组件都获得这样的信息。

      更糟的是,如果我们为不同组件配置使用相同的容器类,最终会浪费内存存储不被任何对象组件需要的状态。 如果我们将渲染专用的数据放入容器对象中,任何隐形对象都会无益地消耗内存。

    • 这让组件的通信基于组件运行的顺序。 在同样的代码中,原先一整块的update()代码小心地排列这些操作。 玩家的输入修改了速度,速度被物理代码使用并修改位置,位置被渲染代码使用将Bjorn绘制到所在之处。 当我们将这些代码划入组件时,还是得小心翼翼地保持这种操作顺序。

      如果我们不那么做,就引入了微妙而难以追踪的漏洞。 比如,我们更新图形组件,就错误地将Bjorn渲染在他上一帧而不是这一帧所处的位置上。 如果你考虑更多的组件和更多的代码,那你可以想象要避免这样的错误有多么困难了。

  • 通过它们之间相互引用

    • 简单快捷。 通信是一个对象到另一个的直接方法调用。组件可以调用任一引用对象的方法。做什么都可以。
    • 两个组件紧绑在一起。 这是做什么都可以带来的坏处。我们向使用整块类又退回了一步。 这比只用单一类好一点,至少我们现在只是把需要通信的类绑在一起。
  • 通过发送消息:

    • 这是最复杂的选项。我们可以在容器类中建小小的消息系统,允许组件相互发送消息。
    • 同级组件解耦。 通过父级容器对象,就像共享状态的方案一样,我们保证了组件之间仍然是解耦的。 使用了这套系统,组件之间唯一的耦合是它们发送的消息。
    • 容器类很简单。 不像使用共享状态那样,容器类无需知道组件使用了什么数据,它只是将消息发送出去。 这可以让组件发送领域特有的数据而无需打扰容器对象。