您说的是entity component system。有一对用JS写的;最受欢迎的是Crafty,很大但值得一看。我最近在 CoffeeScript 中写了一个(只是为了好玩;可能永远不会发布它)。
关于碰撞的几点说明:
首先,问题可能比你想象的更严重:如果两个方法具有相同的名称,就会发生冲突; JS 不区分函数签名。它也可能不是那么糟糕:为什么不创建一个命名空间约定,其中每个行为(含义方法)都以其所属的组件命名,例如burnable_burn?
但退一步说,mixin 并不是构建它的唯一方法——行为(即组件可以做的事情)根本不必是方法。我提出的激励问题是,你如何触发行为?例如,您可能会这样做:
if entity.hasComponent "burnable" #hasComponent provided by your framework
entity.burn()
但这对我来说听起来不对;它在您的游戏中发生的事情和您拥有的组件之间产生了一种奇怪的耦合,并且检查您的实体是否实现了相关组件是很尴尬的。相反,我希望行为是 events 上的 listeners:
entity.send("applySeriousHeat") #triggers whatever behaviors are there
然后让你的组件做它需要做的任何事情。因此,当您将组件添加到实体时,它会为事件注册侦听器。也许它看起来像(只是草图):
register: (entity) -> #called when you add a component to an entity
entity.listen "applySeriousHeat", -> #thing I do when this event is sent to me
#do burnination here
为了把这一点带回家,如果你这样做,你就不会关心碰撞,因为你的行为没有名字。事实上,你想要“碰撞”;您希望能够让多个组件响应同一事件。也许它会同时燃烧和融化?
在实践中,我同时使用了这两种设置。我在组件的函数中混合了entity.addComponent,因为偶尔将行为作为方法调用很方便。但大多数情况下,组件声明了调用这些方法的侦听器,这有助于解耦并减少必须使用范围名称的尴尬,因为在大多数情况下我不直接调用它们。