从概念上讲,重放在游戏中是如何工作的?

我有点好奇在游戏中如何实现重播。

最初,我认为应该只有一个命令列表,列出游戏中每个玩家/人工智能所采取的行动,然后重新玩游戏,让引擎像往常一样渲染。然而,我观察了 FPS/RTS 游戏中的回放,经过仔细检查,甚至像粒子和图形/听觉故障这样的东西都是一致的(这些故障通常是 进去一致的)。

怎么会这样。在固定摄像角度的游戏中,我认为它可能只是把整个场景的每一帧都写到一个存储的流中,然后只是回放流,但是对于允许你暂停和移动摄像机的游戏来说,这似乎还不够。你必须在任何时间点存储场景中所有东西的位置(不?).因此,对于粒子这样的东西,需要推送大量的数据,这似乎是游戏性能的一个重要因素。

20209 次浏览

我相信,在某些增量的游戏将采取一个快照的状态的一切(一切)。然后当重放发生时,简单的使用线性插值可以用来填补“空洞”。至少我是这么想的。

您正确地认为记录输入将是不可靠的/不能保证相同的输出。游戏肯定要跟踪所有物体(或者至少是重要物体)的状态

从技术上讲,你应该写你的引擎是确定性的,这是没有随机性。假设游戏中的一个角色瞄准对手的手臂,然后发射武器,那么在所有情况下对对手造成的伤害应该是相同的。

假设一个炸弹在 X 点爆炸,爆炸产生的粒子应该总是会产生相同的视觉效果。如果需要随机性,可以创建一组随机数,在游戏进行时选择一个种子值,并将该种子值保存到重播中。

一般来说,在游戏中有随机性是一个坏主意。即使是像多人游戏这样的事情,你也不能让一半的玩家能够看到爆炸,而其他人不能,仅仅是因为他们没有得到正确的随机值。

让一切都是确定的,你就会没事的。

我认为你最初的想法是正确的。要创建重播,您需要存储从用户接收到的所有输入(以及接收它的帧号)以及任何随机数生成器的初始种子。为了重播游戏,您可以使用保存的种子重置 PRNG,并向游戏引擎提供相同的输入顺序(与帧号同步)。由于许多游戏会根据帧之间传递的时间更新游戏状态,因此您可能还需要存储每帧的长度。

有两种主要的方法:

  1. 存储事件(比如播放器/AI 操作)——正如您所说的。
  2. 存储状态(完整的游戏状态,物体的位置,在连续的时刻)。

这取决于你想做什么。有时存储事件更好,因为这通常占用更少的内存。另一方面,如果你想提供重播,可以在不同的速度和从不同的起点播放,这是更好的存储状态。当存储状态时,你也可以决定是在每个事件之后存储它们,还是每秒只存储12或25次——这可能会减少你的重播的大小,并且更容易倒带/快进它们。

请注意,“状态”并不意味着图形状态。更像是单位位置、资源状况等等。像图形、粒子系统等通常是确定性的,可以存储为“动画 X,时间 Y: Z”。

有时回放被用作反作弊方案,那么存储事件可能是最好的方法。

也许你可以简单地保存每个玩家发送的一堆命令。所以,与其保存炸弹在某个时间点引爆,或某辆汽车被摧毁,你只需保存每个玩家发送的按键。然后,在回放中,你只需要模拟比赛,就像那些压力机会发生的那样。我觉得这样可能会占用更少的空间,但我从来没有做过这样的重播系统。

不过这个问题很有意思,我很想知道职业比赛是怎么做到的。

给定一个 初始状态和一个 有时间戳的一系列动作,简单地通过序列,因为记录的行动应该发生有一个重播。

为了使随机事件完全相同地重新发生,在重播文件中使用 使用带种子的伪随机数并保存种子

只要使用相同的算法从种子生成随机数,就可以像在现场游戏中那样重新创建所有事件,而不需要游戏状态的完整快照。

这将需要重放为 连续观看,但这是相当正常的游戏重放(见星际争霸2)。如果您想允许随机访问时间线,您可以 按设定的间隔拍摄完整的状态快照(比如每分钟) ,以设定的粒度跳转时间线。

把我的两便士扔进去。

取决于你想要什么,重播可以通过

  1. 录制视频缓冲然后重放,
  2. 捕捉每帧对象状态,然后重放,

大多数时候,人们想要一个交互式的回放,所以2。才是正确的选择。然后,根据您的约束,有许多方法可以优化这个过程

  • 确保系统是一个确定性的模拟 * ,使每个输入产生一致的和预期的输出
  • 如果需要随机性,确保随机数可以在以后的时间复制 没错[查看用伪随机数生成器 PRNG 播种,或使用罐装随机集]
  • 把游戏元素分为“机械”和“美学”元素。机械元素影响结果[例如柱子倒塌和阻塞路径] ,美学元素是为了显示,并不影响系统中的任何决策过程[例如视觉粒子效果像火花]。

