通过简单的 ResourceManager 管理 XNA 中的资源,WPXNA(二)

平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛。在这里分享一下经验,仅为了和各位朋友交流经验。平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXNA 吧,最后请高手绕道而行吧,以免浪费时间。(为了突出重点和减少篇幅,有些示例代码可能不够严谨。)

资源的类型

在一些简单的 2D 游戏设计中,我们通常用到的资源是一些字体,图像和声音。所以这里就不涉及视频和模型这些内容了,要制作视频和 3D 模型往往需要花费很多时间。

这里,平方通过枚举类型 ResourceType 来区分不同的资源:

internal enum ResourceType
{
    Image,
    Font,
    Sound,
    Music,
}

在上面的代码中,Image 表示图像资源,Font 表示字体资源。Sound 和 Music 都表示声音资源,不同的是 Music 通过 MediaPlayer 类播放(一般是 mp3 文件),而 Sound 一般指一些小的 wav 文件。

标识资源

平方创建了一个 Resource 结构来标识一个资源,他有三个公开的字段,Name 是资源的名称,这个名称不能和其他资源重复。Type 是资源的类型,也就是上面提到的 ResourceType。Path 是资源的访问路径,也就是资源文件在项目中的位置,如下图所示:

internal struct Resource
{

    internal readonly string Name;
    internal readonly string Path;
    internal readonly ResourceType Type;

    internal Resource ( string name, ResourceType type, string path )
    {

        if ( string.IsNullOrEmpty ( name ) || string.IsNullOrEmpty ( path ) )
            throw new ArgumentNullException ( "name, path", "name, path can't be null" );

        this.Name = name;
        this.Type = type;
        this.Path = path;
    }

}

因为,只有正确的载入资源,游戏才能正常运行,所以在 Resource 类的构造函数中,平方对参数作了一些检查。

管理资源

然后,我们可以通过 ResourceManager 来管理资源。

在构造函数中,我们接受一个 Resource 结构数组,他们也就是需要管理的资源。

internal readonly IList<Resource> Resources;

internal ResourceManager ( IList<Resource> resources )
{ this.Resources = null == resources ? new Resource[] { } : resources; }

当然,ResourceManager 本身并不能够载入资源,所以我们需要使用 ContentManager 类:

private ContentManager contentManager;
internal World World;

private readonly Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D> ( );
private readonly Dictionary<string, SpriteFont> fonts = new Dictionary<string, SpriteFont> ( );
private readonly Dictionary<string, SoundEffectInstance> sounds = new Dictionary<string, SoundEffectInstance> ( );
private readonly Dictionary<string, Song> music = new Dictionary<string, Song> ( );

public void LoadContent ( )
{

    if ( null == this.contentManager )
        this.contentManager = new ContentManager ( this.World.Services, contentDirectory );

    try
    {

        foreach ( Resource resource in this.Resources )
            switch ( resource.Type )
            {
                case ResourceType.Image:
                    this.textures.Add ( resource.Name, this.contentManager.Load<Texture2D> ( resolution + resource.Path ) );
                    break;

                case ResourceType.Font:
                    this.fonts.Add ( resource.Name, this.contentManager.Load<SpriteFont> ( resource.Path ) );
                    break;

                case ResourceType.Sound:
                    this.sounds.Add ( resource.Name, this.contentManager.Load<SoundEffect> ( resource.Path ).CreateInstance ( ) );
                    break;

                case ResourceType.Music:
                    this.music.Add ( resource.Name, this.contentManager.Load<Song> ( resource.Path ) );
                    break;
            }

    }
    catch { }

}

调用 ResourceManager 的 LoadContent 方法来载入资源,在 LoadContent 方法中,我们会创建一个新的 ContentManager 对象,并通过他的 Load 方法载入我们事先接受的资源。

要创建 ContentManager,我们还需要设置 ResourceManager 的 World 字段(你也可以将他修改为属性)。所以,在调用 LoadContent 之前,你需要确保 World 字段不为空。

当你觉得这些资源已经没有用了的时候,调用 UnloadContent 方法来释放资源:

public void UnloadContent ( )
{

    foreach ( Texture2D texture in this.textures.Values )
        texture.Dispose ( );

    foreach ( SoundEffectInstance sound in this.sounds.Values )
        sound.Dispose ( );

    foreach ( Song song in this.music.Values )
        song.Dispose ( );

    this.textures.Clear ( );
    this.fonts.Clear ( );
    this.sounds.Clear ( );
    this.music.Clear ( );

    if ( !this.Resources.IsReadOnly )
        this.Resources.Clear ( );

    if ( null != this.contentManager )
        this.contentManager.Unload ( );

}

在上面的代码中,我们先对所有的资源调用了 Dispose 方法,然后调用了 ContentManager 的 Unload 方法。

使用资源

最后,我们在 World 类中具体的使用一下 ResourceManager 类。

private readonly ResourceManager resourceManager;

public World ( Color backgroundColor )
    : base ( )
{
    // ...

    this.resourceManager = new ResourceManager ( new Resource[] {
        new Resource ( "bird", ResourceType.Image, @"image\bird" ),
        new Resource ( "click", ResourceType.Sound, @"sound\click" )
    } );
    this.resourceManager.World = this;

}

在 World 的构造函数中,我们创建了一个 ResourceManager,并指出我们需要一个图片和一个波形文件。

protected override void OnNavigatedTo ( NavigationEventArgs e )
{
    // ...
    
    this.resourceManager.LoadContent ( );

    base.OnNavigatedTo ( e );
}

当页面载入之后,我们调用 LoadContent 方法来载入图片和声音。之后,我们在 OnUpdate 和 OnDraw 方法中使用他们。

private void OnUpdate ( object sender, GameTimerEventArgs e )
{
    this.resourceManager.GetSound ( "click" ).Play ( );
}

private void OnDraw ( object sender, GameTimerEventArgs e )
{
    this.GraphicsDevice.Clear ( this.BackgroundColor );

    this.spiritBatch.Begin ( );
    this.spiritBatch.Draw ( this.resourceManager.GetTexture ( "bird" ), new Vector2 ( 20, 20 ), Color.White );
    this.spiritBatch.End ( );
}

通过 ResourceManager 的 GetTexture 和 GetSound 方法可以获取指定的图片和声音,只需要传递资源的名称即可。

最后,你还需要在一个适合的位置调用 ResourceManager 的 UnloadContent 方法。

本期视频 http://v.youku.com/v_show/id_XNTYwOTA2ODgw.html
项目地址 http://wp-xna.googlecode.com/

更多内容 WPXNA
平方开发的游戏 http://zoyobar.lofter.com/
QQ 群 213685539

欢迎平方在其他位置发布的同一文章:http://www.wpgame.info/post/decc4_6329f7

原文地址:https://www.cnblogs.com/zoyobar/p/wpxna2.html