Ghost Replay - хранение и время

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. еще более трудными.

Так эта идея даже правильный подход? Если да, как я могу эффективно хранить и извлекать данные? Обратите внимание, что я, как правило, хотел бы использовать приведенную выше структуру данных, а не детерминированные игровые состояния, запись пользовательского ввода и т. Д.

Спасибо за любую помощь!

РЕДАКТИРОВАТЬ: я понимаю, что я должен описать среду, которую я использую: Cocos2D для iPhone. Есть способ update:(ccTime)delta. В идеале этот метод вызывается каждые 1/60 секунды, но нет никакой гарантии - deltaэто фактическое время, прошедшее с момента последней игры, и оно может быть намного больше или меньше 1/60. Именно в этом методе я хотел бы сохранить текущее состояние игры.

маримба
источник
2
Отличный вопрос. Как это показывает, точное воспроизведение более сложное, чем вы думаете на первый взгляд, и мне любопытно посмотреть, какие решения люди придумали здесь.
Кристиан,

Ответы:

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)
}

Конечно, это можно оптимизировать, кэшируя некоторые расчеты. Там нет поиска по массиву, просто поиск конкретных индексов.

Supr
источник
ДА! Я должен попробовать это позже, но это, похоже, то, что я искал. Спасибо!!
Маримба
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

НТН.

Марчин Серединский
источник
5
+1. Интерполяция является простым и эффективным ответом здесь. Это может быть правдой, что кубическая интерполяция может дать немного лучшие результаты, когда автомобиль поворачивает, но линейная будет хорошо работать, если интервалы достаточно малы.
Kylotan
Спасибо за показ, как интерполировать! Это будет очень полезно для моей игры. Но скажем, я хотел бы получить в момент времени 41.15, глубоко внутри массива. Вы начнете искать по всему массиву, пока не найдете запись> 41.15?
Маримба
1
Простой линейный поиск может работать для вас, но бинарный поиск лучше, когда у вас есть отсортированный массив: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski