重播-存储和定时


11

我正在从事赛车游戏,只是实现了一个幽灵精灵来重播过去的比赛。我使用物理引擎,经过大量阅读后得出的结论是,存储重影数据以进行重放的最佳方法是在给定的时间点记录汽车的位置和旋转,例如此处所述:https:// gamedev。 stackexchange.com/a/8380/26261

但是,在重播期间找到这些时间点的一种好方法是什么?一个示例是具有以下数据的记录:

time: +3.19932 (seconds since race start)
position:  180,40 (position at that time)
rotation: 30.4 (rotation at that time)

但是我有几个问题:

  1. 重新播放时,不太可能再次达到3.19932的确切时间点-更有可能的是,我的时间点大约是3.1,必须找到最接近的匹配记录。插值时,即使是上下最接近的匹配。听起来效率很低,很费时间吗?

  2. 我可以在哪个列表结构中存储这些记录以供以后重播?数组?难道不是说比赛时间越长,匹配特定时间记录的搜索时间会增加吗?

  3. 我应该为时间点使用哪个频率?每个帧都是-我猜想是过大了,相反,我应该保存,即每第n帧并在它们之间进行插值,这使存储问题在2中更加困难。

那么这个想法甚至是正确的方法吗?如果是,我如何有效地存储和检索数据?请注意,我通常希望使用上面的数据结构,而不是确定性的游戏状态和记录用户输入等。

谢谢你的帮助!

编辑:我意识到我应该描述我使用的环境:iPhone的Cocos2D。有一种方法update:(ccTime)delta。理想情况下,每1/60秒调用一次此方法,但不能保证- delta是自上次游戏结束以来经过的实际时间,并且可能多于或少于1/60。我想用这种方法存储当前的游戏状态。


2
很好的问题。如此所示,精确的重放比起初您想的要复杂得多,我很想知道人们在这里提出了什么解决方案。
基督教徒

Answers:


8

难道不是说比赛时间越长,匹配特定时间记录的搜索时间会增加吗?

不 :)

假设您将其存储为数组(请注意,快照按时间顺序排列,但间隔不均匀):

snapshots = [
    {time: 0.0, position: {x,y,z}},
    {time: 0.41,    position: {x,y,z}},
    {time: 0.57,    position: {x,y,z}},
    {time: 1.10,    position: {x,y,z}},
    {time: 1.67,    position: {x,y,z}},
    {time: 2.05,    position: {x,y,z}},
    {time: 3.24,    position: {x,y,z}},
    {time: 3.86,    position: {x,y,z}},
    {time: 3.91,    position: {x,y,z}},
    {time: 5.42,    position: {x,y,z}},
    ...]

然后,当重播/游戏开始时,您将从数组中获得第一个和第二个元素:

nextIdx = 1
previousSnapshot = snapshots[nextIdx-1]
nextSnapshot = snapshots[nextIdx]

然后在每个帧中(currentTime是此新游戏的当前时间):

if currentTime > nextSnapshot.time
    nextIdx++
    previousSnapshot = snapshots[nextIdx-1]
    nextSnapshot = snapshots[nextIdx]

# Do your magic here, e.g.:
snapshotPairGap = nextSnapshot.time - previousSnapshot.time
ratio = (currentTime - previousSnapshot.time) / snapshotPairGap
ghostPosition = {
    x: previousSnapshot.position.x + ratio*(nextSnapshot.position.x - previousSnapshot.position.x)
    y: previousSnapshot.position.y + ratio*(nextSnapshot.position.y - previousSnapshot.position.y)
    z: previousSnapshot.position.z + ratio*(nextSnapshot.position.z - previousSnapshot.position.z)
}

当然,这可以通过缓存一些计算来优化。无需搜索整个数组,只需查找特定的索引即可。


是!我必须稍后再尝试,但这似乎是我想要的。谢谢!!
marimba

15

不太难。您可以将数据存储在任意时间点(越多越好),并且可以根据要查找的时间戳和来自两个最近记录的时间戳的数据插值数据的值,例如:

N | Time | Position | Rotation
1 | 0.05 | 1, 1, 1  | 0
2 | 0.15 | 1, 2, 1  | 0
3 | 0.25 | 1, 3, 2  | 30

现在,假设您想在时间0.10处获得位置和旋转。由于0.10位于点“ 1”(表示0.05时间)和“ 2”(表示0.15时间)之间,因此需要对它们进行插值。

timestamp = 0.10
factor = (timestamp - Time[1]) / (Time[2] - Time[1])
position = Lerp(Position[1], Position[2], factor)
rotation = Lerp(Rotation[1], Rotation[2], factor)

Lerp只是线性插值

因此,让我们用一些示例(*)填补空白。

N | Time  | Position    | Rotation
1 | 0.05  | 1, 1,    1  | 0
* | 0.075 | 1, 1.25, 1  | 0
* | 0.10  | 1, 1.5,  1  | 0
2 | 0.15  | 1, 2,    1  | 0
* | 0.20  | 1, 2.5,  2  | 15
3 | 0.25  | 1, 3,    2  | 30

HTH。


5
+1。插值是这里简单有效的答案。的确,当车辆转弯时,三次插值可能会给出更好的结果,但是如果间隔足够小,则线性插值会很好地工作。
Kylotan

感谢您展示如何进行插值!这对我的游戏非常有用。但是,假设我想在时间41.15检索数组内的深处。您是否会开始搜索整个数组,直到找到记录> 41.15?
marimba

1
一个简单的线性搜索可能对您
有用
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.