在 CoffeeScript 游戏引擎中使用 mixin

发布于 2024-12-29 10:31:30 字数 494 浏览 2 评论 0原文

我正在为 Html5 画布开发 CoffeeScript 游戏引擎。在检查了一个非常简洁的 CoffeeScript 实现。我认为,通过开发一组基于 mixin 的组件(每个组件都有非常具体的功能)来减少游戏对象通常提供的各种对象层次结构,这可能是一个非常酷的想法。然后,在开发实际游戏时,人们可以通过基本上从一个组件开始并将其与一堆其他组件混合来即时构建独特的游戏对象。这减少了层次结构并允许频繁更改。

然后我考虑了可能出现的冲突,例如让一些组件定义具有相同签名的方法。现在,我没有以前那么兴奋了。

我应该怎么办?这是个好办法吗?我仍然喜欢它,特别是因为 JS 的底层原型机制,它允许以如此简单的方式动态组合东西。

I am working on a CoffeeScript game engine for Html5 canvas. I came up with the "cool" idea to utilize mixins after I checked a very neat CoffeeScript implementation. I thought, it may be a very cool idea to reduce the various hierarchy of objects that game objects usually provide, by developing a set of mixin-based components, each of which has a very specific functionality. Then, when developing an actual game, one could build unique game objects on the fly by basically starting from one component and mixing it with a bunch of other components. This reduces the hierarchies and allows for frequent changes.

Then I thought about the possible collisions that might come up, for example having a few components define a method with the same signature. Now, I am not as excited as before.

What should I do? Is this a good way? I still like it, especially because of JS' underlying prototype mechanism, which allows for such an easy way to combine stuff on the fly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

世态炎凉 2025-01-05 10:31:30

您正在谈论实体组件系统。有几个是用JS写的;最受欢迎的是Crafty,它很大但值得一看。我最近用 CoffeeScript 写了一个(只是为了好玩;可能永远不会发布它)。

关于冲突的一些注意事项:

首先,问题可能比你想象的更糟糕:如果两个方法具有相同的名称,就会发生冲突; JS 不区分函数签名。它也可能没那么糟糕:为什么不创建一个命名空间约定,其中每个行为(意思是方法)都以其所属的组件命名,例如 burnable_burn

不过,退一步说,mixin 并不是构建此功能的唯一方法 - 行为(即组件可以执行的操作)根本不必是方法。我问的激励问题是,如何触发一种行为?例如,您可能会这样做:

if entity.hasComponent "burnable" #hasComponent provided by your framework
  entity.burn()

但这对我来说听起来不太正确;它在你的游戏中发生的事情和你拥有的组件之间创建了一种奇怪的耦合,并且检查你的实体是否实现了相关组件是很尴尬的。相反,我希望行为成为事件监听器

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 混合到组件的函数中,因为有时将行为作为方法调用会很方便。但大多数情况下,组件声明调用这些方法的侦听器,这有助于解耦并减少必须使用作用域名称的尴尬,因为在大多数情况下我不会直接调用它们。

You're talking about an entity component system. There are a couple written in JS; the most popular is Crafty, which is big but worth looking at. I recently wrote one in CoffeeScript (just for funsies; will probably never release it).

A few notes about collisions:

So first, the problem may be worse than you think: collisions will happen if two methods have the same name; JS doesn't differentiate function signatures. It also might not be so bad: why don't you just create a namespacing convention, where each behavior (meaning method) is named after the component it belongs to, like burnable_burn?

To take a step back though, mixins aren't the only way to build this - behaviors (i.e. things a component can do) don't have to be methods at all. The motivating question I ask is, how do you trigger a behavior? For example, you might do:

if entity.hasComponent "burnable" #hasComponent provided by your framework
  entity.burn()

But that doesn't sound right to me; it creates a weird coupling between what's happening in your game and what components you have, and it's awkward to check if your entities implement the relevant component. Instead, I'd like behaviors to be listeners on events:

entity.send("applySeriousHeat") #triggers whatever behaviors are there

And then have your component do whatever it needs to do. So when you add a component to an entity, it registers listeners to events. Maybe it looks like (just sketching):

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

To bring that point home, if you do that, you don't care about collisions, because your behaviors don't have names. In fact, you want "collisions"; you want the ability to have more than one component respond to the same event. Maybe it burns and melts at the same time?

In practice, I used both setups together. I made entity.addComponent mix in the component's functions, since it's occasionally convenient to just call a behavior as a method. But mostly, the components declare listeners that call those methods, which helped with decoupling and reduced the awkwardness of having to use scoped names, since I don't call them directly in most cases.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文