2009-08-08 13 views
16

He oído que las corutinas son una buena forma de estructurar juegos (por ejemplo, PEP 342: "Coroutines es una forma natural de expresar muchos algoritmos, como simulaciones, juegos ...") pero estoy teniendo dificultades para envolver mi cabeza alrededor de cómo esto realmente se haría.Coroutines para el diseño de juegos?

Veo en este article que las corutinas pueden representar estados en una máquina de estado que se transicionan usando un programador, pero no me queda claro cómo esto se aplica a un juego donde el estado del juego está cambiando en base a movimientos de múltiples jugadores.

¿Hay algún ejemplo simple de un juego escrito usando corutinas disponibles? ¿O alguien puede ofrecer un boceto de cómo se podría hacer?

Respuesta

7

Una forma de coroutines se puede utilizar en los juegos es como los hilos ligeros en un actor como modelo, como en Kamaelia.

Cada objeto en su juego sería un 'componente' de Kamaelia. Un componente es un objeto que puede pausar la ejecución cediendo cuando está permitido pausar. Estos componentes también tienen un sistema de mensajería que les permite comunicarse de forma segura entre sí de forma asincrónica.

Todos los objetos estarían haciendo sus cosas al mismo tiempo, con mensajes enviados entre ellos cuando ocurren las interacciones.

Por lo tanto, no es realmente específico para juegos, pero cualquier cosa que tenga una gran cantidad de componentes de comunicación que actúen de forma simultánea podría beneficiarse de corutinas.

9

corrutinas permiten la creación de grandes cantidades de muy ligero "microhilos "con multitarea cooperativa (es decir, microthreads que se suspenden deliberadamente para permitir que otros microthreads se ejecuten). Lea en Dave Beazley's article sobre este tema.

Ahora, es obvio cómo estos microthreads pueden ser útiles para la programación de juegos. Considera un juego de estrategia en tiempo real, donde tienes docenas de unidades, cada una con su propia mente. Puede ser una abstracción conveniente para que la IA de cada unidad se ejecute como un microthread en su entorno de multitarea simulado. Este es solo un ejemplo, estoy seguro de que hay más.

La búsqueda de "programación de juegos coroutine" en Google parece traer resultados interesantes.

8

El caso más destacado de corutinas es probally old graph point & haga clic en juegos de aventuras, donde se utilizaron para secuencias de secuencias de comandos y otras secuencias animadas en el juego. Un ejemplo simple código se vería así:

# script_file.scr 
bob.walkto(jane) 
bob.lookat(jane) 
bob.say("How are you?") 
wait(2) 
jane.say("Fine") 
... 

Toda esta secuencia no puede ser escrito como código normal, como usted quiere ver a Bob hacer su animación paseo después de que hizo bob.walkto(jane) en lugar de saltar a la derecha a la línea siguiente . Sin embargo, para que la animación de la caminata juegue, debes devolver el control al motor del juego y ahí es donde entran en juego las corutinas.

Toda esta secuencia se ejecuta como una corutina, lo que significa que tiene la capacidad de suspenderla y reanudarla como desee.Un comando como bob.walkto(jane) le dice al objeto bob del lado del motor su objetivo y luego suspende la corrutina, esperando una llamada de activación cuando bob ha alcanzado su objetivo.

En las cosas laterales del motor podría tener este aspecto (pseudo código):

class Script: 
    def __init__(self, filename): 
     self.coroutine = Coroutine(filename) 
     self.is_wokenup = True 

    def wakeup(self): 
     self.is_wokenup = False; 

    def update(): 
     if self.is_wokenup: 
      coroutine.run()    


class Character: 
    def walkto(self, target, script): 
     self.script = script 
     self.target = target 

    def update(self): 
     if target: 
      move_one_step_closer_to(self.target) 
      if close_enough_to(self.target): 
       self.script.wakeup() 

       self.target = None 
       self.script = None 

objects = [Character("bob"), Character("jane")] 
scripts = [Script("script_file.scr")] 

while True: 
    for obj in objects: 
     obj.update() 

    for scr in scripts: 
     scr.update() 

Palabras litte de advertencia sin embargo, mientras corrutinas hacen codificación de estas secuencias muy simple, no todas las implementaciones se encuentra de ellos Se debe tener en cuenta la serialización, por lo que guardar el juego se convertirá en un problema bastante problemático si haces un uso intensivo de corrutinas.

Este ejemplo es también el caso más básico de una corutina en un juego, las corutinas en sí mismas se pueden utilizar para muchas otras tareas y algoritmos también.

1

Es bastante común querer utilizar corutinas para representar scripts individuales de AI de actor. Desafortunadamente, las personas tienden a olvidar que las corrutinas tienen todos los mismos problemas de sincronización y exclusión mutua que tienen los hilos, solo en un nivel superior. Por lo tanto, a menudo debe hacer todo lo posible para eliminar el estado local y, en segundo lugar, escribir formas robustas de manejar los errores en una corutina que surgen cuando algo a lo que se refería deja de existir.

De modo que en la práctica es bastante difícil obtener un beneficio de ellos, por lo que los lenguajes como UnrealScript que utilizan algunas semejanzas de coroutines empujan mucha de la lógica útil a eventos atómicos. Algunas personas obtienen una buena utilidad de ellos, por ejemplo. las personas de EVE Online, pero tuvieron que diseñar prácticamente todo su sistema en torno al concepto. (Y cómo manejan la contención sobre los recursos compartidos no está bien documentado).

+0

Las corrutinas tienen algunos problemas de sincronización, pero solo si su estado no puede ser coherente entre las llamadas a yield(). Tener un bloqueo implícito alrededor de todo el código entre llamadas a yield() puede simplificar enormemente las cosas. El mayor problema es que cada vez que se crea una rutina, se debe decidir en ese momento si la rutina siempre podrá completarse antes de tener que ceder(). Si la persona que llama a una rutina no espera que se rinda una rutina, es posible que no tenga elementos en un estado constante cuando se llame a la rutina. Agregar un rendimiento() sería un cambio radical. – supercat

+0

El problema es que, por lo general, desea poder ceder en cualquier lugar para que tales rutinas tengan algún uso (como en UnrealScript donde las funciones latentes rinden implícitamente), pero esto lleva a combinaciones de corutinas que interactúan de maneras impredecibles. p.ej. Si se trata de una patrulla corrutina que dice "vete al norte, mira, vete al sur, mira, repite"; y la otra es una rutina de reacción que dice "si (el sonido se escucha hacia el oeste) {ir al oeste; mirar; ir hacia el este;}" luego intercalando los dos podría resultar en que el actor se mueva de manera impredecible. Es por eso que UnrealScript solo permite tales funciones en precisamente una 'corutina'. – Kylotan

+0

E incluso si solo tiene 1 corrutina por actor, eso aún no es suficiente para garantizar que la lógica siempre funcione en el nivel de idioma: si su corotine dice: "dibuje espada, espere 1 segundo; ataque al oponente con espada", entonces qué sucede si la espada desaparece debido a que eres desarmado por otro jugador? En particular, ¿qué pasa si tu lenguaje implementa esa corotine usando una variable local para una espada que ahora se refiere a un objeto nulo? Es muy complicado considerar todos estos casos si usa un lenguaje de propósito general de la forma habitual. – Kylotan

Cuestiones relacionadas