Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using SceneLoader Class to Create Loading Screen in XNA, WPXNA (10)

0.00/5 (No votes)
30 Jun 2013 1  
Using SceneLoader class to create loading screen in XNA

The original post can be found here.

Introduction/Catalog

I have developed some games on Windows Phone. Here, I'll share my experiences and gradually upload some classes, no good name, I just call it WPXNA. (Some example code may not be stringent enough.)

  • SceneLoader
  • LoadingScene 
  • Modify the World class
  • Example

SceneLoader

Class SceneLoader is used to load the resources necessary for the scene, and to inform the outside.

Event Loaded is used to tell the outside world all the scenes were already loaded. Field scenes are the scenes that need to load resources, field afterSceneTypes is used to determine the order of scenes.

internal sealed class SceneLoader
 : IDisposable
{
 internal event EventHandler<SceneLoaderEventArgs> Loaded;

 private readonly List<Scene> scenes = new List<Scene> ( );
 private readonly Type[] afterSceneTypes;

 internal SceneLoader ( World world, Scene[] scenes, Type[] afterSceneTypes )
 {
  if ( null == world )
   throw new ArgumentNullException ( "world", "world can't be null" );

  if ( null != scenes )
   foreach ( Scene scene in scenes )
    if ( null != scene )
    {
     scene.World = world;
     scene.IsClosed = false;

     this.scenes.Add ( scene );
    }

  this.afterSceneTypes = afterSceneTypes;
 }

 internal void LoadResource ( )
 { this.loadContent ( ); }

 private void loadContent ( )
 {
  foreach ( Scene scene in this.scenes )
   scene.LoadContent ( );

  if ( null != this.Loaded )
  {
   SceneLoaderEventArgs loadedArg = new SceneLoaderEventArgs ( this.scenes, this.afterSceneTypes );

   this.Loaded ( this, loadedArg );
   loadedArg.Dispose ( );
  }
 }

 public void Dispose ( )
 { this.scenes.Clear ( ); }

}

You can remove the LoadResource method, modify the loadContent method as internal so that you can directly call it to load the resources. In loadContent, we will call the LoadContent method of all scenes, and then trigger the Loaded event, so outsiders can get all scenes.

LoadingScene

I define LoadingScene class as a base class, the LoadingScene uses the SceneLoader class to load resources.

Here is fields, events, and constructor of LoadingScene. LoadingScene uses Loaded to inform the outside world that the task has been completed. Field loader is used to load resources, field isLoading indicates whether the resources are loaded, the field loadingLabel is used to display relevant information.

internal sealed class LoadingScene
 : Scene
{
 internal event EventHandler<SceneLoaderEventArgs> Loaded;

 private readonly SceneLoader loader;
 private bool isLoading = false;
 private readonly Label loadingLabel;

 internal LoadingScene ( SceneLoader loader )
  : this ( loader,
  null
  )
 { }
 internal LoadingScene ( SceneLoader loader, Label loadingLabel )
  : base ( Vector2.Zero, GestureType.None,
  new Resource[] {
   new Resource ( "peg", ResourceType.Font, @"font\peg" )
  },
  new Making[] {
   loadingLabel
  }
  )
 {

  if ( null == loader )
   throw new ArgumentNullException ( "loader", "loader can't be null" );

  this.loader = loader;
  this.loadingLabel = loadingLabel;
 }
 
 // ...
}

We do not load resources in the LoadContent method, but to set the position of loadingLabel, and the label is drawn in the drawing method.

public override void LoadContent ( )
{
 base.LoadContent ( );

 if ( null != this.loadingLabel )
 {
  Label.InitSize ( this.loadingLabel );

  if ( this.loadingLabel.Rotation == 0 )
   this.loadingLabel.Location = new Vector2 ( 50, 50 );
  else
   this.loadingLabel.Location = new Vector2 ( 50, 430 );
 }
}

protected override void drawing ( GameTime time, SpriteBatch batch )
{
 if ( null != this.loadingLabel )
 {
  this.world.GraphicsDevice.Clear ( this.world.BackgroundColor );

  Label.Draw ( this.loadingLabel, batch );
 }
}

The updating method, the LoadResource method of SceneLoader is called to load resources and we set up Loaded event of SceneLoader before. In order to prevent the code from being called again, we set the isLoading field to true.

The loaded method will be called after resources loaded, we will close the scene and its Loaded event is triggered.

private void loaded ( object sender, SceneLoaderEventArgs e )
{
 this.Close ( );

 if ( null != this.Loaded )
  this.Loaded ( this, e );
}

protected override void updating ( GameTime time )
{
 if ( this.isLoading )
  return;

 this.isLoading = true;

 if ( null != this.Loaded )
  this.loader.Loaded += this.loaded;

 this.loader.LoadResource ( );
}

public override void Dispose ( )
{
 this.loader.Loaded -= this.loaded;
 this.loader.Dispose ( );

 base.Dispose ( );
}

Modify the World Class

We also offer World class to add some fields and methods, so you can use the SceneLoader and LoadingScene above.

Field currentSceneLoader is used to load the resources needed, field isVertical is used to determine the orientation of label in LoadingScene.

Method sceneContentLoaded is used to handle the Loaded event of LoadingScene, in the sceneContentLoaded method, we get all scenes, and add them to the World by the appendScene method.

private SceneLoader currentSceneLoader = null;

private readonly bool isVertical;

private void sceneContentLoaded ( object sender, SceneLoaderEventArgs e )
{
 ( sender as LoadingScene ).Loaded -= this.sceneContentLoaded;

 for ( int index = 0; index < e.Scenes.Count; index++ )
 {
  Type afterSceneType;

  if ( null != e.AfterSceneTypes && index < e.AfterSceneTypes.Length )
   afterSceneType = e.AfterSceneTypes[ index ];
  else
   afterSceneType = null;

  this.appendScene ( e.Scenes[ index ], afterSceneType, true );
 }

 if ( null != this.currentSceneLoader )
  this.currentSceneLoader.Dispose ( );

 this.currentSceneLoader = null;
}

private void appendScene ( Scene[] scenes )
{ this.appendScene ( scenes, null, true ); }
private void appendScene ( Scene[] scenes, bool isVisible )
{ this.appendScene ( scenes, null, isVisible ); }
private void appendScene ( Scene[] scenes, Type[] afterSceneTypes, bool isVisible )
{ this.appendScene ( new SceneLoader ( this, scenes, afterSceneTypes ), isVisible ); }
private void appendScene ( SceneLoader loader )
{ this.appendScene ( loader, true ); }
private void appendScene ( SceneLoader loader, bool isVisible )
{

 if ( null == loader || null != this.currentSceneLoader )
  return;

 this.currentSceneLoader = loader;

 LoadingScene loadingScene = new LoadingScene 
 ( loader, isVisible ? new Label ( "peg.f", 
 "Loading...", 1.5f, this.isVertical ? -90 : 0 ) : null );
loadingScene.Loaded += this.sceneContentLoaded;
this.appendScene ( loadingScene, null, false );
}

We also defined a number of appendScene methods. These methods are different from the first appendScene method we defined before. These methods do not directly add scenes, but rather by creating SceneLoader and LoadingScene.

Example

SceneT11 is a simple class that contains a font resource and a sound resource, and defines a label. SceneT11 will print the label and play music.

internal sealed class SceneT11
 : Scene
{
 private readonly Label l1;

 internal SceneT11 ( )
  : base ( Vector2.Zero, GestureType.None,
  new Resource[] {
   new Resource ( "peg", ResourceType.Font, @"font\myfont" ),
   new Resource ( "scene.sound", ResourceType.Music, @"sound\music1" ),
  },
  new Making[] {
   new Label ( "l1", "I'm SceneT11!!!!!!", 2f, Color.White, 0f )
  }
  )
 {
  this.l1 = this.makings[ "l1" ] as Label;
 }

 protected override void drawing ( GameTime time, SpriteBatch batch )
 {
  base.drawing ( time, batch );

  Label.Draw ( this.l1, batch );
 }
}

In the OnNavigatedTo method in the World, we will call the appendScene method to add a new SceneT11.

protected override void OnNavigatedTo ( NavigationEventArgs e )
{
 // ...

 this.appendScene ( new Scene[] { new mygame.test.SceneT11 ( ) } );

 base.OnNavigatedTo ( e );
}

If we do not use SceneLoader and LoadingScene, then playing background music in a scene might throw an exception.

Get the code from here, for more contents, please visit WPXNA.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here