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

场景,屏幕

这里的场景也就是屏幕或者页面,比如我们常说的主屏幕,主屏幕上通常有一个开始的按钮。平方创建了 Scene 类来表示一个屏幕,页面,场景。而 Scene 类中将包含我们之前所将到的一些类,比如:ResourceManager,AudioManager 等。

下面中 Scene 类的一些字段和属性。

  1. internal readonly GestureType GestureType;
  2. private bool isClosed;
  3. public bool IsClosed
  4. {
  5. get { return this.isClosed; }
  6. set { this.isClosed = value; }
  7. }
  8. protected bool isEnabled = true;
  9. public bool IsEnabled
  10. {
  11. get { return this.isEnabled; }
  12. }
  13.  
  14. internal readonly bool IsBroken;
  15.  
  16. internal readonly Vector2 Location;
  17. protected World world;
  18. public World World
  19. {
  20. get { return this.world; }
  21. set
  22. {
  23. this.resourceManager.World = value;
  24. this.world = value;
  25. }
  26. }
  27.  
  28. private SpriteBatch spiritBatch;
  29. private readonly ResourceManager resourceManager;
  30. protected AudioManager audioManager;
  31. public AudioManager AudioManager
  32. {
  33. get { return this.audioManager; }
  34. }
  35. private readonly bool isBackgroundMusicLoop;
  36.  
  37. protected readonly Dictionary<string, Making> makings = new Dictionary<string, Making> ( );
  38. public Dictionary<string, Making> Makings
  39. {
  40. get { return this.makings; }
  41. }

字段 GestureType 表示 Scene 所支持的手势,比如:双击,单击等。

属性 IsClosed 表示 Scene 是否已经关闭,而属性 IsEnabled 表示 Scene 是否可用。

字段 IsBroken 表示是否进行精灵的绘制。字段 Location 表示 Scene 的位置。属性 World 表示场景所在的 World。

字段 spiritBatch 用来绘制屏幕中所有的内容,他从 World 中获得。字段 resourceManager 用来管理所有需要的资源,字段 audioManager 用来管理音频,字段 isBackgroundMusicLoop 表示背景音乐是否可以循环播放。

属性 Makings 表示屏幕中所有的元件,比如:标签。

之后,我们在构造函数中初始化一些字段。

  1. protected Scene ( Vector2 location, GestureType gestureType, IList<Resource> resources, IList<Making> makings, bool isBroken, bool isBackgroundMusicLoop )
  2. {
  3. this.Location = location;
  4. this.GestureType = gestureType;
  5.  
  6. this.resourceManager = new ResourceManager ( resources );
  7. this.audioManager = new AudioManager ( );
  8.  
  9. if ( null != makings )
  10. foreach ( Making making in makings )
  11. if ( null != making )
  12. this.makings.Add ( making.Name, making );
  13.  
  14. foreach ( Making making in this.makings.Values )
  15. making.Init ( this );
  16.  
  17. this.IsBroken = isBroken;
  18. this.isBackgroundMusicLoop = isBackgroundMusicLoop;
  19. }

注意这里,我们为 Making 类增加了一个 Init 方法,用来初始化元件。我们看到,在 Making 的 Init 方法中,如果 Making 实现了 ILockable 接口,则我们根据 Scene 的位置来修改 Making 的位置。

  1. protected Scene scene;
  2.  
  3. internal virtual void Init ( Scene scene )
  4. {
  5. this.scene = scene;
  6.  
  7. if ( this is ILockable )
  8. ( this as ILockable ).Location += scene.Location;
  9.  
  10. }

在 LoadContent 方法中,我们从 World 获取了 SpriteBatch 服务,并调用了 ResourceManager 的 LoadContent 方法载入资源,并将这些资源传递给 Making 和 AudioManager。此后,AudioManager 试图播放名称为 scene.sound 的音乐。

在 UnloadContent 方法中,我们卸载 ResourceManager 所加载的资源。而在 Dispose 中,除了卸载资源,我们还会调用 Making 的 Dispose 方法。

  1. public virtual void LoadContent ( )
  2. {
  3.  
  4. this.spiritBatch = this.world.Services.GetService ( typeof ( SpriteBatch ) ) as SpriteBatch;
  5.  
  6. this.resourceManager.LoadContent ( );
  7.  
  8. foreach ( Making making in this.makings.Values )
  9. making.InitResource ( this.resourceManager );
  10.  
  11. this.audioManager.LoadContent ( this.resourceManager );
  12.  
  13. this.audioManager.PlayMusic ( "scene.sound", this.isBackgroundMusicLoop );
  14. }
  15.  
  16. public virtual void UnloadContent ( )
  17. {
  18. this.audioManager.UnloadContent ( );
  19.  
  20. this.resourceManager.UnloadContent ( );
  21. }
  22.  
  23. public virtual void Dispose ( )
  24. {
  25.  
  26. foreach ( Making making in this.makings.Values )
  27. making.Dispose ( );
  28.  
  29. this.UnloadContent ( );
  30. }

在 Update 方法中,我们判断如果 Scene 被关闭或者不可用,则不进行更新,从 Scene 派生的类可以修改 updating 方法来实现自己的功能。而 Draw 方法中,我们判断如果 Scene 被关闭,则不进行绘制,从 Scene 派生的类可以修改 drawing 方法来绘制自己的内容。

  1. protected virtual void updating ( GameTime time )
  2. { }
  3.  
  4. public void Update ( GameTime time )
  5. {
  6.  
  7. if ( this.isClosed || !this.isEnabled )
  8. return;
  9.  
  10. this.updating ( time );
  11. }
  12.  
  13. protected virtual void drawing ( GameTime time, SpriteBatch batch )
  14. { }
  15.  
  16. public void Draw ( GameTime time )
  17. {
  18.  
  19. if ( this.isClosed )
  20. return;
  21.  
  22. this.spiritBatch.Begin ( );
  23. this.drawing ( time, this.spiritBatch );
  24. this.spiritBatch.End ( );
  25. }

Scene 类还接受 Controller 类,用来判断用户的输入。派生类可以修改 inputing 方法来完成相关的操作。

  1. protected virtual void inputing ( Controller controller )
  2. { }
  3.  
  4. public bool Input ( Controller controller )
  5. {
  6.  
  7. if ( this.isClosed || !this.isEnabled )
  8. return false;
  9.  
  10. this.inputing ( controller );
  11. return true;
  12. }

最后,Scene 类可以调用自己的 Close 方法来关闭自己。在关闭之前,我们还触发了 Closing 和 Closed 事件,可以在此时弹出窗口询问用户是否关闭。

  1. internal event EventHandler<SceneEventArgs> Closing;
  2. internal event EventHandler<SceneEventArgs> Closed;
  3.  
  4. public void Close ( )
  5. {
  6.  
  7. if ( this.isClosed )
  8. return;
  9.  
  10. if ( null == this.world )
  11. throw new NullReferenceException ( "world can't be null when closing scene" );
  12.  
  13. if ( null != this.Closing )
  14. {
  15. SceneEventArgs closingArg = new SceneEventArgs ( );
  16.  
  17. this.Closing ( this, closingArg );
  18.  
  19. if ( closingArg.IsCancel )
  20. return;
  21.  
  22. }
  23.  
  24. if ( null != this.Closed )
  25. this.Closed ( this, new SceneEventArgs ( ) );
  26.  
  27. this.world.RemoveScene ( this );
  28. }

修改 World 类

为 World 增加一个 isPreserved 用来表示游戏是否直接从内存恢复,我们可以在 activate 方法中,通过 IsApplicationInstancePreserved 属性来获取他,如果 isPreserved 为 true,则我们不需要再次执行 OnNavigatedTo 中的代码。

  1. private bool isPreserved = false;
  2.  
  3. private void activate ( object sender, ActivatedEventArgs e )
  4. { this.isPreserved = e.IsApplicationInstancePreserved; }
  5.  
  6. protected override void OnNavigatedTo ( NavigationEventArgs e )
  7. {
  8.  
  9. if ( this.isPreserved )
  10. return;
  11.  
  12. // ...
  13. }

