基于组件的实体系统中的游戏状态和输入处理


16

我的问题是:

如何在我的实体系统中处理游戏状态,而又不保留周围的游戏状态对象堆栈?

因此,我的实体系统的设计意味着,例如,当某个实体需要注册输入事件时,输入组件将调用输入系统,并说“为此输入注册此实体”。一切都很好,但是,如果您将游戏状态的概念(例如暂停屏幕)加入其中,那么确定实体是否处于当前状态并应接收输入就变得很困难。

我可以扩充输入组件/系统,使其说“在处于这些游戏状态时为此输入注册此实体”,但这要求每个实体都知道将在其中使用哪个状态,这可能并不明显。同样,在每个注册的输入(以及其他使用回调的系统)周围保留游戏状态列表听起来并不太有效。

我有另一个想法,因为将有一个代表游戏状态的实体,将其标记为已禁用,然后在生成输入事件时检查该实体不是已禁用游戏状态实体的后代。为每个回调计算父级似乎很昂贵。

另一个想法是让所有系统针对当前状态存储其数据密钥,这样,在生成输入时,目标实体甚至都不是候选对象。但是,这确实损害了允许处于不同状态的实体之间进行通信的能力(对于暂停屏幕而言,这并不是什么大问题,但请考虑Oblivion / Skyrim中的锁定选择)。

我唯一的另一个想法是让所有组件处理状态更改事件并与相关系统进行通信以禁用其已注册的任何内容,并在切换回此状态时重新启用它。

第二个(将对象标记为已禁用)和第四个(使每个组件都处理状态更改)似乎是我的最佳想法,但是没有一个想法特别出色。

还有其他人对此有其他想法吗?

编辑当我谈论输入专门在这个问题上,它可能意味着能够发送消息/事件的实体,如碰撞,定时器事件等的任何系统...


6
我这样做是这样的:我有Screens,MenuScreen,PauseScreen,GameScreen,每个屏幕都可以创建自己的World(实体容器)和系统(如RenderingSystem),然后在GameScreen中创建World,Entity和CameraComponent,并将CameraComponent.RenderTarget设置为屏幕背景。这样,我可以添加将具有自己的实体和系统(如简化的渲染器)的InventoryScreen。输入可以从屏幕传递到世界,因此您的用户界面将决定是否将输入传递到屏幕(如果其焦点,可见等)以及将输入传递到世界和实体
Kikaimaru 2013年


2
@ Byte56并非如此,只有第一个与游戏状态有关(其他两个与实体中的状态有关),但这并不能真正解决我遇到的相同问题。当游戏处于暂停状态时,输入系统必须发生某些事情才能停止它向玩家实体发送移动消息(例如),我只是想不出一个好方法。
elFarto

1
好,那就考虑一下它们。好问题。
MichaelHouse

1
过去要困扰我基于组件的系统的其他因素:多层UI。在世界或多层屏幕顶部弹出对话框。到目前为止,在我制作的每款游戏中都涉及到这一点,因此我要确保考虑一种可以解决该问题的方法。
亚行

Answers:


14

经常使用的是中间体 Intent System,抽象输入并跟踪上下文和相关的游戏状态。

例如,当模拟暂停时,Intent系统将停止传输输入。它还处理控制器事件和意图之间的映射(方向移动,运行,射击,重新加载...)。

这样,您的其他组件不依赖于特定的游戏手柄/输入(BUTTON_A,BUTTON_B与BUTTON_X,BUTTON_O ...),但是它们都对相同的意图做出反应(IntentRun,IntentReload ...)。

另一个好处是,意图系统可以知道正在添加/删除的可用控制器,因为即使在模拟之外,您也可以像这样处理意图,它可以将意图发送给任何订户AddPlayer(controllerID)

您通过事件/消息或直接向系统提供的有关游戏状态的信息取决于您自己。但是在Intent系统上花费的时间通常是值得的。

您可以管理意图上下文,当它们连接到系统时,它们将生成意图。

可以确定上下文的优先级,即:

  • SimulationAvailableContext在可用(但未运行)时将意图发送给模拟,例如移动摄像机,放大缩小,添加/删除播放器...
  • SimulationRunningContext在未暂停的情况下将意图发送给模拟,以移动播放器,将单元发送到位置,射击...

这样,您可以添加和删除当前相关的上下文。

关于整个意图系统的一件事是,它应该在暂停模拟的同时运行。

在不中断与仿真无关的更新的情况下经常用于播放/暂停游戏仿真的一种方法是使用不同的时间集。即GenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime)

使用这种方法,您的引擎可以简单地阻止游戏simTime上的增量,从而阻止对所使用的相关动画和物理引擎的simTime and simDeltaTime更新,同时即使在暂停过程中也必须移动时,也可以连续更新相机的弹力效果,正在下载数据时对虚拟游戏内广告牌的加载效果...


我喜欢这样的事实,即不必在所有实体上调用一堆“状态更改”功能。您必须担心在错误的时间发送错误的意图,但是我认为这比替代方法要好。
Thomas Marnell 2013年

您的实体可以忽略“跳转”之类的意图,而其状态不允许它们跳转(即不触地)。但他们不必担心暂停游戏时会收到这样的意图。
郊狼

我已经考虑过让实体告诉输入系统传递消息的状态,但是我没有考虑过将状态置于输入本身,这是一个好主意。将时间和simTime分开也很好。
elFarto

您应该避免使用与仿真无关的东西来膨胀与仿真相关的状态。将所有UI和与播放器相关的代码尽可能地移离模拟本身,并且在模拟中仅专注于意图。
郊狼

嘿@Coyote,这个系统听起来很有趣。您能否通过回答这个问题来提供更多信息?谢谢!
13年

2

如何创建一个全局事件系统,然后为每个实体提供一个事件侦听器组件?在“游戏状态更改”事件之后,您可以为每个特定实体单独摆弄组件。

假设您有一个输入组件。事件侦听器组件接收到游戏状态更改事件后,它将为该特定输入组件更改非常特定的值,因此它将不会接收任何输入调用,也不会对系统或其所有者进行任何移动或响应调用。

这对我有用,因为我的大多数组件都是脚本编写的(通过Lua)。即我有一个输入组件,当按下一个键时,它会触发一次,它会向一个运动+方向触发,然后在释放键时,会向一个stop +方向触发,然后触发它。还有一个事件侦听器组件,它与输入组件联系(如果游戏暂停),以停止执行任何功能,并在必要时停止。然后,我可以轻松地使用另一个脚本添加对相同事件和按键反应不同的另一个实体。这样,您可以保存处于不同状态的不同实体之间的交互,甚至可以使其更具可定制性。而且,某些实体甚至可能没有事件侦听器组件。

我刚刚解释的基本上是第四个解决方案的实际示例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.