Skip to content
octubre 7, 2010 / 100xna

[Intermedio] Gestión de Estados


Cuando desarrollamos un juego con todas sus partes (menús, opciones, juegos, pantalla de carga, etc.…) una de las cosas que facilitan mucho el trabajo es tener un sistema de gestión de estados simple , que no complique mucho la lógica del juego, pero a la vez potente, que no limite en gran medida lo que puedes hacer en cada estado.

Un punto de partida puede ser el ejemplo que hay en el Creator’s Club, pero a mi personalmente me parece muy lioso ese ejemplo, sobre todo para gente que todavía no ha hecho ningún sistema parecido con anterioridad.

Así pues, después del salto os voy a explicar otro sistema, para mi gusto mucho más fácil de entender y bastante potente.

Empecemos con lo básico: el diagrama de nuestro sistema de gestión de estados es el siguienteMain

Como podéis observar, tenemos un StateManager, que será el encargado de gestionar los estados que hay en el juego, así como actualizar y pintar aquel estado que este actualmente activado. Contiene una lista de estados y una variable que indica el estado actual en el que se encuentra el juego. Esta variable hemos hecho que sea un enumerado. Cada vez que añadamos un nuevo estado, deberemos añadir también el estado al enumerado. Se podría haber hecho de otro forma, pero trabajar con enumerados es más seguro que trabajar, por ejemplo, con strings.

State es una clase abstracta de la cual heredarán todos los estados que definamos en el juego. Esta clase obliga a que todos los estados implementen los metodos “Update” y “Draw”.

MainMenu, Options, Loading y Playing son estados concretos. Cada uno de estos estados contendrá la lógica que se debe ejecutar cuando estén activos, así como sus propios métodos de pintado. En este ejemplo he puesto estos estados a modo de ejemplo, pero luego cada juego puede tener sus estados, no necesariamente estos que yo he puesto a modo de ejemplo.

Hechas las explicaciones de las clases que vamos a implementar, vamos a ver como las implementamos:

StateManager

Para el StateManager vamos ha hacerlo como GameComponent. Esto es, derivará de una clase llavada “DrawableGameComponent”.

Estos son los atributos que tiene esta clase. Como podéis ver, he cambiado la lista de estados por un diccionario, que es más eficiente para para el uso que le vamos a dar en este caso:

  1         public Dictionary<GameStates, State> estados;
  2         public GameStates estadoActual;

En cuanto a los métodos Update y Draw, simplemente lo que hará será buscar el estado actual en el diccionario. Si ese estado existe, lo actualizará/pintará. Si no existe, no hará nada (lo ignorará):

  1         public override void Update(GameTime gameTime)
  2         {
  3             State estado;
  4             if(estados.TryGetValue(estadoActual, out estado))
  5                 estado.Update(gameTime);
  6         }
  7
  8         
  9         public override void Draw(GameTime gameTime)
10         {
11             State estado;
12             if(estados.TryGetValue(estadoActual, out estado))
13                 estado.Draw(gameTime);
14         }

 

 

State

Esta es la clase base de todos los estados. Contiene una serie de atributos que todos las clases derivadas también tendrán y obliga a que las clases hijas implementen dos métodos, Update y Draw. No olvidar definir esta clase como “abstract”.

Los atributos que va a tener son:

  1   StateManager manager;           //Manager de estados
  2   ContentManager content;         //ContentManager para los recursos del juego
  3   GraphicsDeviceManager graphics; //Graphics
  4   Game game;                      //Juego

 

Una vez definidos estos atributos, si queremos podemos hacer Propiedades para poder acceder a ellos de una forma más sencilla:

  1         public Game Game
  2         {
  3             get { return game; }
  4         }
  5
  6         public StateManager Manager
  7         {
  8             get { return manager; }
  9         }
10
11         public ContentManager Content
12         {
13             get { return content; }
14         }
15
16         public GraphicsDevice GraphicsDevice
17         {
18             get { return graphics.GraphicsDevice; }
19         }
20
21         public GraphicsDeviceManager GraphicsManager
22         {
23             get { return graphics; }
24         }

 

Estas variables las inicializamos en el constructor:

  1  public State(Game game)
  2  {
  3      this.game = game;
  4      manager = game.Services.GetService(typeof(StateManager)) as StateManager;
  5      content = game.Services.GetService(typeof(ContentManager)) as ContentManager;
  6      graphics = game.Services.GetService(typeof(IGraphicsDeviceService)) as GraphicsDeviceManager;
  7  }
 

Y por ultimo la definición de los métodos abstractos:

  1    public abstract void Update(GameTime gameTime);
  2
  3    public abstract void Draw(GameTime gameTime);

 
 

Estados Concretos

Aquí tengo poco que decir, ya que lo programareis según lo que tengáis pensado que haga. Sin embargo, a modo de plantilla, os recomiendo que empecéis por algo así:

  1     public class PlayingState : State
  2     {
  3         private SpriteBatch spriteBatch;        //Spritebatch principal
  4         private GraphicsDeviceManager graphics; //Graphics
  5
  6
  7         public PlayingState(Game game)
  8             : base(game)
  9         {
10             spriteBatch = new SpriteBatch(GraphicsDevice);
11             graphics = GraphicsManager;
12         }
13
14         public void Initialize()
15         {
16
17         }
18
19         public override void Update(GameTime gameTime)
20         {
21             //Logica del estado
22         }
23
24         public override void Draw(GameTime gameTime)
25         {
26             //Pintar el estado
27         }
28
29
30     }

 

Con esto ya tendréis el esqueleto de un sistema de gestión de estados. Ahora toca integrarlo en nuestro juego! Para ello vamos a la clase Game1.cs y modificamos lo siguiente:

Game1.cs

Lo primero que hacemos es crear un atributo de tipo StateManager:

1 StateManager stateManager;

El cual tendremos que inicializar en el constructor:

  1         public Game1()
  2         {
  3             graphics = new GraphicsDeviceManager(this);
  4             Content.RootDirectory = "Content";
  5
  6             //Creamos y añadido el ManagerState
  7             stateManager = new StateManager(this);
  8             Components.Add(stateManager);
  9             
10             //Añadimos los servicios para poder usarlos en otras clases
11             Services.AddService(typeof(ContentManager), Content);
12             Services.AddService(typeof(StateManager), stateManager);
13         }

Como podéis ver, también hemos añadido el StateManager y el Content como servicios del juego. De momento, dejarlo así, y en futuras entradas del blog explicaré que es eso de los Services en XNA (usar el buscador para encontrarlo)

Lo ultimo que nos queda, es añadir los estados del juego e indicar cual va a ser el estado inicial el juego:

  1         protected override void Initialize()
  2         {
  3             // TODO: Add your initialization logic here
  4
  5             base.Initialize();
  6
  7
  8             //Añadimos los estados que tendrá nuestro juego
  9             stateManager.Estados.Add(
10                 GameStates.PlayingState,
11                 new PlayingState(this));
12
13             //Los otros estados se añadirian igual
14
15
16             //Estado inicial del juego
17             stateManager.EstadoActual = GameStates.PlayingState;
18         }

Y ahora si que si, tenemos nuestro gestor de estados para poder usarlo en nuestro juego. Dejo el código fuente del proyecto con comentarios para que le podáis pegar un vistazo y hacer pruebas sobre él.

Ahora os toca a vosotros programar vuestros estados dentro de un juego!

 Download

Descargar Código Fuente

Anuncios

3 comentarios

  1. hashassin / Oct 11 2010 2:44 pm

    Hola,me encanta esta pagina,es muy buena.
    Sigue asi,me esta sirviendo mucho para aprender c# y XNA.

  2. carlos / Ene 17 2012 3:25 pm

    A mi rss de una!!!! xD desde Venezuela Gracias!

  3. carlos / Feb 8 2012 8:42 pm

    Gracia amigo, excelente tu publicación estoy iniciando con xna, y me es de mucha ayuda, saludos desde Panamá.

Los comentarios están cerrados.

A %d blogueros les gusta esto: