`
rcfalcon
  • 浏览: 221794 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

基于silverlight GDE-X开发进展 - 游戏引擎状态机

 
阅读更多

今天正式开始编写我们的游戏引擎GDE-X。

对于一个游戏来说,我们可以把其认为是一个状态机——实际上游戏中不同画面、场景等的变化,就是一个有限状态机状态间的相互切换。

大致可以理解如下:

while(not exit)

{

switch( state )

{

case state1:

do_state1();

case state2:

do_state2();

....

}

}

再抽一层,其实就是每个状态有一个“入状态”和“出状态”

那么我们设计上只需要实现一个调度机制,可以针对当前状态,调度运行状态具体内容,并且在运行完毕之后调度其他状态即可。

我们定义一个枚举型GameStatus,这里存放所有游戏中要使用的状态。(具体到游戏中可能是 主菜单、系统设置、游戏、游戏结束、读取进度……甚至是游戏的各个具体画面)

这里我们只给出两个用于测试的状态,empty和test

针对于每一个状态,我们提供一个“功能类”,就是该状态下游戏应该具体运行的逻辑代码。我们将其称为StateMachineClass(状态机类)

抽出其共性建立StateMachineBase类 和 StateMachine接口。特性如下:

StateMachineBase :

1. 需要拿到游戏引擎实例(本引擎中渲染句柄、资源等都存储在引擎实例中)

2. 需要有方法调度游戏引擎执行下一个状态(出状态)

IStateMachine:

1. 需要提供本状态的功能入口。(就是调度到该状态的时候具体运行哪些代码)

参考代码如下:

细心的你可能会觉得RunNextStatus这个函数的实现很奇怪,别急,之后会解释。

然后我们需要来实现整个游戏引擎状态机的调度机制。

首先,我们要将“状态”和“功能类”绑定起来。这里使用C#的Dictionary

那么我使用m_StateHash[状态名]就可以拿到功能类的实例,然后这里抽出功能类的接口Run方法,就能在调度中得到功能类的代码入口了。

我们得提供一个整个游戏状态机的启动入口

提供一个记录当前游戏状态的变量

最后,我们的重头戏,如何在一个状态结束后,调度运行下一个状态?

我原先的实现是在GameEngine里添加一个静态方法NextStatus,然后在功能类结束的时候去运行GameEngine.NextStatus(xxx)。——这样会有一个悖论。。 游戏引擎调度运行功能类,功能类里又阻塞的调用了游戏引擎的静态方法。。。周而复始,DEBUG一下打开堆栈查看栏。。果然,不出所料,之前所有运行过“功能类”的全部都没有释放内存!这样下去用不了多久就会内存崩掉的。

那么如何让我们的程序能在功能类里不阻塞的调用游戏引擎类方法实现状态机调度呢?

—— 这就是刚才那段奇怪的代码。

CompositionTarget 对象可以根据每个帧回调来创建自定义动画

我们在功能类中为CompositionTarget 注册一个位于GameEngine内的方法,用于调度状态机。

然后在GameEngine内如此实现:

太NICE了,在调度下一帧的时候,原来的堆栈已经释放,非阻塞的实现了GameEngine类方法的调用!

DEBUG,打开堆栈查看栏,非常爽的只有一层堆栈~

下面来使用我们的状态机!

编写两个功能类,其分别为主界面载入一张背景图片,并且等待两秒,跳到下一个状态。

这两个类需要继承自我们的功能类基类和接口类

看看结果 ——

背景切换相当成功。

堆栈记录相当赏心悦目。

内存使用情况十分稳定。

至此完成我们的游戏引擎状态机。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics