Thursday, December 2, 2010

How to add screen to GSM

I bumped into a small but a really annoying problem when I was playing around with the GSM at first time. The problem was about adding a new screen.


Intro
I suppose everyone knows about GSM (Game State Management), if you don't - you are welcome. Also if you know how to add a splash screen, get out of here because you are smart enough.

Problem & Solution
I wanted to add a new screen which shows up when the game starts. The screen supposed to show a publisher name. The screen should be showing for a while and after that the main menu appears, or another screen showing the game name or whatever.
I had created a screen:

  • Created a new custom screen inherited from GameScreen class. Let's call it SplashScreen.
  • Initialized all needed entities in its LoadContent method.
  • Added a some code for drawing all my stuff on the SplashScreen.
Next step I wanted this screen appears before the main menu. I started to dig in the GSM. I found that the two first screens Background and MainMenu are added to a screen manager in the constructor of the my game class like that:

if (!screenManager.DeserializeState())
{
   // Activate the first screens.
   screenManager.AddScreen(new BackgroundScreen(), null);
   screenManager.AddScreen(new MainMenuScreen(), null);
}

The obviously solution is to add my SpashScreen in the same manner before these two.

if (!screenManager.DeserializeState())
{
   // Activate the first screens.
   screenManager.AddScreen(new SplashScreen(), null);       screenManager.AddScreen(new BackgroundScreen(), null);
   screenManager.AddScreen(new MainMenuScreen(), null);
}



The result was really strange.

The main fault is the screens adding order. The top screen should be added to the screen manager at the end! So the code snippet should looks like that:

if (!screenManager.DeserializeState())
{
   // Activate the first screens.
   screenManager.AddScreen(new BackgroundScreen(), null);
   screenManager.AddScreen(new MainMenuScreen(), null);
   screenManager.AddScreen(new SplashScreen(), null);
}


After tuning my SplashScreen class I've got what I wanted. Here is a sample splash screen code:


internal class SplashScreen : GameScreen
{
  ContentManager content;
  Texture2D logoTexture;
  private Vector2 logoPos;
  private TimeSpan elapsedTime;
  private TimeSpan displayTime = TimeSpan.FromSeconds(1);

  private bool instantOff;

  public SplashScreen()
  {
    TransitionOnTime = TimeSpan.FromSeconds(1.5);
    TransitionOffTime = TimeSpan.FromSeconds(1.5);
  }

  public override void LoadContent()
  {
    if (content == null)
      content = new ContentManager(ScreenManager.Game.Services, StringConstants.Content);

    logoTexture = content.Load<Texture2D>(StringConstants.Logo);

    Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
    logoPos = new Vector2((viewport.Width - logoTexture.Width) / 2, (viewport.Height - logoTexture.Height) / 2);
  }

  public override void UnloadContent()
  {
    content.Unload();
  }

  public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
  {
    if (!otherScreenHasFocus && !coveredByOtherScreen)
    {
      if (!IsExiting)
      {
        float transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / TransitionOnTime.TotalMilliseconds);

        // Update the transition position. 
        TransitionPosition = MathHelper.Clamp(TransitionPosition - transitionDelta, 0, 1);

        if (TransitionPosition == 0)
        {
          // Record how long the title has been displayed. 
          elapsedTime += gameTime.ElapsedGameTime;
        }

        if (elapsedTime > displayTime + TransitionOnTime)
        {
          ExitScreen();
        }
      }
      else
      {
        if (!instantOff)
        {

          float transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / TransitionOffTime.TotalMilliseconds);

          // Update the transition position. 
          TransitionPosition = MathHelper.Clamp(TransitionPosition + transitionDelta, 0, 1);
        }
        else
        {
          TransitionPosition = 1;
        }

        // Finished transitioning off 
        if (TransitionPosition == 1)
        {
          ScreenManager.RemoveScreen(this);
        }
      }
    }
  }

  public override void Draw(GameTime gameTime)
  {
    SpriteBatch spriteBatch = ScreenManager.SpriteBatch;


    ScreenManager.GraphicsDevice.Clear(Color.White);

    spriteBatch.Begin();
    spriteBatch.Draw(logoTexture, logoPos, Color.White);
    spriteBatch.End();

    if (TransitionPosition > 0)
      ScreenManager.FadeBackBufferToBlack(1f - TransitionAlpha);
  }

}  

No comments:

Post a Comment