这确实是个有趣的话题。我记得最初的 Xbox鲁莽的一个发布标题有一个很好的播放功能。不幸的是,不止一次重播会搞砸;)

哦,是啊,怎么会有人忘记 布林克斯 时间清扫者! 很好互动重播,这是融合了 进入实际游戏机械!


* = 似乎有一些关于时间步长的评论。我在这里使用“模拟”来捕捉这个特征。在核心,你的引擎需要能够产生离散的时间框架。即使重播帧比原始帧花费更长或更短的时间进行处理,系统也必须在同一时间内完成 感知。这意味着记录每个记录的输入帧的时间步长,并提供这个增量到您的发动机时钟。

NVidia PhysX (一个经常在游戏中使用的物理模拟引擎)能够随着时间的推移记录物理场景的全部状态。这包含了来自游戏引擎的任何驱动输入,这意味着您不需要像其他人建议的那样跟踪随机数种子。如果您使用这个场景转储,您可以在外部工具(由 NVidia 提供)中重放它,这对于跟踪物理模型的问题非常方便。然而,您也可以使用相同的物理流来驱动您的图形引擎,这将允许您有正常的摄像机控制,因为只有物理驱动的图形已被记录。在许多游戏中,这包括粒子效应(物理 X 包括一些非常复杂的粒子系统。)至于声音,我猜那是逐字记录的(作为声音流) ,但我不确定。

你最初的想法是正确的,对于真正复杂的效果来说,它们不会被完全记住。例如,魔兽争霸3重播系统不存储动画的状态,或粒子效果的情况下随机效果,等等。此外,大多数事物都可以从一个确定的起点计算出来,所以对于大多数使用随机变量的系统(例如,给出随机偏移量的粒子爆炸) ,你所需要的只是效应的时间和随机种子。然后你可以重新生成这种效果,而不必真正知道它最终会变成什么样子。.知道它正在通过确定性代码路径。

纯粹从概念上考虑,要重播事件的时间线,所有您需要的就是用户操作。除了随机变量,程序的反应完全相同。在这种情况下,您可以忽略随机性(如果效果看起来完全一样,或者可以随机重新生成,这真的有关系吗?) ,或者存储种子值并伪造随机性。

星际争霸和星际争霸: 母巢战争有一个回放功能。比赛结束后,您可以选择保存重播以便稍后查看。在回放的时候,你可以在地图上滚动,点击单位和建筑,但是不能改变它们的行为。

我记得有一次看了一场比赛的重播,这场比赛是在原来的比赛中进行的,但是重播是在《血战》中进行的。对于那些不熟悉的人来说,“鸡群战争”包含了所有的原始单元和建筑,以及各种各样的新单元和建筑。在最初的游戏中,玩家通过创造计算机无法轻易反击的单位来击败计算机。当我播放《血战》的回放时,计算机可以访问不同的单位,它创建并使用这些单位来击败玩家。所以完全相同的重播文件会导致不同的赢家取决于星际争霸的哪个版本在播放这个文件。

我一直觉得这个概念很吸引人。重放功能似乎是通过记录玩家的所有输入来工作的,并假设计算机每次对这些刺激的反应都是完全相同的。当玩家的输入输入到星际争霸的原始播放器中时,游戏的结果和原始比赛中一模一样。当相同的输入被输入到 Brood War 的重放器中时,计算机的反应就不同了,它创造出更强大的单位,并赢得了游戏。

如果您正在编写重播引擎,请记住这一点。

持续重播的问题和持续多人游戏的问题是一样的。

正如前面提到的,RTS 游戏中的重播通过记录所有输入来存储(这有一个效果)。滚动没有任何效果。) 多人游戏也传输所有的输入

记录所有的输入不只是一个猜测-有一个读取魔兽争霸3重播的库揭示了这一点。

输入包括这个答案的时间戳。

Dan Bryant

此外,记录随机种子将不足以倒带 支持,因为随机进展是不可逆的过程 没有特别的支持在所有的逻辑依赖于随机性。 将随机操作的结果记录为 事件流的一部分。

我一开始也是这么想的当时我想弄明白他们是怎么做到的这样游戏的回放每次都是一样的。在《末日杀手》中,我想到了拍摄的随机性: D。存储任何随机的数字被使用,我发现它可能是一个解决方案。 那是在我看到一篇关于 Crysis 技术的 pdf 文章之前。有些纹理噪音和草或树的配置,似乎是使用伪随机化与固定可逆种子,使它,所以你不会看到改变配置的噪音,树和草随时你看!

避免在同一时间,存储数以百万计的树木和草轴的位置。 显然,伪随机序列可以重播相同的任何时候,因为逻辑是固定的,只是作出一个虚假的统计随机序列的数字。