从概念上讲,重播如何在游戏中起作用?


145

我对如何在游戏中实现重播感到很好奇。

最初,我认为游戏中只会有每个玩家/ ai动作的命令列表,然后“重玩”游戏并让引擎照常渲染。但是,我研究了FPS / RTS游戏中的重播,经过仔细检查,甚至粒子和图形/听觉故障之类的东西都是一致的(而且这些故障通常一致的)。

那么这是怎么发生的。在固定摄影机角度的游戏中,我可能只是将整个场景的每一帧写入要存储的流中,然后重新播放该流,但这对于允许您暂停和移动摄影机的游戏来说似乎还不够周围。您必须在所有时间点存储场景中所有内容的位置(否?)。因此,对于粒子之类的东西来说,要推送的数据很多,这似乎在游戏过程中极大地吸引了游戏性能。


10
原始的《星际争霸》重播实际上并不一致。您可以观看同一游戏两次,并看到一些完全不同的结果。
安德列斯(Andres)2010年

1
@Andres:有趣,我没有注意到。特别是对于RTS风格,我想到的是《英雄连队》。
史蒂文·埃弗斯

4
为了澄清我相信SnOrfus在问的问题:一些游戏(《神秘海域2》,《光环3》,甚至《战地风云2》)允许您完整记录游戏。游戏结束后,您可以指定的速度进行播放,并在进行动作时在关卡中飞行,并从地图上的任何位置查看。因此,我认为这与记录所有玩家/对象的移动有关,而不是与视频缓冲区有关。
肖恩2010年

1
@Sean O'Hollaren:是的,这是正确的。
史蒂文·埃弗斯

1
然后,我还将添加赛车游戏,其中重播几乎是默认的。我很确定记录了模型的位置,然后一切都通过引擎运行了。
d -_- b

Answers:


61

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


帧号可能不是一个很好的参考,因为重播可能会以与实时游戏不同的帧速率运行。
本S

5
@Ben:帧速率没有区别,因为帧号仍然相同。这是正确的答案。
BlueRaja-Danny Pflughoeft,2010年

14
图形框架和引擎“框架”(或迭代)不一定相同。在许多较旧的游戏中,引擎在一个主循环中以与图形相同的速率进行更新。使用现代引擎,通常可以使图形更新速度达到GPU所允许的速度,同时使引擎滴答作响,以达到良好而稳定的游戏动态分辨率(通常是物理引擎)。
丹·布莱恩特

3
@iamgopal:如果您知道伪随机数生成器的状态,则该问题已经解决。另一种方法可能是将随机数视为另一种输入形式,并将其与按键等一起保存。
Kylotan 2010年

1
我想广告是,这种方法需要您的游戏引擎具有确定性,并且必须以固定的时间步长运行。我相信暴雪的所有RTS游戏都是以这种方式构建的。非确定性游戏将包括其他同步数据,以确保长期的一致性。
约翰·莱德格伦

28

《星际争霸》和《星际争霸:母巢之战》具有重播功能。比赛结束后,您可以选择保存重播以供以后查看。回放时,您可以在地图上滚动并单击单位和建筑物,但不能更改其行为。

我记得曾经看过原始游戏中一场比赛的重播,但是重播是在《巢穴战争》中观看的。对于那些不熟悉的人,《巢穴战争》包含了所有原始单位和建筑物,以及各种新建筑物。在原始游戏中,玩家通过创建计算机无法轻易对抗的单位来击败计算机。当我在《巢穴战争》中播放重播时,计算机可以访问不同的单位,这些单位可以创建并用来击败玩家。因此,完全相同的重播文件会导致不同的获胜者,具体取决于正在播放该文件的Starcraft版本。

我总是觉得这个概念很有趣。重放功能似乎是通过记录播放器的所有输入来工作的,并且假定计算机每次都将以完全相同的方式响应这些刺激。当玩家的输入被输入到原始的《星际争霸》重播器中时,游戏的播放与原始比赛中的播放完全相同。当将相同的确切输入输入到Brood War重播器中时,计算机做出不同的反应,创建了更强大的单位并赢得了比赛。

如果要编写重放引擎,请记住一些注意事项。


6
+1:非常有趣。我从未听说过。很好地了解了他们如何开发它。
史蒂文·埃弗斯

18

有两种主要方法:

  1. 就像您所说的那样存储事件(例如玩家/ ai动作)。
  2. 存储状态(完整的游戏状态,对象在连续时间内的位置)。

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

注意,“状态”并不表示图形状态。更多诸如单位职位,资源状态等信息。诸如图形,粒子系统等之类的东西通常是确定性的,可以存储为“动画X,时间Y:Z”。

有时,重播用作防作弊方案。然后在这里存储事件可能是最好的。


10

从技术上讲,您应该编写确定性的引擎,这不是随机性。假设游戏中的角色瞄准对手的手臂并发射武器,那么在所有情况下都应对对手施加相同量的伤害。

