Reproducción fantasma: almacenamiento y sincronización

11

Estoy trabajando en un juego de carreras de autos y acabo de implementar un sprite fantasma para reproducir carreras pasadas. Utilizo un motor de física y después de mucha lectura llegué a la conclusión de que la mejor manera de almacenar los datos fantasma para reproducirlos sería registrar la posición y rotación del automóvil en puntos de tiempo determinados, como por ejemplo se describe aquí: https: // gamedev. stackexchange.com/a/8380/26261 .

Pero, ¿cuál sería una buena manera de encontrar esos puntos de tiempo durante la repetición? Un ejemplo sería un registro con estos datos:

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

Pero tengo varios problemas con eso:

  1. Cuando reproduzco, es poco probable que alcance el punto de tiempo exacto en 3.19932 nuevamente; lo más probable es que tenga un punto de tiempo alrededor de 3.1 y tenga que encontrar el registro de coincidencia más cercano. Al interpolar, incluso la coincidencia más cercana arriba y abajo. Esto suena muy ineficiente y consume mucho tiempo?

  2. ¿En qué estructura de lista podría almacenar estos registros para una reproducción posterior? ¿Una matriz? ¿No significa eso que el tiempo de búsqueda de registros que coincidan con un cierto tiempo aumentará cuanto más larga sea la carrera?

  3. ¿Qué frecuencia debo usar para los puntos de tiempo? Cada cuadro sería, supongo, excesivo, más bien debería guardar, es decir, cada enésimo cuadro e interpolar entre ellos, lo que hace que las preguntas de almacenamiento en 2. sean aún más difíciles.

Entonces, ¿es esta idea incluso el enfoque correcto? En caso afirmativo, ¿cómo podría almacenar y recuperar los datos de manera eficiente? Tenga en cuenta que, en general, me gustaría usar la estructura de datos anterior, no estados de juego deterministas y registrar la entrada del usuario, etc.

¡Gracias por cualquier ayuda!

EDITAR: Me doy cuenta de que debería describir el entorno que uso: Cocos2D para iPhone. Hay un método update:(ccTime)delta. Idealmente, este método se llamaría cada 1/60 segundos, pero no hay garantía: deltaes el tiempo real transcurrido desde el último gametick y podría ser mucho más o menos de 1/60. Es en este método donde me gustaría almacenar el estado del juego actual.

marimba
fuente
2
Excelente pregunta Como muestra esto, una repetición precisa es más compleja de lo que piensas al principio, y tengo curiosidad por ver qué soluciones han encontrado las personas aquí.
Christian

Respuestas:

8

¿No significa eso que el tiempo de búsqueda de registros que coincidan con un cierto tiempo aumentará cuanto más larga sea la carrera?

No :)

Supongamos que lo almacena como una matriz (tenga en cuenta que las instantáneas están en orden cronológico, pero no espaciadas 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}},
    ...]

Luego, cuando comienza la repetición / juego, obtienes el primer y segundo elemento de la matriz:

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

Luego, en cada cuadro ( currentTimees la hora actual en este nuevo juego):

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

Por supuesto, esto podría optimizarse almacenando en caché algunos de los cálculos. No hay búsqueda a través de la matriz, solo busca índices específicos.

Supr
fuente
¡SI! Tengo que probar esto más tarde, pero parece ser lo que estaba buscando. ¡¡Gracias!!
marimba
15

No es muy dificil. Puede almacenar sus datos en puntos arbitrarios en el tiempo (cuanto más, mejor), y puede interpolar los valores de los datos en función de la marca de tiempo que está buscando y los datos de las dos marcas de tiempo registradas más cercanas, por ejemplo:

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

Ahora imagine que desea obtener la posición y la rotación en el momento 0.10. Como 0,10 se encuentra entre los puntos '1' (que significa 0,05 veces) y '2' (que significa 0,15 veces), debe interpolarlos.

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)

Lerpes solo interpolación lineal .

Así que vamos a llenar los vacíos con algunos ejemplos (*).

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
fuente
55
+1. La interpolación es la respuesta simple y efectiva aquí. Puede ser cierto que la interpolación cúbica puede dar resultados ligeramente mejores cuando el vehículo está girando, pero lineal funcionará bien si los intervalos son lo suficientemente pequeños.
Kylotan
¡Gracias por mostrar cómo interpolar! Esto será muy útil para mi juego. Pero digamos que me gustaría recuperar en el momento 41.15, en el fondo de la matriz. ¿Comenzaría a buscar en toda la matriz hasta que encuentre un registro> 41.15?
marimba
1
Una búsqueda lineal simple podría funcionar para usted, pero la búsqueda binaria es mejor cuando tiene una matriz ordenada: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski