Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Paradigmas De Preogramacion De Juegos

Iniciado por Pogacha, 06 de Julio de 2005, 03:05:53 PM

« anterior - próximo »

Pogacha

 Que tal ... estaba dando los ultimos toques al motor 2d y a fin de cuentas me encuentro en un enriedo de codigo.


Mi motor (2d) actual es algo así:



main()
{
 Motor m;

 while(Salir)
 {
    Motor->Actualizar(Get_Tiempo());
    Motor->Dibujar(Screen);
 }
}

void Motor::Actualizar(float tiempo)
{
 switch(Estado)
 {
    case presentacion: ... break;
    case jugando:
          switch(sub_estado)
          {
              // y asi ...
          }
 }
 if(Menu_activo)  Menu->Actualizar(tiempo);
}


void Motor::Dibujar(Superficie &Destino)
{
  swich(Estado)
 {
     case :
 }
 if(Menu_activo)  Menu->Dibujar(tiempo);
}


Pero se me complica para encadenar eventos como pueden ser que cuando pierda te muestre una pantalla pero luego siga haciendo lo de siempre.
O sea tengo que agregar estados ... y mas estados transitorios ...

Se me ocurrio que puedo tener algo así.

class Tarea
{
public:
  virtual void Comenzar(void);
  virtual bool Actualizar(void); // devuelve false cuando ya no se necesita.
  virtual void Dibujar(void);
  virtual ~Tarea(void);
  Tarea() { Error("Esta clase es puramente virtual!!!!"); }
};

class Cinematica : public Tarea
{
  char *Nombre_de_Cinematica
public:
  Cinematica(char *n);
  void Comenzar(void);
  bool Actualizar(void);
  void Dibujar(void);
  ~Cinematica(void);
};

class Motor
{
  Pila<Tarea> Tareas; // en la pila se pueden anexar e insertar tareas, así como purgarse
  Tarea *Actual;

   Actualizar(float tiempo)
  {
       if(!Actual)
       {
            Actual = Tareas.Pop();
            if(!Actual) Actual=Tarea.Push(new FondoAnimado( ... ));
            Actual->Comenzar();
       }

       if(!Actual->Actualizar(tiempo))  
       {
           delete Actual;
           Actual=NULL;
            Actualizar(tiempo);
           return;
       }

       if(Menu_activo)  Menu->Actualizar(tiempo);
  };

   Dibujar()
   {
       Actual->Dibujar()
       if(Menu_activo)  Menu->Dibujar();
   };

};


¿Alguien tiene experiencia con esto?, ¿es muy traumatico?, veo otros problemas nuevos pero no sé cuan complicado pueda ser pues no he probado y es cosa media loca, si tengo un sistema parecido con las cosas del mapa pero no aca a tal punto.

Saludos.

CoLSoN2

 Bueno, tanto para eventos como para estados, estaría bien que te montaras un sistema y no usarás un switch gigantesco.

Por ejemplo para los eventos tener un sistema parecido a esto:

---

Clase Event : el evento en sí. Tiene un ID que indica su tipo y poco más, ya que los miembros útiles se añadirán en subclases. Este ID puede actuar también como bit mask para filtrarlos y que los listeners (ver más abajo) sólo reciban la clase de eventos que les interese. Algunas subclases obvias son KeyEvent, MouseEvent, GameEvent, AppEvent (la aplicación gana o pierde el foco, etc.) y cosas así.

Clase EventSender : cualquiera que necesite enviar eventos, no tiene ningún miembro

Clase EventListener : cualquiera que necesite recibir eventos. Tiene un método virtual puro Notify que toma dos argumentos, un EventSender* y un Event*

Clase EventManager : registra EventListeners y envía Event'os a estos. Tiene una lista de listeners y métodos RegisterListener(EventListener*) y UnregisterListener(EventListener*), así como otro método SendEvent(EventSender*,Event*) que llama al Notify de cada listener registrado.