增加一个 isInitialized 字段,用来表示 World 是否已经初始化,以此来决定是否初始化被 World 管理的 Scene。在方法 OnNavigatedTo 中,我们会设置 isInitialized 为 true。

  1. private bool isInitialized = false;
  2.  
  3. protected override void OnNavigatedTo ( NavigationEventArgs e )
  4. {
  5. // ...
  6.  
  7. this.isInitialized = true;
  8.  
  9. // ...
  10. }

之后,我们要为 World 增加管理 Scene 的功能。

  1. private readonly List<Scene> scenes = new List<Scene> ( );
  2.  
  3. protected override void OnNavigatedTo ( NavigationEventArgs e )
  4. {
  5. // ...
  6.  
  7. foreach ( Scene scene in this.scenes )
  8. scene.LoadContent ( );
  9.  
  10. // ...
  11. }
  12.  
  13. private void appendScene ( Scene scene, Type afterSceneType, bool isInitialized )
  14. {
  15.  
  16. if ( null == scene )
  17. return;
  18.  
  19. if ( !isInitialized )
  20. {
  21. scene.World = this;
  22. scene.IsClosed = false;
  23.  
  24. if ( this.isInitialized )
  25. scene.LoadContent ( );
  26.  
  27. }
  28.  
  29. int index = this.getSceneIndex ( afterSceneType );
  30.  
  31. if ( index < )
  32. this.scenes.Add ( scene );
  33. else
  34. this.scenes.Insert ( index, scene );
  35.  
  36. TouchPanel.EnabledGestures = scene.GestureType;
  37. }
  38.  
  39. internal void RemoveScene ( Scene scene )
  40. {
  41.  
  42. if ( null == scene || !this.scenes.Contains ( scene ) )
  43. return;
  44.  
  45. scene.IsClosed = true;
  46.  
  47. if ( this.isInitialized )
  48. try
  49. {
  50.  
  51. if ( null != scene )
  52. scene.Dispose ( );
  53.  
  54. }
  55. catch
  56. { scene.Dispose ( ); }
  57.  
  58. this.scenes.Remove ( scene );
  59.  
  60. if ( this.scenes.Count > )
  61. TouchPanel.EnabledGestures = this.scenes[ this.scenes.Count - ].GestureType;
  62.  
  63. }
  64.  
  65. private int getSceneIndex ( Type sceneType )
  66. {
  67.  
  68. if ( null == sceneType )
  69. return -;
  70.  
  71. string type = sceneType.ToString ( );
  72.  
  73. for ( int index = this.scenes.Count - ; index >= ; index-- )
  74. if ( this.scenes[ index ].GetType ( ).ToString ( ) == type )
  75. return index;
  76.  
  77. return -;
  78. }
  79.  
  80. private T getScene<T> ( )
  81. where T : Scene
  82. {
  83. int index = this.getSceneIndex ( typeof ( T ) );
  84.  
  85. return index == - ? null : this.scenes[ index ] as T;
  86. }

字段 scenes 中包含了 World 所管理的所有场景,在 OnNavigatedTo 方法中,我们会对已经添加的场景调用一次 LoadContent 方法,但这种情况较少出现。

方法 appendScene 用于将场景添加到 World 当中,参数 afterSceneType 用来指示场景的次序,参数 isInitialized 一般为 false,表示需要初始化场景的资源。

方法 RemoveScene 用于将场景从 World 中移除,getSceneIndex 方法用来获取某一个场景的索引,方法 getScene<T7gt; 用来获取指定的场景。

  1. private void OnUpdate ( object sender, GameTimerEventArgs e )
  2. {
  3.  
  4. if ( !this.IsEnabled )
  5. return;
  6.  
  7. this.controller.Update ( );
  8.  
  9. Scene[] scenes = this.scenes.ToArray ( );
  10. GameTime time = new GameTime ( e.TotalTime, e.ElapsedTime );
  11.  
  12. foreach ( Scene scene in scenes )
  13. if ( null != scene )
  14. scene.Update ( time );
  15.  
  16. for ( int index = scenes.Length - ; index >= ; index-- )
  17. if ( null != scenes[ index ] && scenes[ index ].Input ( this.controller ) )
  18. break;
  19.  
  20. }
  21.  
  22. private void OnDraw ( object sender, GameTimerEventArgs e )
  23. {
  24. this.GraphicsDevice.Clear ( this.BackgroundColor );
  25.  
  26. bool isBroken = false;
  27. GameTime time = new GameTime ( e.TotalTime, e.ElapsedTime );
  28.  
  29. foreach ( Scene scene in this.scenes.ToArray ( ) )
  30. if ( null != scene )
  31. {
  32.  
  33. if ( !isBroken && scene.IsBroken )
  34. {
  35. // Draw sprites
  36. isBroken = true;
  37. }
  38.  
  39. scene.Draw ( time );
  40. }
  41.  
  42. if ( !isBroken )
  43. // Draw sprites.
  44. ;
  45.  
  46. }

在 OnUpdate 方法中,我们会更新每一个场景,而只有第一个场景可以接受用户的输入。在 OnDraw 方法中,我们会逐个绘制场景。

一个例子

我们创建一个名为 SceneT9 的场景。

  1. internal sealed class SceneT9
  2. : Scene
  3. {
  4. private readonly Label l1;
  5. private readonly Movie bird2;
  6.  
  7. internal SceneT9 ( )
  8. : base ( Vector2.Zero, GestureType.Tap | GestureType.DoubleTap,
  9. new Resource[] {
  10. new Resource ( "bird2.image", ResourceType.Image, @"image\bird2" ),
  11. new Resource ( "click.s", ResourceType.Sound, @"sound\click" ),
  12. new Resource ( "peg", ResourceType.Font, @"font\myfont" ),
  13. },
  14. new Making[] {
  15. new Movie ( "bird2.m", "bird2.image", new Vector2 ( , ), , , , , "live",
  16. new MovieSequence ( "live", true, new Point ( , ), new Point ( , ) ),
  17. new MovieSequence ( "dead", , false, new Point ( , ), new Point ( , ) )
  18. ),
  19. new Label ( "l1", "Hello windows phone!", 2f, Color.LightGreen, 0f )
  20. }
  21. )
  22. {
  23. this.l1 = this.makings[ "l1" ] as Label;
  24. this.bird2 = this.makings[ "bird2.m" ] as Movie;
  25. }
  26.  
  27. protected override void inputing ( Controller controller )
  28. {
  29.  
  30. if ( !controller.IsGestureEmpty && controller.Gestures[ ].GestureType == GestureType.Tap )
  31. this.audioManager.PlaySound ( "click.s" );
  32.  
  33. }
  34.  
  35. protected override void updating ( GameTime time )
  36. {
  37. Movie.NextFrame ( this.bird2 );
  38.  
  39. base.updating ( time );
  40. }
  41.  
  42. protected override void drawing ( GameTime time, SpriteBatch batch )
  43. {
  44. base.drawing ( time, batch );
  45.  
  46. Label.Draw ( this.l1, batch );
  47. Movie.Draw ( this.bird2, time, batch );
  48. }
  49.  
  50. }

在 SceneT9 中,我们为场景添加了所需要的资源,和两个元件,一个是小鸟电影,一个是标签。通过场景的 makings 字段我们将这个两个元件分别保存在 l1 和 bird2 中。

在 updating 和 drawing 方法中,我们分别更新了电影和绘制元件。在 inputing 方法中,我们判断用户是否有点击的动作并播放点击的声音。

在 World 的 OnNavigatedTo 方法中,我们使用 appendScene 方法来添加 SceneT9。

  1. protected override void OnNavigatedTo ( NavigationEventArgs e )
  2. {
  3. // ...
  4.  
  5. this.appendScene ( new mygame.test.SceneT9 ( ), null, false );
  6.  
  7. base.OnNavigatedTo ( e );
  8. }

