GameplayKit - 关于在组件之间发送消息的困惑

Posted

技术标签:

【中文标题】GameplayKit - 关于在组件之间发送消息的困惑【英文标题】:GameplayKit - Confusion about sending messages between components 【发布时间】:2015-09-07 21:53:04 【问题描述】:

我正在深入研究带有 Spritekit 的 GameplayKit,据我所知,您将 GKEntity 子类化,然后开始将 GKComponents 添加到该实体。实体或多或少只是一袋填充某些功能的组件。

我感到困惑的部分是组件之间的通信。你如何让它们解耦。例如,假设我有一个HealthComponent 类,我将该组件添加到PlayerEntityEnemyEntity。我也有一个HealthBarComponent但我只想在玩家上方显示一个生命条。当玩家受到伤害时,该信息需要在HealthBarComponent 中更新。

那么应该如何发送这些信息?我看到文档中有一个名为GKComponentSystem 的类。我不是 100% 知道应该如何使用它。

另一个问题是.. 当玩家的生命值降为零时,他应该再生,而敌人应该保持死亡。当玩家的生命耗尽时,游戏结束。

敌人和玩家的健康系统大致相同,但每个人的死亡事件将完全不同。我没有关注如何在保持每个实体的独特行为的同时使用组件系统。

一些伪代码会很棒

【问题讨论】:

在这里他们谈论组件之间的通信。似乎没有特定的方法可以完美地做到这一点。一些耦合可能是必要的。 gameprogrammingpatterns.com/component.html 您可能会发现DemoBots sample project 很有趣。游戏中的健康等价物是充电,有一个ChargeComponent,它会在充电丢失时通知其代表。该游戏还具有与您提到的生命恢复机制类似的机制,它是使用GKStateMachine 和随附的状态实现的。 【参考方案1】:

看起来这个框架的工作方式与我见过的其他框架有点不同,因为系统只在单一类型的组件上工作,而不是在具有组件类型的实体上工作。

在 GamePlay-Kit 框架中,您可以循环并手动更新您的实体(依次更新每个实体组件)或创建一个继承自 GKComponentSystem 的类。然后,当您更新系统时,它会更新所有组件 you have added to it,只要它们的类类型与您的类型匹配 initialized the system with

为了处理您的健康栏问题,我会说创建一个HealthBarComponent,它在更新期间从其所有者实体检索HealthComponent,读取健康值并在每一帧呈现自己。但是你只有add一个HealthBarComponent给你的玩家实体。

您可以从组件中检索所有者实体,反之亦然(请参阅GKComponent.entity 和GKEntity.components),因此您可以像这样更新您的健康栏:

/*Note: I'm not an ios developer, this is pseudocode*/

HealthBarComponent.update()
  int healthValue = self.entity.components.get(HealthComponent).value;
  //Now render your health bar accordingly or hold onto this for later

为了处理玩家死亡问题,我认为最好的办法是拥有两种不同类型的生命值组件(PlayerHealth 和 EnemyHealth)以及两个不同的对应系统。或者只有一个健康组件,但有两个独立的“死亡”组件。是的,这似乎是多余的,但我很难在这个框架内想出更好的方法。根据我的经验,您要么花时间担心让所有内容完全解耦和可重用,要么构建游戏:)

【讨论】:

【参考方案2】:

组件可以通过self.entity 属性访问它们的实体。从那里,您可以查询其他组件以通过实体componentForClass 属性传递数据:

guard let moveComponent = entity?.componentForClass(MoveComponent.self) else 
        fatalError("A MovementComponent's entity must have a MoveComponent")
    

iOS Games by Tutorials 这本书有一个很好的教程,涵盖了 GameplayKit 实体和组件。

【讨论】:

【参考方案3】:

通知非常适合此类问题。根本没有直接的对象耦合,如果多个对象最终需要知道它是非常可扩展的(例如你的健康栏组件,一个决定游戏结束的更高级别的游戏对象,甚至可能敌人/盟友 AI 在健康状况不佳)。

您可以有一个名为“playerHealthChanged”的通知,并让您的健康栏组件和其他对象独立注册以响应该事件。 (您可能需要让 HealthComponent 实例知道它是否应该发布此通知,因此只有玩家发布 - 也许它可以在其实体上使用 isKind(of:) ,或者只是有一个 bool 字段来启用发布,设置为 true播放器实例)。

我通常将所有通知名称定义放在一个模块中,以便任何类都可以访问它们:

let kPlayerHealthChangedNotification = Notification.Name("playerHealthChanged")

这是组件发布通知的方式(如果需要,您可以传递 self 以外的对象):

NotificationCenter.default.post(name: kPlayerHealthChangedNotification, object:self)

然后关心玩家健康变化的对象可以像这样在他们的初始化代码中注册 - 创建一个处理函数,而不是添加 self 作为通知的观察者:

@objc func onPlayerHealthChanged(_ notification:Notification) 
  // do whatever is needed; notification.object 
  // has the object from the post


// put this in a setup method - init or similar:
  NotificationCenter.default.addObserver(self,
      selector: #selector(onPlayerHealthChanged(_:)),
      name: kPlayerHealthChangedNotification
      object:nil)

这是文档:https://developer.apple.com/documentation/foundation/notifications

【讨论】:

以上是关于GameplayKit - 关于在组件之间发送消息的困惑的主要内容,如果未能解决你的问题,请参考以下文章

GameplayKit的GKStateMachine用法与实例

运用GamePlayKit的GKEntity及GKComponent 的iOS游戏开发实例

Android广播机制(转)

消息队列之kafka(基础介绍)

Vue.js组件之间的通信

Android 广播接收者 BroadcastReceiver