Uso del tiempo de inactividad en juegos por turnos (RPG) para actualizar

9

Si tomas un juego de rol basado en turnos, habrá largos períodos de tiempo en que no sucederá nada porque el juego está en bucle sobre 'wait_for_player_input'. Naturalmente, parece sensato usar este tiempo para actualizar cosas.

Sin embargo, esto inmediatamente parece sugerir que necesitaría ser enhebrado. ¿Es posible este tipo de diseño en un solo hilo?

loop:  
if not check_something_pressed:  
    update_a_very_small_amount  
else  
  keep going

Pero si decimos que 'a_very_small_amount' solo está actualizando un solo objeto en cada ciclo, la actualización será muy lenta.

¿Cómo harías esto, preferiblemente en un solo hilo?

EDITAR: He etiquetado este lenguaje independiente ya que parece lo más sensato, aunque cualquier cosa más específica para Python sería genial. ;-)

Segunda edición: este juego no planea tener ningún componente animado; es decir, actualmente lo estoy ejecutando como una entrada de espera para el jugador, luego actualizo todo y dibujo. Entonces, en lugar de X FPS, depende de la velocidad del usuario.

El pato comunista
fuente

Respuestas:

7

Sí, es posible hacerlo en un solo hilo. Sin embargo, en términos generales, querrá actualizar los objetos en cada cuadro y no solo cuando haya ciclos de reserva. Sus animaciones y movimientos se desconectarán de la velocidad de fotogramas y se verán entrecortados si no lo hace. Si estás hablando más sobre actualizaciones de IA o sobre otra cosa que no necesita ser en tiempo real, le pondría un temporizador. Debe saber cuál es su velocidad de fotogramas objetivo y el tiempo de inactividad será lo que quede después de que se haya completado todo lo demás.

Digamos que estás apuntando a 60 FPS para tu juego. Eso te deja con 16.667 ms para realizar todo el trabajo que necesitas para hacer cada fotograma. Al comienzo del juego, obtén la hora actual usando el temporizador de mayor resolución disponible, agrégale 16.667 ms y guárdalo en algún lugar. Creo que la función en python es time () aunque ha pasado un tiempo desde que trabajé en el lenguaje. Una vez que se complete el procesamiento, ingrese un bucle que verifique la hora actual con la hora que ha grabado. Si la hora actual es menor que la hora de finalización de la trama, update_a_very_small_amount. No me preocuparía mucho el procesamiento que va más allá del final del marco, ya que su pequeña actualización debería procesarse rápidamente. Solo sería un ligero retraso hasta el inicio del siguiente cuadro y parece que tienes suficiente tiempo de inactividad para manejarlo.

Después de que el marco haya terminado de procesarse, agregue 16.667 ms al tiempo que se almacenó para el final del último marco para averiguar dónde debería estar el final del siguiente marco. Si utiliza el tiempo actual + 16.667 ms y el procesamiento continúa, el final del siguiente cuadro será expulsado por el tiempo que transcurra el último cuadro.

Re: Segunda edición

Para aclarar, utilizo el término frame-rate aquí para indicar una iteración a través del bucle principal. Si se basa en la velocidad de entrada del usuario, imagino que su objetivo es simplemente hacer que el juego se sienta receptivo. De lo contrario, podría verificar la entrada y actualizar todo cada vez a través del ciclo, incluso si tarda 10 segundos en hacerlo. Sin embargo, para que se sienta receptivo, es probable que desee verificar la entrada alrededor de 20 veces por segundo, lo que proporciona una velocidad de cuadros efectiva de 20 FPS, incluso si no está dibujando estos cuadros. Esto le daría 50 ms para actualizar las cosas antes de que necesite verificar la entrada nuevamente.

Joel Baker
fuente
2

Para Python específicamente, puedes probar y usar Coroutines para hacer cálculos en varias llamadas de actualización.

... las rutinas son componentes del programa que generalizan las subrutinas para permitir múltiples puntos de entrada para suspender y reanudar la ejecución en ciertos lugares ...

PEP-0342 detalla la implementación de rutina en Python.

Puede crear su propio planificador y tareas que pueden simular múltiples subprocesos en un solo hilo. Esta es la misma forma en que los marcos de Javascript permiten procesos multiproceso como el procesamiento aunque Javascript se ejecute en un solo proceso.

David Young
fuente
1

Si esto es posible. Tu juego tendrá algún tipo de bucle principal del juego. Algo como esto:

while(gameRunning)
{
  checkUserInput()
  updateGame()
  renderGame()
}

En updateGame puedes iterar sobre los objetos de tu juego y actualizarlos "un poco". Si está haciendo algún cálculo pesado en este método, el juego simplemente se bloqueará. Por lo tanto, necesita una forma de dividir estos cálculos para ejecutar varias iteraciones del bucle del juego.

Cómo dividirlos depende del tipo de juego que estés construyendo. Suponga que tiene una rutina de búsqueda de ruta que usa A * para calcular una ruta a través de un laberinto. Debería detener el algoritmo después de un cierto período de tiempo o después de una cantidad fija de iteraciones, mantener la ruta calculada hasta el momento y devolver el control al ciclo del juego. La búsqueda de ruta continuará la próxima vez que se llame a updateGame. Incluso puedes mover el personaje a lo largo del camino parcial mientras aún se está calculando.

La mayoría de las veces no necesita preocuparse por la actualización de la llamada de Game demasiado larga.

"¡La optimización temprana es la raíz de todo mal!"
Si llega a un cuello de botella de rendimiento en su rutina de actualización, ese es el momento de investigar y optimizar. Dado que no puede saber de antemano qué partes de su juego tomarán más tiempo para calcular y podrían perder tiempo optimizando las cosas incorrectas.

Stephen
fuente
1

La forma en que entiendo su pregunta es que básicamente está preguntando sobre la multitarea cooperativa.

La infraestructura básica sería una lista de punteros de funciones, todos llamados de forma circular (a menos que se requiera una priorización más sofisticada), y todas estas funciones harían "un poco de trabajo", lo suficientemente pequeño como para no causar que el juego volverse lento He hecho esto en DOS antes de que los subprocesos estuvieran calientes, y también en dispositivos móviles que no admitían subprocesos.

Una mejor pregunta, en mi opinión, es qué hacer mientras se espera que el jugador se mueva. ¿Qué tipo de cosas serían tan intensivas en el procesador que no se pueden realizar sobre la marcha de todos modos, mientras que, al mismo tiempo, son tan opcionales que si el jugador simplemente golpea alguna tecla de movimiento lo suficientemente rápido, el juego no tiene hora de procesar estos. Por lo tanto, cosas como calcular A * para un par de miles de IA están fuera.

La mejor solución, en mi opinión, es simplemente ceder / dormir, y dejar que la administración de energía de la computadora haga su trabajo. A los usuarios de computadoras portátiles les gustará más por eso.

Jari Komppa
fuente