本期视频 http://v.youku.com/v_show/id_XNTczNjYzOTQ4.html

项目地址 http://wp-xna.googlecode.com/
更多内容 WPXNA

平方开发的游戏 http://zoyobar.lofter.com/

QQ 群 213685539

欢迎访问我在其他位置发布的同一文章:http://www.wpgame.info/post/decc4_6da46a

使用 Scene 类在 XNA 中创建不同的场景(八)的更多相关文章

  1. 使用 CommandScene 类在 XNA 中创建命令场景(十二)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  2. 使用 Spirit 类在 XNA 中创建游戏中的基本单位精灵(十三)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  3. 使用 Button 类在 XNA 中创建图形按钮(九)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  4. 使用 Anime 类在 XNA 中创建小动画(十一)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  5. 使用 NPC,NPCManager 在 XNA 中创建 NPC

    使用 NPC,NPCManager 在 XNA 中创建 NPC 平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐 ...

  6. 编写Java程序,使用ThreadLocal类,项目中创建账户类 Account,类中包括账户名称name、 ThreadLocal 类的引用变量amount,表示存款

    查看本章节 查看作业目录 需求说明: 某用户共有两张银行卡,账户名称相同,但卡号和余额不同.模拟用户使用这两张银行卡进行消费的过程,并打印出消费明细 实现思路: 项目中创建账户类 Account,类中 ...

  7. 使用 NPC,NPCManager 在 XNA 中创建 NPC(十九)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  8. 使用 Region,RegionManager 在 XNA 中创建特殊区域(十八)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  9. 使用 Pinup,PinupManager 在 XNA 中创建贴图(十七)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

随机推荐

  1. 微信开发 config:invalid url domain

    当遇到config:invalid url domain 有2种可能 1.没有配置url. 2.url配置错误.配置url如http://write.blog.csdn.NET/,就要这样配置writ ...

  2. 变更gcc版本

    当前的GCC版本为GCC-4.2,需要切换到GCC-3.4.首先,你需要去你的usr/bin/下去看看有没有gcc-3.4这样文件,如果没有的话,就安装一下吧: apt-get install gcc ...

  3. 使用as开发jni入门(附验证):配置ndk开发环境,配置as相关jni配置

    编写jni,生成so文件: 1.通过as内置的Android SDK下载需要使用的ndk,在系统环境变量设置相关参数 2.新建一个普通as项目,新建一个类,用来静态加载so库和书写本地native方法 ...

  4. 【整站源码分享】分享一个JFinal3.4开发的整站源码,适合新手学习

    分享这个源码是14年开发上线的<威海创业者>站点的全套整站源码,前后端都在一个包里.当时开发使用的是JFinal1.4,最近改成了JFinal3.4.使用的JSP做的页面.有一定的参考价值 ...

  5. /pentest/enumeration/irpas/itrace

    /pentest/enumeration/irpas/itrace 追踪防火墙内部路由

  6. MySQL开启skip-name-resolve和skip-networking优化

    使用skip-name-resolve增加远程连接速度 skip-name-resolve 该选项表示禁用DNS解析,属于官方一个系统上的特殊设定不管,链接的的方式是经过hosts或是IP的模式,他都 ...

  7. document.all.item作用

    1.document.all.myCheckBox和 document.all.item通过控件的名字定位控件,item()中是控件的名字例如:<input type="checkbo ...

  8. ABAP,Java和JavaScript的序列化,反序列化

    ABAP 1. ABAP提供了一个工具类cl_proxy_xml_transform,通过它的两个方法abap_to_xml_xstring和xml_xstring_to_abap实现两种格式的互换. ...

  9. 如何下载js类库

    https://bower.io/  这个已经淘汰 https://learn.jquery.com/jquery-ui/environments/bower/ Web sites are made ...

  10. [BZOJ4327]:[JZOI2012]玄武密码(AC自动机)

    题目传送门 题目描述: 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河.相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中.老人们说,这是玄武神灵将天书藏匿在此.  ...