NOTA: pregunté esto en Stack Overflow hace unos días, pero tuve muy pocas vistas y ninguna respuesta. Supuse que debería preguntar en gamdev.stackexchange en su lugar.
Esta es una pregunta / solicitud general de asesoramiento sobre el mantenimiento de un sistema de generación de procedimientos a través de múltiples actualizaciones posteriores al lanzamiento, sin romper el contenido generado anteriormente.
Estoy tratando de encontrar información y técnicas para evitar problemas de "efecto mariposa" al crear contenido procesal para juegos. Cuando se usa un generador de números aleatorios sembrados, se puede usar una secuencia repetitiva de números aleatorios para crear un mundo reproducible. Si bien algunos juegos simplemente guardan el mundo generado en el disco una vez generado, una de las características poderosas de la generación de procedimientos es el hecho de que puede confiar en la reproducibilidad de la secuencia numérica para recrear una región varias veces de la misma manera, eliminando la necesidad de persistencia. Debido a las limitaciones de mi situación particular, debo minimizar la persistencia y necesito confiar en el concentrado puramente sembrado tanto como sea posible.
El principal peligro en este enfoque es que incluso el más mínimo cambio en el sistema de generación de procedimientos puede causar un efecto mariposa que cambia el mundo entero. Esto hace que sea muy difícil actualizar el juego sin destruir los mundos que los jugadores están explorando.
La técnica principal que he estado usando para evitar este problema es diseñar la generación de procedimientos en múltiples fases, cada una de las cuales tiene su propio generador de números aleatorios. Esto significa que cada subsistema es autónomo, y si algo se rompe, no afectará a todo en el mundo. Sin embargo, parece que todavía tiene un gran potencial de "rotura", incluso si está en una parte aislada del juego.
Otra posible forma de lidiar con este problema podría ser mantener versiones completas de sus generadores dentro del código, y seguir usando el generador correcto para una instancia mundial dada. Sin embargo, esto me parece una pesadilla de mantenimiento, y tengo curiosidad por saber si alguien realmente hace esto.
Por lo tanto, mi pregunta es realmente una solicitud de asesoramiento general, técnicas y patrones de diseño para tratar este problema del efecto mariposa, especialmente en el contexto de las actualizaciones del juego posteriores al lanzamiento. (Esperemos que esa no sea una pregunta demasiado amplia).
Actualmente estoy trabajando en Unity3D / C #, aunque esta es una pregunta independiente del lenguaje.
ACTUALIZAR:
Gracias por las respuestas
Se parece cada vez más a que los datos estáticos son el mejor y más seguro enfoque, y también que cuando almacenar una gran cantidad de datos estáticos no es una opción, tener una campaña larga en un mundo generado requeriría una versión estricta de los generadores utilizados. La razón de la limitación en mi caso es la necesidad de un guardado / sincronización en la nube basado en dispositivos móviles. Mi solución puede ser encontrar formas de almacenar pequeñas cantidades de datos compactos sobre cosas esenciales.
Creo que el concepto de "Jaulas" de Stormwind es una forma particularmente útil de pensar sobre las cosas. Una jaula es básicamente un punto de reinicio, evitando los efectos de pequeños cambios, es decir, enjaulando a la mariposa.
Respuestas:
Creo que has cubierto las bases aquí:
Usar múltiples generadores o volver a sembrar a intervalos (por ejemplo, usar hashes espaciales) para limitar la propagación de los cambios. Esto probablemente funcione para el contenido cosmético, pero como usted señala, aún puede causar roturas dentro de una sección.
Realizar un seguimiento de la versión del generador utilizada en el archivo de guardar y responder adecuadamente. Lo que significa "apropiado" podría ser ...
n
versiones del generador en su ejecutable. Si el archivo de guardado usa una de esas versiones recientes, (ofrezca) actualizar el archivo de guardado a la última versión. Esto usa el generador apropiado para desempaquetar cualquier estado desactualizado en literales (o en deltas de la salida del nuevo generador en la misma semilla, si son muy similares). Cualquier estado nuevo de aquí en adelante proviene de los generadores más nuevos. Sin embargo, los jugadores que no juegan durante mucho tiempo podrían quedarse atrás. Y en el peor de los casos, terminas almacenando todo el estado del juego en forma literal, en cuyo caso también podrías ...Si espera alterar su lógica de generación con frecuencia y no quiere romper la compatibilidad con versiones anteriores, no confíe en el determinismo del generador: guarde todo su estado en su archivo de guardado. (es decir, "Destrozarlo desde la órbita. Es la única forma de estar seguro")
fuente
Podría decirse que la fuente principal de este efecto mariposa no es la generación de números, que debería ser lo suficientemente fácil como para mantener la determinación de un solo generador de números, sino más bien el uso de esos números por el código del cliente. Los cambios de código son el verdadero desafío para mantener las cosas estables.
Código: Pruebas unitarias La mejor manera de garantizar que algún cambio menor en algún lugar no se manifieste involuntariamente en otro lugar, es incluir pruebas unitarias exhaustivas para cada aspecto generativo, en su construcción. Esto es cierto para cualquier pieza de código compacto en el que cambiar una cosa puede afectar a muchas otras: necesita pruebas para todo para que pueda ver en una sola compilación lo que se ha visto afectado.
Números: secuencias periódicas / ranuras Supongamos que tiene un generador de números que sirve para todo. No asigna significado, solo escupe números en secuencia, como cualquier PRNG. Dada la misma semilla en dos carreras, obtenemos las mismas secuencias, ¿sí? Ahora piensa un poco en las cosas y decides que habrá unos 30 aspectos de tu juego que regularmente necesitarán un valor aleatorio. Aquí asignamos una secuencia de ciclismo de 30 espacios, por ejemplo, cada primer número de la secuencia es un diseño de terreno irregular, cada segundo número es perturbaciones del terreno ... etc. ... cada décimo número agrega algún error al estado de IA para el realismo. Entonces tu período es 30.
Después de 10, tienes 20 espacios libres que puedes usar para otros aspectos a medida que avanza el diseño del juego. El costo aquí es, por supuesto, que debe generar números para las ranuras 11-30 a pesar de que actualmente no están en uso , es decir, completar el período, para volver a la siguiente secuencia de 1-10. Eso tiene un costo de CPU, aunque debería ser menor (dependiendo del número de ranuras libres). La otra desventaja es que debe asegurarse de que su diseño final pueda acomodarse en la cantidad de ranuras que puso a disposición al comienzo de su proceso de desarrollo ... y cuanto más asigne al comienzo, más ranuras "vacías" potencialmente tienes que pasar por cada uno para que las cosas funcionen.
Los efectos de esto son:
Por supuesto, habrá un largo período durante el cual tu juego no estará disponible para el público, en alfa, por así decirlo, para que puedas reducir de 30 a 20 aspectos sin afectar a ningún jugador, solo a ti mismo, si te das cuenta de que que había asignado manera demasiadas ranuras en la salida. Por supuesto, esto ahorraría algunos ciclos de CPU. Pero tenga en cuenta que una buena función hash (que puede escribir usted mismo) debería ser extremadamente rápida. Por lo tanto, tener que ejecutar espacios adicionales no debería ser costoso.
fuente
Si desea persistencia con PCG, le sugiero que trate el código PCG en sí mismo como datos . Del mismo modo que conservaría los datos en las revisiones con contenido regular, con contenido generado, si desea conservarlos en las revisiones, deberá conservar el generador.
Por supuesto, el enfoque más popular es convertir los datos generados en datos estáticos, como has mencionado.
No conozco ejemplos de juegos que tengan muchas versiones de generador, porque la persistencia es inusual en los juegos de PCG; es por eso que permadeath a menudo va de la mano con PCG. Sin embargo, hay muchos ejemplos de múltiples PCG, incluso del mismo tipo, dentro del mismo juego. Por ejemplo, Unangband tiene muchos generadores separados para salas de mazmorras y, a medida que se agregan nuevos, los viejos siguen funcionando igual. Si eso se puede mantener depende de su implementación. Una forma de mantenerlo mantenible es usar scripts para implementar sus generadores, manteniéndolos aislados con el resto del código del juego.
fuente
Mantengo un área de aproximadamente 30000 kilómetros cuadrados, con capacidad para aproximadamente 1 millón de edificios y otros objetos, además de ubicaciones aleatorias de cosas misceláneas. Una simulación al aire libre de c. Los datos almacenados son de aproximadamente 4 GB. Tengo la suerte de tener espacio de almacenamiento, pero no es ilimitado.
Aleatorio es aleatorio, no controlado. Pero uno puede enjaularlo un poco:
Eso es todo. Las jaulas también consumen datos, desafortunadamente.
Hay un dicho en finlandés, Hajota ja hallitse. Se traduce en dividir y conquistar .
Rápidamente abandoné la idea de una definición precisa de los detalles más pequeños. Random quiere libertad, entonces obtuvo la libertad. Deje volar a la mariposa, dentro de su jaula. En cambio, me concentré en tener una forma rica de definir (¡y mantener !!) las jaulas. No importa qué automóviles sean, siempre que sean azules o azul oscuro (un empleador aburrido dijo una vez :-)). "Azul o azul oscuro" es la jaula (muy pequeña) aquí, a lo largo de la dimensión de color.
¿Qué es manejable para controlar y gestionar espacios numéricos?
En cuanto al mantenimiento, y en cuanto a compatibilidad de versiones ... tenemos
: if version = n then
: elseif version = m then ...
Sí, la base del código se hace más grande :-).
Cosas familiares Su manera correcta de seguir adelante sería definir un método rico para dividir y conquistar , y sacrificar algunos datos al respecto. Luego, cuando sea posible, otorgue libertad de aleatorización (local), donde no es crucial controlarla.
No es totalmente incompatible con el divertido "nuke it fom orbit" propuesto por DMGregory, pero ¿tal vez use armas nucleares pequeñas y precisas? :-)
fuente