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.