您的位置:首页 > 大数据 > 人工智能

打造轻量级Windows Phone7 游戏引擎-Samurai 第三话SADirector与SAScreen

2013-10-23 12:39 239 查看
打造轻量级Windows Phone7 游戏引擎-Samurai 第三话SADirector与SAScreen
在XNA编程框架中,一开始项目就自动帮我们生成了一个“Game1.cs”文件,Game1主要就是这个样子的:

public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
TargetElapsedTime = TimeSpan.FromTicks(333333);
InactiveSleepTime = TimeSpan.FromSeconds(1);
}
protected override void Initialize()
{
//TODO
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
//TODO
}
protected override void UnloadContent()
{
//TODO
}
protected override void Update(GameTime gameTime)
{
//TODO
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
//TODO
base.Draw(gameTime);
}
}


现在现在只有Game1.cs,而且Update与Draw的循环调用又得借助Game1的相应方法。那么,Game1是游戏的老大吗?或者应该问谁是整个游戏的老大呢?

  游戏是由一个个页面够成,管理这些页面的管理者应该就是了吧!但是做页面的管理者也不是那么容易的事,因为管理者没有实际的权利来Update和Draw,他也不得不给Game1低头,说“老大呀,麻烦你更新的时候通知我一声呗”,Game1很嚣张的说“以后要想跟我混,得多学着点儿,I Jump,You Jump”(呵呵)。没错,虽然Game1目前是绝对的大佬,但是只要有页面管理者在,所有的事情都交给他就行了。我们就叫页面管理者为SADirector吧(就叫她阿SA导演吧)

阿SA导演主要干两件事:

跟Game1混,You Jump,I Jump:

太简单了,就是You Update,I Update;You Draw ,I Draw;

管理页面的切换(页面类还没出场,大家稍等一下吧)

也不难,就是作为上下文(状态模式),交给Screen选择下一个Screen的权利。只有Game1.cs,而且Update与Draw的循环调用又得借助Game1的相应方法。那么,Game1是游戏的老大吗?或者应该问谁是整个游戏的老大呢?
游戏是由一个个页面够成,管理这些页面的管理者应该就是了吧!但是做页面的管理者也不是那么容易的事,因为管理者没有实际的权利来Update和Draw,他也不得不给Game1低头,说“老大呀,麻烦你更新的时候通知我一声呗”,Game1很嚣张的说“以后要想跟我混,得多学着点儿,I Jump,You Jump”(呵呵)。没错,虽然Game1目前是绝对的大佬,但是只要有页面管理者在,所有的事情都交给他就行了。我们就叫页面管理者为SADirector吧(就叫她阿SA导演吧)
阿SA导演主要干两件事:
1.跟Game1混,You Jump,I Jump:
太简单了,就是You Update,I Update;You Draw ,I Draw;
2.管理页面的切换(页面类还没出场,大家稍等一下吧)
也不难,就是作为上下文(状态模式),交给Screen选择下一个Screen的权利。
3.如果是一个好的导演的话,在用完一个页面后要想想,我是释放掉你好,还是先留下,再用的时候直接让你重新初始化一下呢(作为一个懒汉导演,我还是选择了后者的方法)

下面,我先贴出来You Jump,I Jump的代码:在Game1.cs中:

public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;

SADirector director;

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
TargetElapsedTime = TimeSpan.FromTicks(333333);
InactiveSleepTime = TimeSpan.FromSeconds(1);

//Config 全屏
graphics.IsFullScreen = true;
//Config 竖屏
SAGraphicUtil.SetVertical(graphics);
}

protected override void Initialize()
{
//TODO
base.Initialize();
}

protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);

//TODO DIRECTOR
director = new ScreenManager(this, graphics, spriteBatch);
}

protected override void UnloadContent()
{
//TODO DIRECTOR
director.UnloadContent();
}

protected override void Update(GameTime gameTime)
{
//TODO DIRECTOR
director.Update(gameTime);
base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
//TODO DIRECTOR
director.Draw(gameTime);
base.Draw(gameTime);
}

