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

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


ДА! Я должен попробовать это позже, но это, похоже, то, что я искал. Спасибо!!
— Маримба

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.