Ghost replay - archiviazione e tempistica


11

Sto lavorando a un gioco di corse automobilistiche e ho appena implementato uno sprite fantasma per riprodurre le gare passate. Uso un motore fisico e dopo molte letture sono giunto alla conclusione che il modo migliore per archiviare i dati fantasma per la riproduzione sarebbe quello di registrare la posizione e la rotazione dell'auto in determinati punti temporali, come ad esempio descritto qui: https: // gamedev. stackexchange.com/a/8380/26261 .

Ma quale sarebbe un buon modo per trovare quei punti durante la riproduzione? Un esempio potrebbe essere un record con questi dati:

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

Ma ho diversi problemi con questo:

  1. Quando ripeto, è improbabile che raggiunga di nuovo il punto esatto a 3.19932 - più probabilmente, avrò un punto di tempo intorno a 3.1 e dovrò trovare il record di corrispondenza più vicino. Durante l'interpolazione, anche la corrispondenza più vicina sopra e sotto. Sembra molto inefficiente e richiede tempo?

  2. In quale struttura di elenco è possibile archiviare questi record per una riproduzione successiva? Un array? Ciò non significa che il tempo di ricerca per i record che corrispondono a un determinato tempo aumenterà quanto più lunga è la gara?

  3. Quale frequenza dovrei usare per i punti temporali? Ogni frame sarebbe - suppongo - eccessivo, piuttosto dovrei salvare cioè ogni ennesimo frame e interpolare in mezzo, il che rende le domande di archiviazione in 2. ancora più difficili.

Quindi questa idea è anche l'approccio giusto? In caso affermativo, come posso archiviare e recuperare i dati in modo efficiente? Si prega di notare che in genere vorrei utilizzare la struttura dei dati sopra, non gamestates deterministici e registrare input dell'utente ecc.

Grazie per qualsiasi aiuto!

EDIT: mi rendo conto che dovrei descrivere l'ambiente che uso: Cocos2D per iPhone. C'è un metodo update:(ccTime)delta. Idealmente, questo metodo dovrebbe essere chiamato ogni 1/60 secondi, ma non vi è alcuna garanzia: deltaè il tempo effettivo trascorso dall'ultimo gametick e potrebbe essere molto più o meno di 1/60. È in questo metodo che vorrei memorizzare l'attuale gamestate.


2
Ottima domanda Come dimostra questo, un replay preciso è più complesso di quanto si possa pensare all'inizio, e sono curioso di vedere quali soluzioni le persone hanno escogitato qui.
Christian,

Risposte:


8

Ciò non significa che il tempo di ricerca per i record che corrispondono a un determinato tempo aumenterà quanto più lunga è la gara?

No :)

Supponi di memorizzarlo come un array (nota che le istantanee sono in ordine cronologico, ma non spaziate uniformemente):

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

Quindi, quando inizia il replay / gioco, ottieni il primo e il secondo elemento dall'array:

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

Quindi in ogni fotogramma ( currentTimeè l'ora corrente in questo nuovo gioco):

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

Naturalmente questo potrebbe essere ottimizzato memorizzando nella cache alcuni dei calcoli. Non è possibile effettuare ricerche nell'array, ma solo cercare indici specifici.


SÌ! Devo provarlo più tardi, ma questo sembra essere quello che stavo cercando. Grazie!!
marimba,

15

Non è troppo difficile. Puoi archiviare i tuoi dati in punti temporali arbitrari (più, meglio è) e puoi interpolare i valori dei dati in base al timestamp che stai cercando e i dati da due timestamp registrati più vicini, ad esempio:

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

Ora immagina di voler ottenere posizione e rotazione al tempo 0.10. Dato che 0,10 è compreso tra i punti '1' (che significa 0,05 volte) e '2' (che significa 0,15 volte), è necessario interpolarli.

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è solo interpolazione lineare .

Quindi riempiamo gli spazi vuoti con alcuni esempi (*).

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. L'interpolazione è la risposta semplice ed efficace qui. Potrebbe essere vero che l'interpolazione cubica potrebbe dare risultati leggermente migliori quando il veicolo gira ma lineare funzionerà bene se gli intervalli sono abbastanza piccoli.
Kylotan,

Grazie per aver mostrato come interpolare! Questo sarà molto utile per il mio gioco. Ma supponiamo che vorrei recuperare al tempo 41.15, nel profondo dell'array. Inizieresti a cercare nell'intero array fino a quando non avessi trovato un record> 41.15?
marimba,

1
Una semplice ricerca lineare potrebbe funzionare per te, ma la ricerca binaria è migliore quando hai una matrice ordinata: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.