Replay fantôme - stockage et timing

11

Je travaille sur un jeu de course automobile et je viens de mettre en place un sprite fantôme pour rejouer les courses passées. J'utilise un moteur physique et après de nombreuses lectures, je suis arrivé à la conclusion que la meilleure façon de stocker les données fantômes pour la relecture serait d'enregistrer la position et la rotation de la voiture à des moments donnés, comme par exemple décrit ici: https: // gamedev. stackexchange.com/a/8380/26261 .

Mais quel serait un bon moyen de trouver ces points temporels lors de la relecture? Un exemple serait un enregistrement avec ces données:

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

Mais j'ai plusieurs problèmes avec ça:

  1. Lorsque je rejoue, il est peu probable que j'atteigne à nouveau le point de temps exact à 3.19932 - plus probablement, j'aurai un point de temps autour de 3.1 et je devrai trouver l'enregistrement correspondant le plus proche. Lors de l'interpolation, même la correspondance la plus proche au-dessus et en dessous. Cela semble très inefficace et prend du temps?

  2. Dans quelle structure de liste puis-je stocker ces enregistrements pour une relecture ultérieure? Un tableau? Cela ne signifie-t-il pas que le temps de recherche des enregistrements correspondant à un certain temps augmentera plus la course est longue?

  3. Quelle fréquence dois-je utiliser pour les points temporels? Chaque trame serait -Je suppose- exagérée, je devrais plutôt enregistrer chaque nième trame et interpoler entre les deux, ce qui rend les questions de stockage en 2. encore plus difficiles.

Cette idée est-elle même la bonne approche? Si oui, comment pourrais-je stocker et récupérer efficacement les données? Veuillez noter que j'aimerais généralement utiliser la structure de données ci-dessus, pas les gamestates déterministes et l'enregistrement des entrées utilisateur, etc.

Merci pour toute aide!

EDIT: Je me rends compte que je devrais décrire l'environnement que j'utilise: Cocos2D pour iPhone. Il y a une méthode update:(ccTime)delta. Idéalement, cette méthode serait appelée toutes les 1/60 secondes, mais il n'y a aucune garantie - deltac'est le temps réel écoulé depuis le dernier gametick et pourrait être beaucoup plus ou moins que 1/60. C'est dans cette méthode que je voudrais stocker le gamestate actuel.

marimba
la source
2
Excellente question. Comme cela le montre, une rediffusion précise est plus complexe que vous ne le pensez au début, et je suis curieux de voir quelles solutions les gens ont trouvées ici.
Christian

Réponses:

8

Cela ne signifie-t-il pas que le temps de recherche des enregistrements correspondant à un certain temps augmentera plus la course est longue?

Nan :)

Supposons que vous le stockiez sous forme de tableau (notez que les instantanés sont dans l'ordre chronologique, mais pas régulièrement espacés):

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

Ensuite, lorsque la relecture / partie commence, vous obtenez le premier et le deuxième élément du tableau:

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

Puis dans chaque image ( currentTimec'est l'heure actuelle dans ce nouveau jeu):

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

Bien sûr, cela pourrait être optimisé en mettant en cache certains des calculs. Il n'y a pas de recherche dans le tableau, il suffit de rechercher des indices spécifiques.

Supr
la source
OUI! Je dois essayer cela plus tard, mais cela semble être ce que je cherchais. Merci!!
marimba
15

Ce n'est pas trop dur. Vous pouvez stocker vos données à des moments arbitraires (plus, mieux c'est), et vous pouvez interpoler les valeurs des données en fonction de l'horodatage que vous recherchez et des données des deux horodatages enregistrés les plus proches, par exemple:

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

Imaginez maintenant que vous souhaitez obtenir la position et la rotation au temps 0,10. Comme 0,10 se situe entre les points «1» (ce qui signifie 0,05 fois) et «2» (ce qui signifie 0,15 fois), vous devez les interpoler.

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)

Lerpest juste une interpolation linéaire .

Remplissons donc les lacunes avec quelques exemples (*).

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
la source
5
+1. L'interpolation est la réponse simple et efficace ici. Il pourrait être vrai que l'interpolation cubique pourrait donner des résultats légèrement meilleurs lorsque le véhicule tourne, mais linéaire fonctionnera bien si les intervalles sont suffisamment petits.
Kylotan
Merci d'avoir montré comment interpoler! Ce sera très utile pour mon jeu. Mais disons que je voudrais récupérer au moment 41.15, profondément à l'intérieur du tableau. Souhaitez-vous commencer à rechercher dans l'ensemble du tableau jusqu'à ce que vous trouviez un enregistrement> 41,15?
marimba
1
Une recherche linéaire simple pourrait fonctionner pour vous, mais la recherche binaire est meilleure lorsque vous avez un tableau trié: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski