Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Guardar Replays

Iniciado por ethernet, 01 de Septiembre de 2005, 05:27:31 PM

« anterior - próximo »

Zaelsius

Cita de: "CoLSoN2"
Cita de: "samsaga2"Ricemos el rizo (mas que nada por putear :P) y si entre version y version del juego cambias la funcion rand? Los relplays no serian compatibles.
¿Qué función rand? Se supone que usas la de la librería estándar de C, y sólo guardas la semilla.

Aunque lo de que en diferentes versiones dejen de funcionar las replays antiguas no es nada nuevo, en según qué parches de WarCraft3 pasa esto, no estoy seguro si en otros juegos como AoE 2 o StarCraft también.
¿Y si el juego es multiplataforma y usa el rand de dos implementaciones libc diferentes? xD

Sí, es cierto que el 90% de los parches de StarCraft y Warcraft III invalidaban los replays de las partidas anteriores, lo cual era una putada porque ya no podias ver las repeticiones de jugadores coreanos "pros" que te habias bajado la semana anterior.. :P

Pogacha

 En realidad para guardar una demo lo que se hace es tener encapsulado el estado del juego como tambien el estado de los sprites, de tal manera que los guardas en un array circular donde guardes los ultimos 30 segundos por ejemplo ..., esto funciona realmente bien para timesteps fijos, para timesteps  variables debes ademas guardar el delta_tiempo y crear una funcion que interpole los estados para la reproducción. Tambien se aplica a fisica con los estados fisicos y es compatible para la transmicion por red, las funciones interpolar_estados es algo muy usado y el grabar demos es otra aplicación mas.
Saludos

CoLSoN2

 Creo que no te entiendo bien Pogacha. Sugieres que guardemos el estado del juego cada 30 segundos y luego al reproducir la partida, interpoles entre esos "keyframes"  que están a 30 segundos de distancia? Porque eso es totalmente ridículo.
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

Topper

 Si no he entendido mal, pogacha quiere decir que se guarden 30 segundos de partida siempre. Cuando se alcanza el límite de tiempo se vuelve a grabar desde el principio. De ahí la lista circular de la que habla...

El problema que yo le veo a eso es que como no se limpie la lista al finalizar el tiempo, se mezclarán repeticiones entre tramos de tiempo.  :ph34r:  

ethernet

Cita de: "Topper"Si no he entendido mal, pogacha quiere decir que se guarden 30 segundos de partida siempre. Cuando se alcanza el límite de tiempo se vuelve a grabar desde el principio. De ahí la lista circular de la que habla...

El problema que yo le veo a eso es que como no se limpie la lista al finalizar el tiempo, se mezclarán repeticiones entre tramos de tiempo.  :ph34r:
En esa lista circular siempre tienes un puntero al principio, no habrá problemas por ello.

CoLSoN2

 Yo sigo pensando que usando fixed timestep (por ejemplo, 100 frames por segundo de tus llamadas a Update() o lo que sea, como hace el PopCap Framework) no debes tener problemas luego para reproducir los eventos que leas.
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

Jare

 Alguien me ha mentado? :)

Las dos formas habituales de guardar replays son:

- guardando pares (tiempo, evento del jugador), y teniendo un motor de juego completamente determinista, que si recibe esa misma serie, generará unos resultados IDENTICOS a cada paso. Este método es típico de los RTS (aunque el Total Annihilation usaba el segundo sistema).

- guardando las posiciones de los bichos, y los eventos que se producen en el juego (disparo, daño, muerte). Esto es lo que suelen hacer los FPS (aunque el Doom original usaba el método anterior).

El método elegido suele tener mucho que ver con el sistema de red. Ya que todos los jugadores de una partida en red tienen que ver la misma partida, el problema es similar al de reproducir la partida posteriormente.

El segundo método es en principio más sencillo y flexible, pero potencialmente genera más tráfico, todo depende de la cantidad de entidades móviles. El primero genera unos problemas del carajo, porque conseguir que el motor sea 100% determinista es un horror. 99.999% no sirve, en el momento en que la reproducción se desvíe del original, el juego empezará a tomar decisiones diferentes, y la reproducción se degenera rápidamente, el conocido "efecto mariposa".

- Hay que tener muy controladitos los generadores de números aleatorios. Se suelen usar dos: uno para la lógica y cualquier cosa que pueda afectar al estado del juego, y uno para los efectos visuales y cosas que jamás afecten a la lógica. De esa forma, los rand() que hagas para las partículas (que pueden estar desactivadas o con niveles de detalle) no afectan a las decisiones de la IA.

- Hay que tener cuidado con las operaciones en coma flotante, si reproduces la partida en una máquina con una CPU diferente (Mac vs. PC por ejemplo) los decimales de las operaciones pueden diferir, y lo harán. Al carajo la reproducción!

- Hay que cuantizar el tiempo de ejecución de la lógica a intervalos de tiempo fijos, o al menos almacenar los tiempos entre ticks de lógica, y asegurarse de que la reproducción los sigue fielmente. No puedes hacer ninguna virguería para ajustar el tiempo de cálculo de la lógica en función del framerate, y por supuesto esparcir la lógica en varios thread es imposible excepto en operaciones muy concretas (para las que no suele merecer la pena sacar un thread aparte).

- Cualquier chorrada, cualquier bug, cualquier valor no inicializado, cualquier cambio en los valores o algoritmos (típico al sacar parches), cambiará los resultados de la lógica y destruirá la reproducción. Necesitas incluir trazas para depurar estos problemas, normalmente grabas el valor de la semilla de rand() de la lógica y lo compruebas constantemente para detectar si se ha producido una desincronización.

- La reproducción tiene que realizarse en orden y desde el principio: no puedes arrancar a mitad, saltarte unos minutos, o ir hacia atrás. Aunque quieras dar esas capacidades al juego, por dentro tendrás que reproducir cada tick hasta el punto que quieres mostrar.

Muchos muchos problemas, pero las ventajas también son potentes:

- Tienes la herramienta perfecta para depurar un crash: saca el replay para reproducir exactamente la situación que provocó el casque, mucho mejor que el tester te cuente "pues llevé a la tropa cerca del pueblo y luego lancé un ataque contra uno de los defensores".

- El tamaño de las replays es muy pequeño.

- Cuando se usa este sistema para implementar la red, resulta casi imposible hacer muchas trampas en red, como las que hacen los trainers típicos, de darte vida o pasta o matar a un enemigo porque sí. Lo más que puedes hacer es mostrar

El tema da para un libro entero, pero como decía al principio, lo mejor es leer artículos sobre sistemas de red - una vez que tienes el juego corriendo en red, incluir un sistema de replay es casi trivial.

http://www.gamasutra.com/features/20010322.../terrano_01.htm

http://www.gamasutra.com/features/20050823...itkine_02.shtml

ethernet

Cita de: "Jare"- La reproducción tiene que realizarse en orden y desde el principio: no puedes arrancar a mitad, saltarte unos minutos, o ir hacia atrás. Aunque quieras dar esas capacidades al juego, por dentro tendrás que reproducir cada tick hasta el punto que quieres mostrar.
Bueno, yo me refería a algo menos avanzado, algo así como un replay de lo que pasó pero sin volver a calcular la lógica. Por ejemplo, si tenemos la simulación de una pelota botando, hay dos opciones:

- cualquiera de las que has dicho tú
- guardar pares (tiempo, estado_pelota) para después reproducirlo sin recurrir a la lógica del juego símplemente interpolando los valores de estado pelota para el instante de tiempo especificado. Vamos, saltarse la lógica del juego y usar la ya calculada.

En el segundo caso se podría reproducir a partir de cierto instante, rebobinar, etc.

El tema que planteaba era más parecido a el replay de un juego de fútbol en el que te repiten los goles, das para alante, atrás, etc, que a un sistema de reproducción exacta de la partida. No sé si es un sistema viable para sistemas con muchas entidades, pero para un juego con, como mucho, 15 ó 20 entidades sin muchas florituras creo que puede funcionar bien.

La verdad es que echando un ojo a los artículos que has puesto me asombra que el código que he usado yo reproduzca tan bien lo que guardé XD, tendré que probarlo en otra máquina.

 

Jare

 Ah vale, para las repeticiones de momentos puntuales sin duda el segundo método, guardar el estado visual de los objetos en cada frame (or cada X frames),  e interpolar. Como sugerían, un buffer circular es perfecto para eso. Es exactamente lo que hicimos en el NBA Inside Drive (que para todo lo demás usaba ticks fijos y motor determinista). Allí además teníamos otros buffers en donde copiabamos ese buffer circular y así contar con una selección de "las mejores jugadas" al final del partido.

seryu

 Supongo que los juegos deportivos son los que mayor uso dan a los replays.

Jare, enrollate y cuentanos como funciona los savestates en el napoleon  :P  

Zaelsius

 A mí me impresionó la capacidad de "rebobinar" en el tiempo en los últimos Prince of Persia :D.

Me pareció algo original y que afectaba positivamente la jugabilidad...  ahora es cuando me decís que el juego 'X' ya hacía lo mismo hace 5 años  :ph34r:  

seryu

 Eso ya lo hacian juegos anteriores.  :P

Pero si, en el prince of persia quedaba espectacular, y ademas parecia guardar intacta toda la partida. Seguro que usaban el sistema que comenta jare, teniendo en cuenta que la marcha atras estaba limitada a un tiempo maximo (se supone en el juego por la cantidad de arena).





Pogacha

 Si tienes la función guardar partida (), la idea seria guardarla en cada frame en un arreglo de memoria ... en definitiva yo te recomendaria usar un metodo visual de demos, que consiste en guardar unicamente los estados de los graficos y sonidos tan solo (todo en un arreglo circular), los sonidos puedes manejarlos como eventos y los graficos como sprites donde pones que frame de animación tiene, que posicion y a que sprite del timestep anterior corresponde y listo, luego tener el motor preparado para que haga los replays. Puedes ir mas allá y hacer que las entidades hereden de las clases sprites las cuales sean dibujadas por el motor y así tienes un motor con solo una linea de render, a fin de cuentas puedes usar fixed time steps y con solo agregar una funcion "interpolar estados" puedes tener time steps variables ( de aca lo de guardar tambien en la info de estado del sprite un puntero al estado anterior).

Saludos.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.