Odtwarzanie duchów - przechowywanie i czas

11

Pracuję nad grą wyścigową i właśnie wdrożyłem ducha duchowego do odtwarzania poprzednich wyścigów. Korzystam z silnika fizyki i po wielu lekturach doszedłem do wniosku, że najlepszym sposobem przechowywania danych o duchach do odtworzenia byłoby zapisanie pozycji i obrotów samochodu w danych punktach czasowych, jak na przykład opisano tutaj: https: // gamedev. stackexchange.com/a/8380/26261 .

Ale jaki byłby dobry sposób na znalezienie tych punktów czasowych podczas powtórki? Przykładem może być zapis z tymi danymi:

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

Ale mam z tym kilka problemów:

  1. Kiedy ponownie odtwarzam, jest mało prawdopodobne, że ponownie dotrę do dokładnego punktu czasowego 3.19932 - bardziej prawdopodobne, że będę miał punkt czasowy około 3.1 i będę musiał znaleźć najbliższy pasujący rekord. Podczas interpolacji nawet najbliższe dopasowanie powyżej i poniżej. Brzmi bardzo nieefektywnie i zajmuje dużo czasu?

  2. W jakiej strukturze listy mogę przechowywać te rekordy do późniejszego odtworzenia? Tablica? Czy to nie znaczy, że czas wyszukiwania rekordów pasujących do określonego czasu wydłuży się, im dłużej trwa wyścig?

  3. Jakiej częstotliwości powinienem użyć dla punktów czasowych? Każda klatka byłaby - myślę - przesadą, raczej powinnam zapisać, tj. Każdą nią klatkę i interpolować pomiędzy nimi, co sprawia, że ​​pytania w 2. pamięci są jeszcze trudniejsze.

Czy ten pomysł jest nawet właściwym podejściem? Jeśli tak, w jaki sposób mogę skutecznie przechowywać i odzyskiwać dane? Należy pamiętać, że generalnie chciałbym skorzystać z powyższej struktury danych, a nie deterministycznych gamestate i rejestrować dane wejściowe użytkownika itp.

Dzięki za wszelką pomoc!

EDYCJA: Zdaję sobie sprawę, że powinienem opisać środowisko, którego używam: Cocos2D na iPhone'a. Istnieje metoda update:(ccTime)delta. Najlepiej byłoby, gdyby ta metoda była wywoływana co 1/60 sekundy, ale nie ma gwarancji - deltajest to rzeczywisty czas, jaki upłynął od ostatniego gametu i może być znacznie większy lub mniejszy niż 1/60. Właśnie w tej metodzie chciałbym zapisać bieżący stan gry.

marimba
źródło
2
Doskonałe pytanie. Jak to pokazuje, dokładna powtórka jest bardziej złożona, niż mogłoby się początkowo wydawać, i jestem ciekawy, jakie rozwiązania wymyślili tutaj ludzie.
Christian

Odpowiedzi:

8

Czy to nie znaczy, że czas wyszukiwania rekordów pasujących do określonego czasu wydłuży się, im dłużej trwa wyścig?

Nie :)

Załóżmy, że przechowujesz go jako tablicę (pamiętaj, że migawki są uporządkowane chronologicznie, ale nie są równomiernie rozmieszczone):

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}},
    ...]

Następnie, gdy rozpoczyna się powtórka / gra, otrzymujesz pierwszy i drugi element z tablicy:

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

Następnie w każdej klatce ( currentTimejest aktualny czas w tej nowej grze):

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

Oczywiście można to zoptymalizować, buforując niektóre obliczenia. Nie ma przeszukiwania tablicy, tylko wyszukiwanie określonych wskaźników.

Supr
źródło
TAK! Muszę to wypróbować później, ale wydaje się, że tego właśnie szukałem. Dzięki!!
marimba
15

To nie jest zbyt trudne. Możesz przechowywać swoje dane w dowolnych punktach czasowych (im więcej, tym lepiej), i możesz interpolować wartości danych na podstawie poszukiwanego znacznika czasu oraz danych z dwóch najbliższych zarejestrowanych znaczników czasu, np .:

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

Teraz wyobraź sobie, że chcesz uzyskać pozycję i obrót w czasie 0.10. Ponieważ 0,10 znajduje się między punktami „1” (co oznacza czas 0,05) i „2” (co oznacza czas 0,15), musisz je interpolować.

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)

Lerpjest po prostu interpolacją liniową .

Wypełnijmy zatem luki kilkoma przykładami (*).

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.

Marcin Seredynski
źródło
5
+1. Interpolacja jest tutaj prostą i skuteczną odpowiedzią. Być może prawdą jest, że interpolacja sześcienna może dać nieco lepsze wyniki, gdy pojazd skręca, ale liniowy będzie działał dobrze, jeśli odstępy będą wystarczająco małe.
Kylotan
Dziękujemy za pokazanie, jak interpolować! Będzie to bardzo przydatne w mojej grze. Ale powiedzmy, że chciałbym odzyskać w czasie 41.15, głęboko w tablicy. Czy zacząłbyś przeszukiwać całą tablicę, aż znajdziesz rekord> 41,15?
marimba
1
Proste przeszukiwanie liniowe może być dla Ciebie przydatne, ale wyszukiwanie binarne jest lepsze, gdy masz posortowaną tablicę: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski