Tiempo de carga previo al juego vs. tiempo de carga del juego

9

Estoy desarrollando un juego en el que se incluye un laberinto aleatorio .
Hay algunas criaturas de IA que acechan en el laberinto. Y quiero que sigan algún camino de acuerdo con la forma del laberinto.

Ahora hay dos posibilidades para que implemente eso, la primera forma (que usé) es calculando varias rutas de acecho deseadas una vez que se crea el laberinto.
El segundo es calcular una ruta que una vez se necesitaba calcular, cuando una criatura comienza a acecharla.

Mi principal preocupación son los tiempos de carga. Si calculo muchos caminos en la creación del laberinto, el tiempo de precarga es un poco largo, así que pensé en calcularlos cuando fuera necesario.
Por el momento, el juego no es "pesado", por lo que el cálculo de las rutas a mitad del juego no se nota, pero me temo que lo hará una vez que se vuelva más complicado.

Cualquier sugerencia, comentario, opinión, será de ayuda.

Editar:

Por ahora, psea ​​el número de rutas precalculadas, una criatura tiene la probabilidad de 1/ptomar una nueva ruta (lo que significa un cálculo de ruta) en lugar de una existente.
Una criatura no comienza su patrulla hasta que la ruta esté completamente calculada, por lo que no hay que preocuparse de que muera en el proceso.

Guardián
fuente
8
En este punto, sería una optimización previa. Lo dejaría como está hasta que haya un problema.
John McDonald
3
Y para intervenir un poco con @JohnMcDonald, si puedes calcular estos caminos a mitad del juego y no se nota, ¿cuánto pueden contribuir en el momento de la carga?
James

Respuestas:

6

BerickCook ha expresado la idea correctamente. Deje los cálculos donde están si funcionan correctamente ahora.

Si puedes hacer el cálculo antes y estás seguro de que no los necesitarás a mitad del juego, entonces hazlo antes. De lo contrario, hágalo después de cargar. Si el cálculo durante el juego no se nota, puedes hacerlo allí. Si en algún momento la complejidad evoluciona y los cálculos se vuelven demasiado pesados, comience a optimizar.

Pero una cosa: si sus cálculos se implementan para ejecutarse a mitad del juego, siempre puede forzarlos a que se realicen durante la carga de todos modos.

Existen numerosas soluciones:

  • calcular / cargar las rutas durante la creación / carga de nivel
  • usa un segundo hilo para calcular las rutas
  • optimiza tus algoritmos
  • use un sistema interrumpible si no tiene acceso a subprocesos.

He visto y usado la última opción en un juego de mercado masivo. Simplemente asegúrese de guardar correctamente todos los datos necesarios para que se reanude el cálculo, y verifique regularmente el tiempo / operaciones restantes durante el cálculo.

Dependiendo de su caso, el sistema interrumpible podría proporcionar soluciones preliminares y parciales que se pueden utilizar antes de que finalice el cálculo.


Editar : respondiendo @Keeper

El "algoritmo interrumpible" fue útil solo debido a las restricciones que teníamos. Básicamente nos paliamos a la falta de multihilo.

En un momento tuvimos un juego donde la IA tenía que calcular grandes cantidades de movimientos basados ​​en múltiples diccionarios. Durante este cálculo, todas las animaciones se detendrían porque los diccionarios se expandieron con más datos y el conjunto de datos que contenía los datos cambió y fue menos eficiente cuando el juego se adaptó para el modo multijugador (donde la IA tenía que interactuar incluso para los movimientos del jugador). Solo teníamos un subproceso disponible para el bucle del juego (el imperativo es que el código multiplataforma debe ejecutarse en todas las plataformas compatibles). En este punto, se decidió romper el algoritmo de cálculo para poder interrumpirlo. Por lo tanto, no podíamos usar el sistema recursivo que estaba en su lugar ya que las variables no se podían guardar. Las funciones fueron reemplazadas por objetos que simplemente contenían todas las variables y punteros necesarios para los objetos primarios y secundarios. Yo no'

  • guardar el estado de sus cálculos actuales
  • interrumpir al final de un ciclo o durante un ciclo (cuando un objeto hijo interrumpe)
  • salir cuando se acabe el tiempo
  • reanudar donde se detuvo, ya sea reiniciando un bucle en el índice derecho o llamando al objeto secundario que se encuentra actualmente en la parte superior de su pila secundaria.
  • limpiar todo si se interrumpe el cálculo
  • dar el mejor resultado parcial.

Solo las operaciones más caras se dividieron en objetos separados y tomó algún tiempo encontrar los lugares correctos donde pudiéramos detener los cálculos, pero al final funciona muy bien.

Perdimos el rendimiento, pero el rendimiento percibido fue mucho mejor para el usuario, ya que las animaciones funcionaron sin problemas en todas las plataformas, todas las plataformas podían usar los diccionarios más grandes sin sufrir animaciones o congelaciones entrecortadas. Además, esto nos permitió ejecutar varias instancias en paralelo cuando lo necesitábamos más tarde.

Por supuesto, ahora en el iPhone y iPad el juego no necesita esto, usar un segundo hilo sería ideal. Pero sospecho que el código sigue ahí.

Coyote
fuente
Gracias, eso suena muy interesante. ¿Puede por favor elaborar sobre el concepto de "sistema interrumpible" (por qué y cómo). (googlear no fue tan útil ...). Agregaré algunos detalles a la pregunta.
Guardián
@Keeper Espero haber respondido tu pregunta sobre la parte interrumpible. Es un dolor en el pero que hacer y no es relevante en la mayoría de los sistemas modernos.
Coyote
Aunque no voy a usar este método por ahora, me abriste los ojos a algo nuevo. Su respuesta merece ser aceptada.
Guardián
8

Por ahora, dado que el cálculo de las rutas a mitad del juego es imperceptible, ese es el enfoque ideal. Si / cuando llega al punto en que los cálculos interrumpen el juego, entonces cámbielo a calcular las rutas antes de cargar el nivel.

Los tiempos de carga iniciales más largos son perdonables, pero las fluctuaciones aleatorias de FPS durante el juego generalmente no lo son.

BerickCook
fuente
2

Una posibilidad si la necesita es mover los cálculos a un segundo subproceso, ya que casi todas las PC tienen más de un núcleo de CPU en estos días.

El proceso básico sería:

  • Cuando una criatura solicita una ruta, agregue la solicitud a una cola de solicitudes seguras de subprocesos y señale el subproceso.
  • La criatura permanece inactiva mientras espera que el hilo saque la entrada de esa cola, la procese y la coloque en una lista completa.
  • Cada cuadro verifica la lista completa desde el hilo principal y asigna rutas completas a las criaturas.

También debes tener cuidado con algunos casos extra adicionales (por ejemplo, ¿qué pasa si la criatura muere antes de que se procese la solicitud?).

Adán
fuente
2
Si no desea introducir subprocesos, también puede calcular su ruta de forma incremental (por ejemplo, calcular algunos pasos en cada cuadro).
bummzack