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.