假设炸弹在位置X处爆炸,则爆炸产生的粒子应始终产生相同的视觉效果。如果需要随机性,请创建一组随机数,在玩游戏时选择一个种子值,然后将该种子值保存在重播中。

通常,在游戏中具有随机性是一个坏主意。即使对于多人游戏,您也不能让一半的玩家能够看到爆炸,而其他玩家也不能仅仅因为他们没有获得正确的随机值。

确定一切,然后就可以了。


1
人工智能呢?AI不是随机的吗?
杰西·雅辛斯基

18
真的没有必要。对所有随机事件使用种子伪随机数,并将种子保存在重播文件中。这样,在重放期间将生成相同的“随机”数字。
本S

13
-1清楚地误解了计算机中“随机性”的工作原理
BlueRaja-Danny Pflughoeft 2010年

10
嗯..没有....我完全意识到,没有“真正的”随机性之类的东西。但是,大多数人试图通过将其随机种子设置为系统时间等方法来解决此问题。但是,我的意思是不应该这样做。我不在乎他是否使用系统API或预定义的随机数表。我最初说的是正确的。引擎中的每个功能都应根据其输入产生相同的结果。时间永远不会成为一个因素。
Timothy Baldridge 2010年

2
如果粒子没有以任何有意义的方式与游戏机制互动,那么RNG对于它们而言是否不同并不重要。在网络同步模拟的情况下(这在大多数RTS游戏以及许多其他类型的游戏中都是这样),这将有所帮助,因为模拟必须同步每一帧(粒子效果只是获得单独更新)。
RCIX

10

给定初始状态一系列带有时间戳的动作,只需按顺序进行操作即可,因为所记录的动作应该发生并具有重放功能。

为了获得完全相同的随机事件,请使用播种的伪随机数并将种子保存在重播文件中。

只要您使用相同的算法从种子生成随机数,就可以重新创建所有事件,就像它们在实时游戏中发生的一样,而无需游戏状态的完整快照。

这需要按顺序观看重播,但这对于游戏重播是很正常的(请参阅《星际争霸2》)。如果要允许随机访问时间线,则可以按设置的时间间隔(例如每分钟)拍摄完整状态快照,以设置的粒度在时间线中跳转。


如果每隔一定秒数(例如5或10)重新播种,就很容易在重放流中进行记录,并且还允许向前或向后跳转(本质上是PRNG“关键帧”)。

7

NVidia PhysX(游戏中经常使用的物理模拟引擎)能够记录一段时间内物理场景的完整状态。这包含了来自游戏引擎的任何驾驶输入,这意味着您无需像其他人所建议的那样跟踪随机数种子。如果使用此场景转储,则可以在外部工具(由NVidia提供)中对其进行重播,这对于跟踪物理模型的问题非常方便。但是,您也可以使用相同的物理场流来驱动图形引擎,这将使您能够进行常规的摄像机控制,因为仅记录了驱动图形的物理场。在许多游戏中,这包括粒子效果(PhysX包括一些非常复杂的粒子系统。)至于声音,我想它是逐字记录的(作为声音流),但是我


4

您最初的想法是正确的,并且对于真正复杂的效果,不会仅仅记住它们。例如,《魔兽争霸3》重播系统不存储动画的状态,在随机效果的情况下不存储粒子效果等。此外,大多数内容都可以从确定的起点通过计算来确定,因此对于大多数系统使用随机变量(例如提供随机偏移的粒子爆炸),您所需要做的就是效果的时间和随机种子。然后,您可以重新生成效果,而无需真正知道它最终看起来是什么样子。知道它正在通过确定性代码路径。

纯粹从概念上考虑,重播事件的时间轴,您需要做的只是用户操作。该程序将以完全相同的方式进行响应,但随机变量除外。在这种情况下,您可以忽略随机性(如果效果看上去完全一样,这真的很重要,或者可以随机重新生成效果),也可以存储种子值并伪造随机性。


3

扔进我的两便士。

取决于您想要什么,可以通过以下方式完成重播

  1. 记录视频缓冲区并稍后重播,
  2. 捕获每一帧的对象状态并稍后重播,

大多数情况下,人们都希望进行交互式重播,因此2.可行之路。然后,根据您的限制,有多种方法可以优化此过程

  • 确保系统是确定性仿真*,以便每个输入生成一致且预期的输出
  • 如果需要随机性,请确保可以在以后的时间精确地复制随机数[查看使用伪随机数发生器PRNG进行播种,或使用固定的随机集]
  • 将游戏元素分为“机械”元素和“美学”元素。机械元素会影响结果(例如,列倒下和阻塞路径),美学元素是用来显示的,并不影响系统中的任何决策过程(例如,视觉粒子效果,例如火花)。

这确实是一个有趣的话题。我记得原来的Xbox Wreckless的一个启动标题具有良好的播放功能。不幸的是,在不止一次的情况下,重播会变得很糟;)

哦,是的,谁能忘记Blinx Time Sweeper!真正的游戏机制中包含很棒的互动性重播!


* =似乎有一些关于时间步进的评论。我在这里使用“模拟”来捕获此功能。从根本上讲,您的引擎需要能够产生离散的时间帧。即使重播帧比原始帧花费更长或更短的时间,系统也必须感知到相同的时间增量已经过去。这意味着用每个记录的输入记录帧时间步长,并将此增量提供给引擎时钟。


2

也许您可以简单地保存每个玩家发送的一堆命令。因此,您不必保存每个玩家发送的按键,而不必保存炸弹在某个特定的时间和地点爆炸或摧毁特定的汽车。然后,在重播中,您只需模拟游戏,就像在按下这些按键时一样。我觉得这样有可能占用更少的空间,但是我从来没有在这样的重放系统上工作过。

有趣的问题。我会对专业游戏中的操作方式感兴趣。


2

丹·布莱恩特

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

刚开始我想弄清楚他们是如何做到的时,我就是这么想的,以使游戏每次都能始终播放相同的内容。有了《毁灭战士》,我想到了拍摄的随机性:D。存储使用的任何随机数,我发现这可能是一个解决方案。那是在我遇到有关Crysis技术的pdf论文之前。那里的一些纹理噪声以及草或树的配置似乎是在使用带有固定可逆种子的伪随机化来制作的,因此您每次看时都不会看到噪声,树和草的配置发生了变化!

避免在同一时间,存储数以百万计的树木和草杆的位置。显然,伪随机序列可以随时重播,因为逻辑是固定的,因此只能制作伪造的统计随机数序列。


如果您想引起Dan的注意,请在他的贡献下添加评论-否则他可能看不到。
Halfer 2013年

Dan回答说,可能是因为我只是个客人,但在父帖子中看不到任何“添加评论”功能的原因,更不用说Dan的答复了。我看到有一个编辑改进功能,即使不是我的帖子也可以,但是这是如何工作的?
匿名2013年

啊,好问题!它似乎在这里,你需要50个代表处点上的问题或答案不是您自己的评论-我的歉意。尽管50很容易获得-通常只需几个有用的贡献即可实现。是的,您可以编辑其他人的问题和答案,尽管您的编辑将由其他人审阅,直到您达到2000年为止。请参见此处特权图表
2013年

1

拥有一致的重播问题就像拥有一致的多人游戏一样(容易)。

如前所述,RTS游戏中的重播是通过记录所有输入来存储的(有作用。滚动没有作用。)多人游戏也传输所有输入

记录所有输入而不仅仅是猜测-有一个用于阅读Warcraft3重放的库可以揭示这一点。

输入包括此答案的时间戳。


不,它与一致的MP游戏不同(或更容易)。当您播放MP时,游戏通常要求每个人都拥有相同版本的游戏,而存储的会话并不一定是这种情况(因为它可能已经与较旧版本的游戏一起存储了)。如果其中一位玩家是AI对手,这一点尤其重要。试想一下,如果您重玩一个游戏,其中某个单位的攻击点比更新的版本多一个记录点。这可能导致完全不同的结果。
drakon

-1

我相信游戏会以一定的增量拍摄所有事物(所有事物)的状态快照。然后,当重放发生时,可以使用线性插值的简单用法来填充“空洞”。至少那是我认为的方式。

您正确地记录了输入的输入将是不可靠的/不保证相同的输出。游戏绝对必须跟踪所有对象(或至少重要对象)的状态


2
不,输入相同的输入将得到与第一次完全相同的结果。您只需要确保获得正确的时序,就可以在最初接收输入的相同帧之间输入输入。定期保存整个游戏状态可能需要大量的内存,并且也会产生不一致的结果。
彼得·鲁德曼

@Peter,“提供相同的输入将得到完全相同的结果”:不。许多游戏中都有一个随机元素,每次重播可能会有所不同。您需要跟踪的不仅仅是输入。
houbysoft 2010年

确实如此。您还需要存储PRNG的种子(请参阅我对这个问题的回答)。
彼得·鲁德曼

1
我知道这会消耗性能和内存,但是如果您错过了输入或随机生成器等方面的一点点东西……或什至任何东西,重播将以可怕的切线开始!
Bob Fincheimer 2010年

@ BlueRaja,Bob的内存快照想法不一定牵强,尽管一个好的引擎可以记录状态“增量”,而不是为每次迭代编码所有内存。在引擎级别,这可能更容易支持。此外,记录随机种子不足以支持倒带,因为如果没有依赖于随机性的所有逻辑的特殊支持,则随机进行不是可逆的过程。将随机操作的结果记录为事件流的一部分更加灵活。
丹·布莱恩特
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.