让我们说你正在制作一个基于回合的游戏,象棋是一个很好的例子。
有一个逻辑表示法:一个棋盘,其中每个单元格要么是空的,要么有一个特定的象棋棋子。就是这样。玩家执行动作。这些动作修改了逻辑游戏状态。游戏状态完全独立于任何视觉效果。您可以在服务器上模拟它而不需要任何图形、资产或游戏引擎。
但是,您也希望有视觉效果。一个特殊的动画,当一个车子捕捉到另一个棋子时。粒子效果。表示选择棋子可以移动到的位置的视觉指示符。一个动画,当卒升级为皇后时。输家单位的碎片化。场景中应用的灰度着色器在玩家输掉时。就是那些华丽的、丰富的东西。
您希望能够加载任何逻辑游戏状态,并使视觉表示与之匹配。因此,您可以创建一个函数 set_world_to_logical_state,设置所需的一切。将象棋棋子在正确的位置生成。将摄像机设置为棋盘的正确一侧。将 UI 设置为反映当前回合的玩家。就是大量的设置代码,使视觉表示与逻辑状态匹配。
现在,当您执行动作或对手做出移动时,您将通过事件接收通知。您可以通过播放 "车子 A1 捕捉卒子 A6" 动画来反应事件。随后(希望)卒子将被踢到棋盘的另一侧,车子将在正确的位置。这里的 "希望" 是因为没有保证视觉表示与逻辑状态匹配。
如果您忘记在捕捉动画中去除卒子实体,那么现在就有一个叫做 "状态漂移" 的问题。
问题是,大多数游戏的逻辑状态远远比象棋复杂。同样,玩家看到的游戏世界也是如此。
现在,基于上述模型,每当您添加东西到逻辑状态时,您需要在两个地方进行更改:在设置函数中,使初始加载的视觉效果反映逻辑状态。然后,在所有事件中,都需要更新这些新添加的状态。这个问题非常容易出错。
假设您决定在象棋游戏中添加法力(mana),并且单位可以花费法力来使敌人昏迷。那么,在设置函数中,您需要在每个单位上生成一个小的 UI 标签来显示它们的法力值。但是,可能有五个事件,或者添加法力、盗取法力或使用法力来激活能力,这些事件都将触及这些法力标签并需要更新它们。您很容易在某个地方忘记更新某个法力面板。噗!状态漂移。
那么我们如何解决这个问题呢?
- 我们不能直接显示逻辑状态中的法力值。逻辑状态在事件到达时立即更新。但是,视觉表示需要有一点延迟,才能在动画播放时显示法力值的变化。例如,标签可以在动画播放时抖动,然后在动画结束时显示新的法力值。
- 我们可以使用一个单一函数 sync_to_state,用于设置和当新事件到达时。它类似地计算逻辑状态和视觉表示之间的差异,并生成/去除实体。然后可以在此基础上播放动画,掩盖某些变化到视觉表示(例如,在下一个 0.5 秒内向卒子标签发送信号,请显示旧的法力值。如果在一帧内不再接收到这个信号,标签就显示从逻辑游戏状态中获取的法力值。
- 我们可以始终将视觉表示初始化为空场景,然后重新播放所有导致当前游戏状态的事件。这意味着我们只需要编写描述某个事件如何修改视觉表示的函数,而不是如何将视觉表示设置为某个逻辑游戏状态的函数。这被称为事件源。然而,这可能在更复杂的游戏中会变得极其不实用,游戏状态在视觉世界中发生的所有变化都被表示为动画,动画对世界进行了大量的修改。它们将需要在每次玩家加载游戏时全部重新播放。
是否有好的解决方案?
评论 (0)