文/毛星云
游戏开发过程中,各种游戏状态的切换无处不在。但很多时候,简单粗暴的if else加标志位的方式并不能很地道地解决状态复杂变换的问题,这时,就可以运用到状态模式以及状态机来高效地完成任务。状态模式与状态机,因为他们关联紧密,常常放在一起讨论和运用。而本文将对他们在游戏开发中的使用,进行一些探讨。
PS:
- 这篇文章起源于《Game Programming Patterns》第二章第六节。
- 这是一篇略长的文章,约5200余字,将分析游戏开发过程中状态模式与有限状态机的运用,已经非常了解相关内容的高端选手请略读。
- 文中使用C++承载讲解内容,文章末尾也提供了UnityC#版本的代码实现。
一、文章的短版本与思维导图
还是国际惯例,先放出这篇文章的短版本——所涉及知识点的一张思维导图,再开始正文。大家若是疲于阅读文章正文,直接看这张图,也是可以Get到本文的主要知识点的大概。
二、引例
假如我们现在正在开发一款横版游戏。当前的任务是实现玩家用按键操纵女英雄。当按下向上方向键的时候,女英雄应该跳跃。那么我们可以这样实现:
- void Heroine::handleInput(Input input)
- {
- if (input == PRESS_UP)
- {
- yVelocity_ = JUMP_VELOCITY;
- setGraphics(IMAGE_JUMP);
- }
- }
OK,我们成功创建了一个有限状态机(Finite-state machine,FSM)。它来自计算机科学的分支自动理论,那里有很多著名的数据结构,包括著名的图灵机。状态机是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。有限状态机是其中最简单的成员。(本文限于篇幅,更多状态机暂不讨论,在文章末尾进阶阅读中,列举了分层状态机Hierarchical State Machines与下推自动机Push down Automata的参考资料,有需要的朋友们可以阅读)
有限状态机FSM的要点是:
- 拥有一组状态,并且可以在这组状态之间进行切换。在我们的例子中,是站立,跳跃,蹲下和跳斩。
- 状态机同时只能在一个状态。英雄不可能同时处于跳跃和站立。事实上,防止这点是使用FSM的理由之一。
- 一连串的输入或事件被发送给机器。在我们的例子中,就是按键按下和松开。
- 每个状态都有一系列的转换,转换与输入和另一状态相关。当输入进来,如果它与当前状态的某个转换匹配,机器转为转换所指的状态。
举个例子,在站立状态时,按下下键转换为俯卧状态。在跳跃时按下下键转换为跳斩。如果输入在当前状态没有定义转换,输入就被忽视。
目前而言,游戏编程中状态机的实现方式,有两种可以选择:
- 用枚举配合switch case语句。
- 用多态与虚函数(也就是状态模式)。
下面让我们用代码来实现。不妨先从简单的方式开始,用枚举与switch case语句实现。
四、用枚举配合switch case实现状态机
我们知道,上文中实现的女英雄类Heroine有一些布尔类型的成员变量:isJumping_和isDucking,但是这两个变量永远不可能同时为True。
OK,这边可以提供一个小经验:当你有一系列的标记成员变量,而它们只能有且仅有一个为True时,定义成枚举(enum)其实更加适合。
在这个例子当中,我们的FSM的每一个状态可以用一个枚举来表示,所以,让我们定义以下枚举:- enum State
- {
- STATE_STANDING,
- STATE_JUMPING,
- STATE_DUCKING,
- STATE_DIVING
- };
- STATE_DIVING
- STATE_DUCKING,
- STATE_JUMPING,
- STATE_STANDING,
- {
上文引例中的示例,也就是女英雄在站立,跳跃,俯卧,下斩几个状态之间切换的问题,在example4中进行了Unity版本的实现。【链接】
运行场景,按键盘上的上下方向键,可以在console窗口中看到对应的状态,有点游戏发展史早期文字冒险游戏的感觉。运行截图:
由于篇幅原因,代码就不贴在这边了。具体可以到我的Github的repo Unity-Design-Pattern”中看到。这个repo目前已经在Unity中实现了《设计模式:可复用面向对象软件的基础》一书中提出的23种设计模式。且每种模式都包含对应的结构实现、应用示例以及图示介绍。有感兴趣的朋友可以关注一下。【链接】
八、本文知识点总结
本文涉及知识点总结如下:
1. 在游戏开发过程中,涉及到复杂的状态切换时,可以运用状态模式以及状态机来高效地完成任务。
2. 有限状态机的实现方式,有两种可以选择:
- 用枚举配合switch case语句。
- 用多态与虚函数(即状态模式)。
3. 状态模式的经典定义:允许对象在当内部状态改变时改变其行为,就好像此对象改变了自己的类一样。
4. 对状态模式的理解:状态模式用来解决当控制一个对象状态转换的条件表达式过于复杂的情况,它把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。
5. 状态模式的实现分为三个要点:
- 为状态定义一个接口。
- 为每个状态定义一个类。
- 恰当地进行状态委托。
6. 通常来说,状态模式中状态对象的存放有两种实现存放的思路:
- 静态状态。初始化时把所有可能的状态都new好,状态切换时通过赋值改变当前的状态。
- 实例化状态。每次切换状态时动态new出新的状态。
九、参考文献与进阶阅读
[1] http://gameprogrammingpatterns.com/state.html
[2] Gamma E. Design patterns: elements of reusableobject-oriented software[M]. Pearson Education India, 1995.
[3] https://www.youtube.com/watch?v= ... 06175C7E07index=20
[4] UML state machine :https://en.wikipedia.org/wiki/UML_state_machine
[5] Hierarchical State Machines分层状态机:
http://www.eventhelix.com/Realti ... ne.htm#.WAHM3Y996Uk
[6] Pushdown Automata下推自动机:https://en.wikipedia.org/wiki/Pushdown_automaton
相关阅读:【游戏设计模式】之二 论撤消重做、回放系统的优雅实现锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛
- 为每个状态定义一个类。
- 一连串的输入或事件被发送给机器。在我们的例子中,就是按键按下和松开。
- 状态机同时只能在一个状态。英雄不可能同时处于跳跃和站立。事实上,防止这点是使用FSM的理由之一。
- 还没有人评论,欢迎说说您的想法!