从概念上讲,游戏是如何工作的?

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

起初,我认为只会有一个在游戏中采取的每个玩家/行动的命令列表,然后“重新玩”游戏并让引擎像往常一样呈现。 但是,我已经看过FPS / RTS游戏中的重放,经过仔细检查,甚至像粒子和graphics/声音故障一样(这些故障通常一致的)。

那么这是如何发生的呢? 在固定的摄像头angular度游戏中,虽然它可能只是将整个场景的每一帧都写入到一个存储的stream中,然后只是将stream重放回来,但对于允许您暂停和移动摄像机的游戏来说,这似乎不够周围。 你必须在所有时间点存储场景中的所有位置(否?)。 所以对于像粒子这样的东西来说,这是很多的数据推动,这似乎是游戏的performance,同时玩的重要吸引力。

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

星际争霸和星际争霸:母巢战争有一个重播function。 比赛结束后,您可以select保存重播以便以后查看。 在回放时,您可以滚动地图并单击单位和build筑物,但不会改变其行为。

我记得曾经看过一场原始比赛中的比赛重播,但重播正在母巢之战中进行观察。 对于那些不熟悉的人来说,母巢之战包含了所有的原始单位和build筑,以及各种新的。 在原始游戏中,玩家通过创build计算机不能轻易对抗的单位击败了计算机。 当我在母巢之战中播放重播时,电脑可以访问不同的单位,并创build并用来击败玩家。 因此,完全相同的重播文件导致了不同的获胜者,取决于哪个版本的Starcraft正在播放该文件。

我总是觉得这个概念很有意思。 似乎重放function通过logging播放器的所有input来工作,并且假定计算机将每次以完全相同的方式响应这些刺激。 当玩家的input被input到原来的星际争霸重播器中时,游戏的效果与原来的比赛完全相同。 当相同的确切的input被送入母巢战争的回放,计算机反应不同,创造更强的单位,并赢得了比赛。

如果您正在编写重播引擎,请记住一些事项。

有两种主要方法:

  1. 存储事件(如玩家/ AI行动) – 正如你所说。
  2. 存储状态(完整的游戏状态,物体的位置,在连续的时刻)。

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

请注意,“状态”并不意味着graphics状态。 更多的东西像单位的位置,资源的状态等等。 像graphics,粒子系统等东西通常是确定性的,可以存储为“animationX,时间Y:Z”。

有时重放被用作预测scheme。 那么存储事件可能是最好的。

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

假设一个炸弹在X位置引爆,那么爆炸产生的粒子总是会产生相同的视觉效果。 如果您需要随机性,请创build一组随机数,在游戏进行时select一个种子值,并在重播中保存该种子值。

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

使一切都确定,你应该没问题。

考虑到初始状态一系列带有时间戳的动作 ,只需简单地通过序列,因为所logging的动作应该发生重播。

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

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

这将需要依次观看重放,但这对于游戏重放来说是非常正常的(参见“星际争霸2”)。 如果您想允许随机访问时间线,则可以按设定的时间间隔 (例如每分钟) 进行完整的状态快照 ,以设定的粒度在时间线上跳转。

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

你原来的想法是正确的,而对于复杂的效果,它们并不是独一无二的。 例如,魔兽争霸3的重放系统不会存储animation的状态,或者在随机效果的情况下存储粒子效果等。另外,MOST的东西可以通过确定性的方式从起点计算出来,所以对于大多数系统使用随机variables(例如产生随机偏移的粒子爆炸),所有你需要的就是效果的时间和随机种子。 然后,您可以重新生成效果,而不必真正知道最终会看起来像什么样子..知道它正在经历一个确定性的代码path。

单纯从概念上思考,重播事件的时间表,所有你需要的就是用户的行动。 除了随机variables,程序的反应方式完全一样。 在这种情况下,你可以忽略随机性(真的很重要,如果效果看起来完全一样,或者可以随机重新生成),或者存储种子值并伪造随机性。

把我的两便士扔进去。

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

  1. 录制video缓冲区并稍后重播,
  2. 捕获每个帧的对象状态并稍后重放,

大多数时候,人们都希望有一个互动的重播,所以2是要走的路。 那么根据你的限制,有很多方法可以优化这个过程

  • 确保系统是一个确定性的模拟*,使每个input产生一致的和预期的输出
  • 如果需要随机性,确保随机数可能在以后的时间正确再现[看看伪随机数发生器PRNG种子,或使用随机随机设置]
  • 将游戏元素分为“机械”和“美学”元素。 机械元素会影响结果[例如列倒塌和阻塞path],审美元素是为了展示而不影响系统中的任何决策过程[例如像火花这样的视觉粒子效应]。

这真是一个非常吸引人的话题。 我记得原始的Xbox Wreckless的一个发射标题有一个很好的播放function。 不幸的是,不止一次,重播就会搞砸了;)

噢,谁能忘记Blinx Time Sweeper ! 伟大的交互式重播,并入实际的游戏机制!


* =似乎有一些关于时间步进的意见。 我在这里使用“模拟”来捕获这个function。 在核心,你的引擎需要能够产生离散的时间帧。 即使重放帧需要比原始处理更长或更短的时间,系统也必须感觉到相同的时间差已经过去了。 这意味着logging每个录制input的帧时间步长,并将这个增量提供给您的引擎时钟。

也许你可以简单地保存每个玩家发送的一堆命令。 因此,不要保存一个炸弹在某个时间点引爆,或者某辆汽车被摧毁,您只需保存每个玩家发送的按键即可。 然后,在重播中,您只需模拟游戏,就像在这些游戏中发生的那样。 我觉得这样可以占用更less的空间,但是我从来没有像这样做过重播系统。

有趣的问题,虽然。 我会对如何在专业游戏中完成感兴趣。

丹·布莱恩特

此外,logging随机种子不足以支持倒带,因为随机进展不是一个可逆的过程,如果没有依赖随机性的所有逻辑的特殊支持。 logging随机操作的结果作为事件stream的一部分更为灵活。

当我试图弄清楚他们是如何做到这一点的时候,我就是这么想的,所以每次重放游戏总是一样的。 随着厄运,我想随机射击是如何进行的:D。 存储任何使用的随机数,我发现它可能是一个解决scheme。 那是在我遇到有关Crysis技术的pdf文件之前。 一些纹理在那里发出响声,草或树的气质,似乎是用固定可逆的种子进行伪随机化,使得你看到任何时候都没有看到噪声,树木和草地的变化。

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

有一个一致的重播的问题是一样的(好,更容易),就像有一个一致的多人游戏。

正如前面提到的,RTS游戏中的重放是通过logging所有input来存储的(这是有效的,滚动没有效果)。多人游戏也传输所有的input

logging所有的input不只是一个猜测 – 有一个阅读魔兽争霸3重播与图书馆揭示这一点。

input包括此答案的时间戳。

我相信在某些增量的情况下,游戏会拍摄一切(一切)的状态。 然后当重播发生时,可以使用线性插值的简单使用来填充“孔”。 至less我认为这将是完成的。

你是正确的,logginginput将是不可靠/不保证相同的输出。 游戏必须跟踪所有对象的状态(或者至less是重要的)