---

Es la arquitectura típica de un sistema de eventos. Lo de los estados, mírate el patrón State, que es más elegante que un switch gigante. Aunque yo en vez del patrón tal como está definido uso una pila para poder volver a estados anteriores y hacer algo así:

INICIO: Push (Estado_Main_Menu)
Usuario clica en 'Opciones' -> Push (Estado_Options)
Usuario cambia cosas y le da a 'Volver' -> Pop()
Usuario clica en 'Play' -> Push (Estado_InGame)
Se muestra un dialogo informativo -> Push (Estado_Dialogo)
Usuario clica 'Ok' -> Pop()
Usuario clica 'Salir' -> Pop()

Y estaríamos en MainMenu de nuevo. El patrón State tal como está definido me parece más adecuado para cosas como el estado de un bot (IA) controlado por una máquina de estados, pero aún así te puede servir.
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

CoLSoN2

 He releído tu post y no se si te refieres a cómo controlar estados en el tiempo. Es decir, que durante X tiempo pase esto y luego lo otro, o que dentro de Y tiempo ocurra tal cosa, etc.

En ese caso yo tengo montado un sistema de Triggers, muy sencillo pero útil, que es algo así:


class Trigger
{
   Trigger(int theDelay, int theDuration, int theKillCode = -9);
   
   void Start();
   void Tick(float theElapsedTime);
   void End();

   int mDelay;
   int mDuration;
   int mKillCode;
};


Y luego un TriggerManager muy sencillo que simplemente los gestiona (los crea cuando transcurre su Delay, los va actualizando luego (Tick) y finalmente los termina). También puedes terminar con ellos prematuramente enviándole al manager un kill code determinado, que borrará los triggers que tengan ese código, etc. Es muy sencillo pero yo lo uso para gestionar cualquier cosa que necesita de control de tiempo: mover algo de X a Y linearmente o en curva, envíar un evento tras X segundos, mostrar los típicos mensajitos de "592 points!" que salen flotando y luego desaparecen, este tipo de cosas.
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

Lord Trancos 2

 Yo uso una clase virtual a modo de interfaz y despues creo las clases derivadas que corresponden a los estados.

Solo creo 1 objeto por clase, y lo creo al inicio del programa.

- Uno para el modulo "menu principal".
- Otro para el modulo "explorador"
- Otro para el modulo "combate"
- Otro para el modulo "tienda/inventario"
etc....

Despues internamente, cada estado tiene un stack con subestados que no es mas que un array de enteros.

Y dentro de cada funcion basica (que son 3: dibujar, animar, procesarInput) tengo un "switch" para el subestado.

Note mucha mejoria cuando cambie a este sistema. Basicamente pq te permite dividir el programa en partes muy bien diferenciadas, lo cual las simplifica bastante.

pd. estaria bien hablar mas a menudo de estos temas... creo q son  cosas a las que no se les da la suficiente importancia (es mas divertido hablar de como hacer tal o cual efecto grafico) y luego tienen un gran impacto en el desarrollo del juego.
on los años y mucho esfuerzo he llegado a atesorar una ignorancia total sobre casi todas las cosas.
Gate to Avalon (mi Blog)

NeLo

 Yo hago igual que tú Lord Trancos, pero sin subsistemas.

Saludos.
Drowning deep in my sea of loathing

senior wapo

 Yo lo veo mas como subsistemas en paralelo, que se ejecutan a la vez y/o duermen.

Tiene el sistema de menu, el sistema de HUD durante partida, el sistema de exploracion 3D ( la partida en si o una escena de fondo), el sistema de carga de datos...

Cuando haces una transicion de un estado a otro de juego simplemente cambias los datos que maneja cada sistema y si estan durmiendo o no. Notese que el juego no cambia de estado al azar, sino porque un subsistema a detectado una condicion y actualiza a los otros.

Tu bucle de programa es siempre el mismo en todo momento:


 menuMgr.reset();
 hudMgr.reset();
 inputMgr.reset();
 sceneMgr.reset();
 dataLoader.reset();
 timeService.reset();
 
 while (!exiting) {
    frameTime = timeService.doTick();
    inputMgr.step(frameTime);
    menuMgr.step(frameTime);
    hudMgr.step(frameTime);
    sceneMgr.step(frameTime);
    dataLoader.step(frameTime);
 }  


Observese que en cada paso los subsistemas haran algo o no dependiendo de SU estado actual y sus estructuras, no del estado del juego.

Por ejemplo, durante la carga inicial de juego, solo hud y dataLoader harán algo (cargar medio segundo de datos uno, el otro mostrar imagen pantalla completa y una barra de progreso). Al terminar de cargar los datos, dataLoader reseteara el sistema de hud y pasara nuevos datos al de menu y el de escena (menu de juego y poner escena de fondo una partida grabada o escenario estatico).

El sistema de menu por ejemplo, podria auto ocultarse cuando pulsas en "Jugar", ordenar al de datos que cargue el nivel 1, resetear al de escenario, etc...

Todas estas ordenes no son mas que eventos, que se pueden implementar como tales o simplemente llamando a pelo a los otros subsistemas.

gdl

 
CitarTarea() { Error("Esta clase es puramente virtual!!!!"); }

Mejor poner
protected:
Tarea() {}


NeLo

 Me gusta el sistema de senior wapo combinando cada subsistema con sus propios estados.

Quizás para lo próxima me lo plantee.
Drowning deep in my sea of loathing

Pogacha

 Miré, estudié, miré, probé, pero no sé, lo dejaré para otro motor.

La idea del senior wapo no la habia pensado, pero me referia mas a lo que dice Lord Trancos 2.

En mi proximo motor veré como implementar algo así.

Saludos.

tiutiu

 Yo prefiero mas un sistema de estados orientado a objetos.
Una clase base GameState de la que derivas los estados posibles del juego (PlayState, GameOverState, MenuState, ...). El engine tiene un apuntador al estado actual del juego, y en cada frame llama a la funcion de frame. Una version mejorada puede tener una pila de eventos.

El sistema del senior wapo no me acaba de convencer porque queda un poco confuso que es el estado del juego o de la aplicacion. En cambio una abstraccion del estado en un objeto a parte con su interface da una idea mas concreta.
Este metodo no esta reñido con el uso de subsistemas. El estado del juego es el que dice a cada subsistema que debe hacer. Siguiendo el ejemplo en el post de wapo durante la carga del juego, tenemos el estado LoadingState encima de la pila, y lo que hace es llamar a la funcion frame del GUI y del manager de recursos o de niveles.
Evidentemente cada estado tiene funciones de inicializacion, destruccion, pausa y resumen que se encargarian de 'cambiar' el estado de los subsistemas que requerimos.

Hace un tiempo vi un tutorial sobre estados de juego que da una buena idea sobre esto mismo.


PD: En los tiempos que corren, con CPUs potentes y equipos de desarrollo cada vez mas grandes y/o distribuidos, la OO y el diseño que de un concepto claro y conciso de que se esta haciendo prima sobre 'lo facil y rapido'.
b>:: Pandora's Box project ::
Notas e ideas sobre desarrollo de engines para juegos

Lord Trancos 2

Cita de: "tiutiu"Yo prefiero mas un sistema de estados orientado a objetos.
Una clase base GameState de la que derivas los estados posibles del juego (PlayState, GameOverState, MenuState, ...). El engine tiene un apuntador al estado actual del juego, y en cada frame llama a la funcion de frame. Una version mejorada puede tener una pila de eventos.
Ese es exactamente el sistema que yo uso (la version mejorada).  (ole)

Pogacha, tal vez no sea buena idea cambiar ahora el juego que estas haciendo, pero te recomiendo (mucho) que uses un sistema similar para el proximo proyecto.  ;)  
on los años y mucho esfuerzo he llegado a atesorar una ignorancia total sobre casi todas las cosas.
Gate to Avalon (mi Blog)

Pogacha

 
CitarUna clase base GameState de la que derivas los estados posibles del juego (PlayState, GameOverState, MenuState, ...).
Exactamente a esto me referia, en conceptos abstractamente correctos, pero yo no lo vi en ningun lado, salio por combustion espontanea!, o sea ... estaba escribiendo y se me ocurrio algo así y por eso es que preguntaba si era posible, de ahora en mas usaré ese modo.

CitarPogacha, tal vez no sea buena idea cambiar ahora el juego que estas haciendo, pero te recomiendo (mucho) que uses un sistema similar para el proximo proyecto. 
El resto de las cosas excepto la IA utiliza este sistema ... pero no se me ocurrio para el "desarrollo del juego", ahora tendre una clase UpdatableObject de donde desenderan todos los elementos del juego  :P

CitarEse es exactamente el sistema que yo uso (la version mejorada). 
Si ya la habia captado, pero tiutiu lo dijo con elegante teoria  :P

Gracias a todos.

PD: Leí en mi ultimo intento de entender lo de CoLSoN2 y recien caigo ...
Si eso estaria bien para un juego tipo Puzzle ... tendria que pensar un rato si tendria una ganancia en un juego arcade ...

senior wapo

 La verdadera ventaja de tener estados propios en sistemas independientes es la flexibilidad. Por ejemplo, el menu puede estar disponible en cualquier momento y estado de juego, incluso durante la pantalla de carga. Los sistemas se pueden enviar mensajes entre si y ocultar al programador el trabajo de actualizacion. Sin codificarlo expresamente.

La complejidad de programación es lineal al numero de subsistemas. estados_sist1 + estados_sist2 .....

Con un sistema de estado único, tienes que codificar expresamente en cada estado que capacidades hay disponibles en ese estado para todos los subsistemas.

La complejidad es geometrica(?) al numero de subsistemas * estados. Que si, que normalmente tendras pocos subsistemas, pero hay está el potencial.


Pero bueno, para gustos, colores.

zupervaca

 una vez estuve en una empresa para una entrevista de trabajo y cuando me empezaron a enseñar la empresa y parte de codigo que tenian montado me quede perplejo al ver que la IA la tenian con switch´s  :huh:, y pense ... ¿y salen juegos de aqui?  :lol:

saludos

gdl

 
Cita de: "zupervaca"una vez estuve en una empresa para una entrevista de trabajo y cuando me empezaron a enseñar la empresa y parte de codigo que tenian montado me quede perplejo al ver que la IA la tenian con switch´s  :huh:, y pense ... ¿y salen juegos de aqui?  :lol:
Supongo que dependerá de lo que se quiera hacer. En algunos casos un switch es más que suficiente, en otros entra en la categoría de código spaghetti. De hecho, muchas veces me ha ocurrido que he querido hacer una arquitectura orientada a objetos completa, robusta, flexible y extensible para sólo encontrarme al final con tropecientas clases que prácticamente no tienen código. Encima, para hacer algo hay que llamar a una clase que es la fachada de otra que hace de puente con otra que ha de buscar el padre de otra para llamar al método que finalmente es delegado de lo que quieres hacer. :blink:

Por otra parte como decía senior wapo...

CitarLa complejidad es geometrica(?) al numero de subsistemas * estados. Que si, que normalmente tendras pocos subsistemas, pero hay está el potencial.

Me ha gustado la idea. Es el producto cartesiano de estados. :D

Conjunto de estados totales = Conjunto_Estado_Subsistema1 x Conjunto_Estado_Subsistema2 x ... x Conjunto_Estado_SubsistemaN








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.