打造轻量级Windows Phone7 游戏引擎-Samurai 第五话 使用Samurai创建游戏
2013-10-24 13:23
507 查看
打造轻量级Windows Phone7 游戏引擎-Samurai 第五话 使用Samurai创建游戏
博客写了有一段时间了,其实目前我自己在制作Samurai的过程中已经写好了一个WP7的游戏了,应该两周后大家就可以在应用商店见到了。还是蛮开心的,今天终于可以带大家一起试着使用Samurai了。
首先呢,如果读者您比较偷懒一些,可以到:http://download.csdn.net/detail/u011257271/6448941去下载下面要讲到的整个样例程序,直接就可以运行了~
如果您是一位勤劳的读者,那么,请先到:http://download.csdn.net/detail/u011257271/6448961去下载目前的Samurai项目模板。当然了也请到:http://download.csdn.net/detail/u011257271/6448941去下载下面要讲到的整个样例程序,因为有一些图像资源我们将会用到。
先说下如何导入模板:下载了Samurai模板zip文件后,直接将其放置在“\My Documents\Visual Studio 2010\Templates\ProjectTemplates\语言\”文件夹中。
(比如我本人的是“E:\Users\BC\Documents\Visual Studio 2012\Templates\ProjectTemplates\Visual C#”)
然后打开VS,像往常一样新建“windows phone游戏”项目,当项目生成好后,我们右击解决方案->添加->新建项目->点选Visual C#->找到“SamuraiPublicV0.1”即可
然后我们给游戏主项目“添加引用”
这样,我们就可以开始了~
开始的开始,项目就是下图的样子了:
当前的Samurai只有如下的功能:
Controls(控件),Globals(全局共享的引用),Graphics(图像管理),Inputs(输入检测),Music(音乐音效),Screens(页面管理),Sprites(精灵(图像)绘制)
先说下我们的样例做出来后长啥样,有啥功能吧:
这个样例有5个页面,一个是菜单页面,有标题以及按钮,点击按钮Play跳转到其他页面。另外四个页面很空旷啊~(分别代表了春夏秋冬...)只有一个Menu的按钮,点击会回到菜单页面来,当然了,如果你在这四个页面之间做出滑动的手势的话,页面也会进行相应的跳转。
下面是实现后的截图:(哎呀,看来截图暴露了我当前在做的...)
现在请大家先将下载的资源里的“Images/”中的三张图像(包括文件夹Images)添加到Content项目中:
我们先来重写一个导演类:(会看到创建页面 模块是红的,没关系,因为我们还没有创建页面)
然后,我们将阿SA导演注册给Game1老大哥:
通过这两行语句我们设置了竖屏和全屏:
然后原本点击Back键的检测也注释掉了。
当前已经完成了Director和Game的对接,接下来我们实现各个页面:
先来实现MenuScreen: 类中定义了四个简单的图像以及六个Button
我们可以看到,Button可以有不同的构造,勤奋固然好,偷懒的做法也是允许的~我们重写了LoadContent、Init以及SetupInput。
LoadContent中主要放的是一次性加载的资源,以后页面回收利用的时候就不会再LoadContent了,而所有页面回收需要重新初始化的变量,我们在Init中进行初始化,特别的是,与触控相关的(比如Button)必须要在Init中进行初始化,因为实际上Button在初始化时对SAInput进行了注册,而每当页面回收时,SAInput会清空,所以Button在页面重用的过程中必须重新注册。
如果您已经等急了,那么先把这个模块:
替换成:
好的,我们可以运行一下程序了~大家可以试着点击不同的Button看看他们有什么不同的表现~
接下来是“春天的页面”
这个页面,我们的注意力主要放在
1.页面跳转:
其实就是Screen通过构造函数中的委托获得了上下文中的管理页面的权限,可以在自己方便的时候使用跳转到其他页面的这个权利。
大家看“状态模式”的话,往往这里给的是一个上下文(Context)的引用,但是由于C#的语言特性,使用委托实现是一种更为巧妙的方式。
2.触控接口:
关于响应事件,大家应该也看得比较明白吧。多说一句就是——对于像FreeDrag之类的手势,会有DragComplete事件,我们在注册FreeDrag时不要忘记注册DragComplete。那么我们如何判断Drag的方向呢?通过GestureSample的Delta.X的值的正负来判断。但是这个判断只能在FreeDrag注册的响应事件中进行判断,DragComplete中并不给出响应的方向信息。这个页面中对FreeDrag手势的使用其实是一个错误的方式,正确的方式请看下一个页面:
关于这个页面,没啥好多说的了~
不过还是有要说的:春夏秋冬四个页面每个页面都差不多,这种Ctrl C+Ctrl V的方式来写页面也太逊了吧~
那么我们其实只需要修改一下相关的private为protected:(好吧已经到了秋页面了)
OK,最后一个页面继承自秋页面:
是不是通过继承,冬页面看起来就有点儿专业了?
最后一步,如果刚刚把您把“创建页面”模块替换掉了,那么现在再把它换回来:(在MyDirector中)
点击运行吧~
以上~
虽然效果都基本上有了,但是有一个地方很不美气:
就是ScreenType枚举定义的位置,我们可以在
位置见到:
这个枚举是在Samurai项目中定义的,也就是说平时我们写好页面后,还要过来手工地“注册”一下(显得好傻呀),希望大家有更好的解决方法告我一声~
遇见不明白的地方,欢迎提问;遇见我犯下的错误或者做的不好的地方,欢迎指正~
最后呢?我到11月中旬时间就比较宽裕了,在那时想再写两个系类的博客——“3天打造WP版《会说话的汤姆猫》”以及“7天打造WP版《Doodle Jump》”敬请关注~
博客写了有一段时间了,其实目前我自己在制作Samurai的过程中已经写好了一个WP7的游戏了,应该两周后大家就可以在应用商店见到了。还是蛮开心的,今天终于可以带大家一起试着使用Samurai了。
首先呢,如果读者您比较偷懒一些,可以到:http://download.csdn.net/detail/u011257271/6448941去下载下面要讲到的整个样例程序,直接就可以运行了~
如果您是一位勤劳的读者,那么,请先到:http://download.csdn.net/detail/u011257271/6448961去下载目前的Samurai项目模板。当然了也请到:http://download.csdn.net/detail/u011257271/6448941去下载下面要讲到的整个样例程序,因为有一些图像资源我们将会用到。
先说下如何导入模板:下载了Samurai模板zip文件后,直接将其放置在“\My Documents\Visual Studio 2010\Templates\ProjectTemplates\语言\”文件夹中。
(比如我本人的是“E:\Users\BC\Documents\Visual Studio 2012\Templates\ProjectTemplates\Visual C#”)
然后打开VS,像往常一样新建“windows phone游戏”项目,当项目生成好后,我们右击解决方案->添加->新建项目->点选Visual C#->找到“SamuraiPublicV0.1”即可
然后我们给游戏主项目“添加引用”
这样,我们就可以开始了~
开始的开始,项目就是下图的样子了:
当前的Samurai只有如下的功能:
Controls(控件),Globals(全局共享的引用),Graphics(图像管理),Inputs(输入检测),Music(音乐音效),Screens(页面管理),Sprites(精灵(图像)绘制)
先说下我们的样例做出来后长啥样,有啥功能吧:
这个样例有5个页面,一个是菜单页面,有标题以及按钮,点击按钮Play跳转到其他页面。另外四个页面很空旷啊~(分别代表了春夏秋冬...)只有一个Menu的按钮,点击会回到菜单页面来,当然了,如果你在这四个页面之间做出滑动的手势的话,页面也会进行相应的跳转。
下面是实现后的截图:(哎呀,看来截图暴露了我当前在做的...)
现在请大家先将下载的资源里的“Images/”中的三张图像(包括文件夹Images)添加到Content项目中:
我们先来重写一个导演类:(会看到创建页面 模块是红的,没关系,因为我们还没有创建页面)
class MyDirector:Samurai.SADirector { public MyDirector(Game game, GraphicsDeviceManager graphics, SpriteBatch spriteBatch) : base(game,graphics,spriteBatch) { //跳转页面 ChangeScreenTo(ScreenType.Menu); } /// <summary> /// 简化的状态转换方法,实际上为了页面的重用,在基类中实现了一个管理者, /// 通过页面的ScreenType为Key,Screen的创建方法为Value,如果不存在该页面则创建, /// 否则ReInit()重新使用即可 /// </summary> /// <param name="screenType"></param> public override void ChangeScreenTo(ScreenType screenType) { switch (screenType) { case ScreenType.Menu: ChangeScreenTo(ScreenType.Menu,CreateMenuScreen); break; case ScreenType.SpringScreen: ChangeScreenTo(ScreenType.SpringScreen, CreateSpringScreen); break; case ScreenType.SummerScreen: ChangeScreenTo(ScreenType.SummerScreen, CreateSummerScreen); break; case ScreenType.AutumnScreen: ChangeScreenTo(ScreenType.AutumnScreen, CreateAutumnScreen); break; case ScreenType.WinterScreen: ChangeScreenTo(ScreenType.WinterScreen, CreateWinterScreen); break; } } #region 创建界面 public SAScreen CreateMenuScreen() { return new MenuScreen(ChangeScreenTo); } public SAScreen CreateSpringScreen() { return new SpringScreen(ChangeScreenTo); } public SAScreen CreateSummerScreen() { return new SummerScreen(ChangeScreenTo); } public SAScreen CreateAutumnScreen() { return new AutumnScreen(ChangeScreenTo); } public SAScreen CreateWinterScreen() { return new WinterScreen(ChangeScreenTo); } #endregion }
然后,我们将阿SA导演注册给Game1老大哥:
public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //TODO SADirector director; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; TargetElapsedTime = TimeSpan.FromTicks(333333); InactiveSleepTime = TimeSpan.FromSeconds(1); //设置竖屏 SAGraphicUtil.SetVertical(graphics); //设置全屏 graphics.IsFullScreen = true; } protected override void Initialize() { // TODO base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); // TODO director = new MyDirector(this, graphics, spriteBatch); } protected override void UnloadContent() { // TODO director.UnloadContent(); } protected override void Update(GameTime gameTime) { //if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) // this.Exit(); // TODO director.Update(gameTime); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { // TODO director.Draw(gameTime); base.Draw(gameTime); } }
通过这两行语句我们设置了竖屏和全屏:
//设置竖屏 SAGraphicUtil.SetVertical(graphics); //设置全屏 graphics.IsFullScreen = true;
然后原本点击Back键的检测也注释掉了。
当前已经完成了Director和Game的对接,接下来我们实现各个页面:
先来实现MenuScreen: 类中定义了四个简单的图像以及六个Button
class MenuScreen:SAScreen { SASimpleSprite backSprite; SASimpleSprite title1Sprite; SASimpleSprite title2Sprite; SASimpleSprite logoSprite; SAButton musicButton; SAButton soundButton; SAButton helpButton; SAButton aboutButton; SAButton scoreButton; SAButton playButton; public MenuScreen(ChangeScreenDelegate changeScreen): base(changeScreen){} //特别提醒,不需要Reinit的变量在此初始化(所有与Input有关的变量必须在Init中初始化) public override void LoadContent() { backSprite = new SASimpleSprite("Images/BeginBack"); title1Sprite = new SASimpleSprite("Images/Resource", new Rectangle(0, 0, 238, 72), new Vector2(30, 130),Color.White); title2Sprite = new SASimpleSprite("Images/Resource", new Rectangle(240, 0, 198, 72), new Vector2(256, 217),Color.White); logoSprite = new SASimpleSprite("Images/Resource",new Rectangle(440, 0, 386, 174), new Vector2(290, 84),Color.White); } public override void Init() { //最全面的 musicButton = new SAButton("Images/Resource", new Rectangle[] { new Rectangle(0, 220, 148, 35), new Rectangle(200, 220, 148, 35), new Rectangle(200, 220, 148, 35) }, new Vector2(180, 667), OnMusicButtonClick); //较全面的 playButton = new SAButton("Images/Resource", new Rectangle[] { new Rectangle(0, 80, 194, 53), new Rectangle(200, 80, 194, 53) }, new Vector2(143, 385), BeginToPlay); soundButton = new SAButton("Images/Resource", new Rectangle[] { new Rectangle(0, 260, 148, 35), new Rectangle(200, 260, 148, 35) }, new Vector2(32, 587), OnMusicButtonClick); helpButton = new SAButton("Images/Resource", new Rectangle[] { new Rectangle(0, 300, 120, 35) }, new Vector2(328, 747), OnClick); //最偷懒的 aboutButton = new SAButton("Images/Resource", new Rectangle(0, 180, 148, 35), new Vector2(280, 35), OnClick); scoreButton = new SAButton("Images/Resource", new Rectangle(0, 140, 148, 35), new Vector2(308, 507), OnClick); } public override void SetupInput() { SAInput.EnableBackButton(OnBackButton); } private void OnBackButton() { SAGlobal.game.Exit(); } public override void Draw(GameTime gameTime) { backSprite.Draw(); logoSprite.Draw(); title1Sprite.Draw(); title2Sprite.Draw(); musicButton.Draw(); soundButton.Draw(); helpButton.Draw(); aboutButton.Draw(); scoreButton.Draw(); playButton.Draw(); } #region 响应事件 public void OnMusicButtonClick() { } public void OnClick() { } public void BeginToPlay() { ChangeScreenTo(ScreenType.SpringScreen); } #endregion }
我们可以看到,Button可以有不同的构造,勤奋固然好,偷懒的做法也是允许的~我们重写了LoadContent、Init以及SetupInput。
LoadContent中主要放的是一次性加载的资源,以后页面回收利用的时候就不会再LoadContent了,而所有页面回收需要重新初始化的变量,我们在Init中进行初始化,特别的是,与触控相关的(比如Button)必须要在Init中进行初始化,因为实际上Button在初始化时对SAInput进行了注册,而每当页面回收时,SAInput会清空,所以Button在页面重用的过程中必须重新注册。
如果您已经等急了,那么先把这个模块:
#region 创建界面 public SAScreen CreateMenuScreen() { return new MenuScreen(ChangeScreenTo); } public SAScreen CreateSpringScreen() { return new SpringScreen(ChangeScreenTo); } public SAScreen CreateSummerScreen() { return new SummerScreen(ChangeScreenTo); } public SAScreen CreateAutumnScreen() { return new AutumnScreen(ChangeScreenTo); } public SAScreen CreateWinterScreen() { return new WinterScreen(ChangeScreenTo); } #endregion
替换成:
#region 创建界面 public SAScreen CreateMenuScreen() { return new MenuScreen(ChangeScreenTo); } public SAScreen CreateSpringScreen() { return null;//new SpringScreen(ChangeScreenTo); } public SAScreen CreateSummerScreen() { return null;//new SummerScreen(ChangeScreenTo); } public SAScreen CreateAutumnScreen() { return null;// new AutumnScreen(ChangeScreenTo); } public SAScreen CreateWinterScreen() { return null;//new WinterScreen(ChangeScreenTo); } #endregion
好的,我们可以运行一下程序了~大家可以试着点击不同的Button看看他们有什么不同的表现~
接下来是“春天的页面”
class SpringScreen:SAScreen { SASimpleSprite backSprite; SAButton menuButton; public SpringScreen(ChangeScreenDelegate changeScreenDelegate) : base(changeScreenDelegate) { } public override void LoadContent() { backSprite = new SASimpleSprite("Images/Season"); } public override void Init() { menuButton = new SAButton("Images/Resource", new Rectangle[] { new Rectangle(0, 340, 156, 44), new Rectangle(200, 340, 156, 44) }, new Vector2(143, 385), OnMenuButtonClick); } public override void SetupInput() { SAInput.EnableBackButton(OnBackButton); SAInput.EnableGesture(Microsoft.Xna.Framework.Input.Touch.GestureType.FreeDrag, OnFreeDrag); SAInput.EnableGesture(Microsoft.Xna.Framework.Input.Touch.GestureType.DragComplete, OnDragComplete); } private void OnBackButton() { ChangeScreenTo(ScreenType.Menu); } private void OnFreeDrag(Microsoft.Xna.Framework.Input.Touch.GestureSample gestureSample) { } private void OnDragComplete(Microsoft.Xna.Framework.Input.Touch.GestureSample gestureSample) { if (gestureSample.Delta.X <= 0) { ChangeScreenTo(ScreenType.SummerScreen); } } private void OnMenuButtonClick() { ChangeScreenTo(ScreenType.Menu); } public override void Draw(GameTime gameTime) { backSprite.Draw(); menuButton.Draw(); } }
这个页面,我们的注意力主要放在
1.页面跳转:
其实就是Screen通过构造函数中的委托获得了上下文中的管理页面的权限,可以在自己方便的时候使用跳转到其他页面的这个权利。
大家看“状态模式”的话,往往这里给的是一个上下文(Context)的引用,但是由于C#的语言特性,使用委托实现是一种更为巧妙的方式。
2.触控接口:
关于响应事件,大家应该也看得比较明白吧。多说一句就是——对于像FreeDrag之类的手势,会有DragComplete事件,我们在注册FreeDrag时不要忘记注册DragComplete。那么我们如何判断Drag的方向呢?通过GestureSample的Delta.X的值的正负来判断。但是这个判断只能在FreeDrag注册的响应事件中进行判断,DragComplete中并不给出响应的方向信息。这个页面中对FreeDrag手势的使用其实是一个错误的方式,正确的方式请看下一个页面:
class SummerScreen : SAScreen { enum Direction { Left, Right }; SASimpleSprite backSprite; SAButton menuButton; Direction direction; public SummerScreen(ChangeScreenDelegate changeScreenDelegate) : base(changeScreenDelegate) { } public override void LoadContent() { backSprite = new SASimpleSprite("Images/Season", new Rectangle(480, 0, 480, 800), Vector2.Zero, Color.White); } public override void Init() { direction = Direction.Right; menuButton = new SAButton("Images/Resource", new Rectangle[] { new Rectangle(0, 340, 156, 44), new Rectangle(200, 340, 156, 44) }, new Vector2(143, 385), OnMenuButtonClick); } public override void SetupInput() { SAInput.EnableBackButton(OnBackButton); SAInput.EnableGesture(Microsoft.Xna.Framework.Input.Touch.GestureType.FreeDrag, OnFreeDrag); SAInput.EnableGesture(Microsoft.Xna.Framework.Input.Touch.GestureType.DragComplete, OnDragComplete); } private void OnBackButton() { ChangeScreenTo(ScreenType.SpringScreen); } private void OnFreeDrag(Microsoft.Xna.Framework.Input.Touch.GestureSample gestureSample) { if (gestureSample.Delta.X > 0) { direction = Direction.Right; } else if(gestureSample.Delta.X<0) { direction = Direction.Left; } } private void OnDragComplete(Microsoft.Xna.Framework.Input.Touch.GestureSample gestureSample) { if (direction == Direction.Right) { ChangeScreenTo(ScreenType.SpringScreen); } else { ChangeScreenTo(ScreenType.AutumnScreen); } } private void OnMenuButtonClick() { ChangeScreenTo(ScreenType.Menu); } public override void Draw(GameTime gameTime) { backSprite.Draw(); menuButton.Draw(); } }
关于这个页面,没啥好多说的了~
不过还是有要说的:春夏秋冬四个页面每个页面都差不多,这种Ctrl C+Ctrl V的方式来写页面也太逊了吧~
那么我们其实只需要修改一下相关的private为protected:(好吧已经到了秋页面了)
class AutumnScreen:SAScreen { protected enum Direction { Left, Right }; protected SASimpleSprite backSprite; protected SAButton menuButton; protected Direction direction; public AutumnScreen(ChangeScreenDelegate changeScreenDelegate) : base(changeScreenDelegate) { } public override void LoadContent() { backSprite = new SASimpleSprite("Images/Season", new Rectangle(960, 0, 480, 800), Vector2.Zero, Color.White); } public override void Init() { direction = Direction.Right; menuButton = new SAButton("Images/Resource", new Rectangle[] { new Rectangle(0, 340, 156, 44), new Rectangle(200, 340, 156, 44) }, new Vector2(143, 385), OnMenuButtonClick); } public override void SetupInput() { SAInput.EnableBackButton(OnBackButton); SAInput.EnableGesture(Microsoft.Xna.Framework.Input.Touch.GestureType.FreeDrag, OnFreeDrag); SAInput.EnableGesture(Microsoft.Xna.Framework.Input.Touch.GestureType.DragComplete, OnDragComplete); } protected virtual void OnBackButton() { ChangeScreenTo(ScreenType.SummerScreen); } protected void OnFreeDrag(Microsoft.Xna.Framework.Input.Touch.GestureSample gestureSample) { if (gestureSample.Delta.X > 0) { direction = Direction.Right; } else if(gestureSample.Delta.X<0) { direction = Direction.Left; } } protected virtual void OnDragComplete(Microsoft.Xna.Framework.Input.Touch.GestureSample gestureSample) { if (direction == Direction.Right) { ChangeScreenTo(ScreenType.SummerScreen); } else { ChangeScreenTo(ScreenType.WinterScreen); } } protected void OnMenuButtonClick() { ChangeScreenTo(ScreenType.Menu); } public override void Draw(GameTime gameTime) { backSprite.Draw(); menuButton.Draw(); } }
OK,最后一个页面继承自秋页面:
class WinterScreen:AutumnScreen { public WinterScreen(ChangeScreenDelegate changeScreenDelegate) : base(changeScreenDelegate) { } public override void LoadContent() { backSprite = new SASimpleSprite("Images/Season", new Rectangle(1440, 0, 480, 800), Vector2.Zero, Color.White); } protected override void OnBackButton() { ChangeScreenTo(ScreenType.AutumnScreen); } protected override void OnDragComplete(Microsoft.Xna.Framework.Input.Touch.GestureSample gestureSample) { if (direction == Direction.Right) { ChangeScreenTo(ScreenType.AutumnScreen); } } }
是不是通过继承,冬页面看起来就有点儿专业了?
最后一步,如果刚刚把您把“创建页面”模块替换掉了,那么现在再把它换回来:(在MyDirector中)
#region 创建界面 public SAScreen CreateMenuScreen() { return new MenuScreen(ChangeScreenTo); } public SAScreen CreateSpringScreen() { return new SpringScreen(ChangeScreenTo); } public SAScreen CreateSummerScreen() { return new SummerScreen(ChangeScreenTo); } public SAScreen CreateAutumnScreen() { return new AutumnScreen(ChangeScreenTo); } public SAScreen CreateWinterScreen() { return new WinterScreen(ChangeScreenTo); } #endregion
点击运行吧~
以上~
虽然效果都基本上有了,但是有一个地方很不美气:
就是ScreenType枚举定义的位置,我们可以在
位置见到:
//这样子似乎不美气,但是更美气的方法还在思考中 public enum ScreenType { Menu, SpringScreen, SummerScreen, AutumnScreen, WinterScreen };
这个枚举是在Samurai项目中定义的,也就是说平时我们写好页面后,还要过来手工地“注册”一下(显得好傻呀),希望大家有更好的解决方法告我一声~
遇见不明白的地方,欢迎提问;遇见我犯下的错误或者做的不好的地方,欢迎指正~
最后呢?我到11月中旬时间就比较宽裕了,在那时想再写两个系类的博客——“3天打造WP版《会说话的汤姆猫》”以及“7天打造WP版《Doodle Jump》”敬请关注~
相关文章推荐
- Android 游戏引擎libgdx 资源加载进度百分比显示案例分析
- 16大跨平台游戏引擎
- 16大跨平台游戏引擎
- [游戏引擎]ForstBite2(寒霜)游戏引擎SDK下载
- [手机资讯]Windows Phone 7首款机型8月25日开卖
- [游戏引擎]Cry Engine 3引擎开放下载
- 8款开源的Android 游戏引擎
- 顶级游戏设计大师谈如何成为一名游戏设计师!
- Windows Phone 7开发一月谈(8)
- 浅析国内某种端游服务器架构
- [源码分享]游戏引擎Gamebryo_LightSpeed_src_3.0
- 黑马程序员---面向对象
- 黑马程序员---异常处理异常
- 黑马程序员---委托和事件
- 黑马程序员---文件的读写
- 黑马程序员---集合
- 微软首次展示Windows 8:开始界面类似WP7(图)
- 8个Windows Phone开发工具
- CSDN博文精选:最受欢迎的系列专栏博客推荐