protected override void OnActivated(object sender, EventArgs args)
{
//TODO DIRCTOR
director.OnActivated();
base.OnActivated(sender, args);
}
}

在SADirector中:
#region 与Game的接口
public void Update(GameTime gameTime)
{
currentScreen.BaseUpdate(gameTime);
}

public void Draw(GameTime gameTime)
{
game.GraphicsDevice.Clear(Color.CadetBlue);
currentScreen.BaseDraw(gameTime);
}

public virtual void UnloadContent() { }

public virtual void OnActivated() { }
#endregion

关于Screen页面的切换(当然我们可以使用多线程加载资源以及Loading动画,但对于不是比较大的游戏,我比较习惯于一次性加载资源,所以并没有在页面切换时加入多线程和loading相关内容)不过未来这一部分还是打算做比较大的改动的,因为想做一些页面切换时候的3D效果,目前这一部分还没开工...回到正题,大家可以看看关于“状态模式”的描述,这里主要用的是状态模式。

直接贴上SADirector的所有代码吧:
public abstract class SADirector
{
//常用的引用
protected static Game game;
protected static GraphicsDeviceManager graphics;
protected static SpriteBatch spriteBatch;
protected static ContentManager content;

//单例
protected SAScreen currentScreen;

//缓存
protected Dictionary<ScreenType, SAScreen> screenDictionary;

public delegate SAScreen CreateScreen();

public SADirector(Game game, GraphicsDeviceManager graphics, SpriteBatch spriteBatch)
{
SADirector.game = game;
SADirector.content = game.Content;
SADirector.graphics = graphics;
SADirector.spriteBatch = spriteBatch;
screenDictionary = new Dictionary<ScreenType, SAScreen>();
//注册 Global
SAGlobal.Setup(game, spriteBatch, graphics);
//设置竖屏
SAGraphicUtil.SetVertical(graphics);
//初始化SAMusicManager
SAMusicManager.Setup();
//ATTENTION 加载第一个页面
//ChangeScreenTo(ScreenType.Loading);
}

#region 与Game的接口
public void Update(GameTime gameTime)
{
currentScreen.BaseUpdate(gameTime);
}

public void Draw(GameTime gameTime)
{
game.GraphicsDevice.Clear(Color.CadetBlue);
currentScreen.BaseDraw(gameTime);
}

public virtual void UnloadContent() { }

public virtual void OnActivated() { }
#endregion

#region 切换页面(SAScreen需要知道)
public virtual void ChangeScreenTo(ScreenType screenType){}

public void ChangeScreenTo(ScreenType screenType, CreateScreen createScreen)
{
if (currentScreen != null)
{
//重置
currentScreen.UnloadContent();
}
if (!screenDictionary.ContainsKey(screenType))
{
screenDictionary.Add(screenType, createScreen());
screenDictionary[screenType].BaseLoadContent();
currentScreen = screenDictionary[screenType];
}
else
{
currentScreen = screenDictionary[screenType];
//重新启动
currentScreen.ReInit();
}
}
#endregion

#region 创建界面(工厂模式)
#endregion
}

public enum ScreenType
{
Test,
Loading,
MainMenu,
Game
}

下面是一个SADirector的子类:
public class ScreenManager:SADirector
{
public ScreenManager(Game game,GraphicsDeviceManager graphics,SpriteBatch spriteBatch)
:base(game,graphics,spriteBatch)
{
ChangeScreenTo(ScreenType.Test);
}

public override void ChangeScreenTo(ScreenType screenType)
{
switch (screenType)
{
case ScreenType.Test:
ChangeScreenTo(screenType, CreateTestScreen);
break;
//...
}
}

public SAScreen CreateTestScreen()
{
return new MarioScreen(game, spriteBatch, graphics, ChangeScreenTo);
}
}

下面页面基类SAScreen登场:

同样的SAScreen还是抽象类,我们通过继承来使用。

代码简单易懂,直接Ctrl V吧:

不过现在看这段代码,觉得“依赖项,常用引用”这样写真的很烂!!!不如直接提取出来一个静态的Util在SADirector构造的时候初始化,全局共享即可。(这不就是前面博客中提到的SAGlobal,原谅我吧,当初写SAGlobal是为了消灭掉常用引用,现在发现竟然忘了改)
public class SAScreen
{
#region 依赖项 常用引用
protected static Game game {set;get;}
protected static GraphicsDeviceManager graphics { get; set; }
protected static ContentManager content { get; set; }
protected static SpriteBatch spriteBatch { get; set; }
#endregion
protected static Random random { get; set; }

public delegate void ChangeScreenDelegate(ScreenType screenType);
protected static ChangeScreenDelegate ChangeScreenTo;

public SAScreen() { }
public SAScreen(Game game, SpriteBatch spriteBatch, GraphicsDeviceManager graphics, ChangeScreenDelegate changeScreenDelegate)
{
SAScreen.game = game;
SAScreen.spriteBatch = spriteBatch;
SAScreen.content = game.Content;
SAScreen.graphics = graphics;
SAScreen.ChangeScreenTo = changeScreenDelegate;
random = new Random();
}

#region 与SADirector的接口 不可复写
public void BaseLoadContent()
{
LoadContent();
SetupInput();
Init();
}

public void BaseUpdate(GameTime gameTime)
{
SAInput.UpdateInput();
Update(gameTime);
}

public void BaseDraw(GameTime gameTime)
{
spriteBatch.Begin();
Draw(gameTime);
spriteBatch.End();
}

public void BaseUnloadContent()
{
UnloadContent();
}

public void ReInit()
{
Init();
SetupInput();
}
#endregion

#region 子类复写
public virtual void LoadContent()
{
//TODO
}

public virtual void Init()
{
//TODO
}

public virtual void Update(GameTime gameTime)
{
//TODO
}

//只管画,不管Begin和End
public virtual void Draw(GameTime gameTime)
{
//TODO
}

public virtual void UnloadContent()
{
//如果子类override就更好了
SAInput.ResetInput();
}

public virtual void SetupInput()
{
//TODO
}
#endregion
}
}

稍稍改了下:
public abstract class SAScreen
{
protected static Random random;
public delegate void ChangeScreenDelegate(ScreenType screenType);
protected static ChangeScreenDelegate ChangeScreenTo;
static SAScreen()
{
random = new Random();
}
public SAScreen() { }
public SAScreen(ChangeScreenDelegate changeScreenDelegate)
{
SAScreen.ChangeScreenTo = changeScreenDelegate;
}

#region 与SADirector的接口 不可复写
public void BaseLoadContent()
{
LoadContent();
SetupInput();
Init();
}

public void BaseUpdate(GameTime gameTime)
{
SAInput.UpdateInput();
Update(gameTime);
}

public void BaseDraw(GameTime gameTime)
{
SAGlobal.spriteBatch.Begin();
Draw(gameTime);
SAGlobal.spriteBatch.End();
}

public void BaseUnloadContent()
{
UnloadContent();
}

public void ReInit()
{
Init();
SetupInput();
}
#endregion

#region 子类复写
public virtual void LoadContent()
{
//TODO
}

public virtual void Init()
{
//TODO
}

public virtual void Update(GameTime gameTime)
{
//TODO
}

//只管画,不管Begin和End
public virtual void Draw(GameTime gameTime)
{
//TODO
}

public virtual void UnloadContent()
{
//如果子类override就更好了
SAInput.ResetInput();
}

public virtual void SetupInput()
{
//TODO
}
#endregion
}


细心的读者会发现,这里面已经有了SetupInput和UnloadContent里的ResetInput,也就是说我们在每一个单独页面都重新注册在该页面中需要检测的输入,而当页面被回收再利用时,会调用ReInit方法(当然了,加载的资源因为没有被释放,所以就不用再重新LoadContent了)。花费了这么多功夫,我们是不是应该写一个页面转换以及使用不同的手势检测的小例子了呢?

I am sorry to tell you that,等到写完Button相关的内容吧~敬请期待~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