一、例子
参考大话设计模式-状态模式
abstract class State {
public abstract function writeProgram($w);
}
//上午工作状态
class ForenoonState extends State {
public function writeProgram($w) {
if($w->hour < 12) {
echo '当前时间:'.$w->hour.'点 上午工作,精神百倍。
';
} else {
$w->setState(new NoonState());
$w->writeProgram();
}
}
}
//中午工作状态
class NoonState extends State {
public function writeProgram($w) {
if($w->hour < 13) {
echo '当前时间:'.$w->hour.'点 饿了,午饭;犯困,午休。
';
} else {
$w->setState(new AfternoonState());
$w->writeProgram();
}
}
}
//下午工作状态
class AfternoonState extends State {
public function writeProgram($w) {
if($w->hour < 17) {
echo '当前时间:'.$w->hour.'点 下午工作状态还不错,继续努力。
';
} else {
$w->setState(new EveningState());
$w->writeProgram();
}
}
}
//晚间工作状态
class EveningState extends State {
public function writeProgram($w) {
if($w->taskFinished) {
$w->setState(new RestState());
$w->writeProgram();
} else {
if($w->hour < 21) {
echo '当前时间:'.$w->hour.'点 加班哦,疲劳至极。
';
} else {
$w->setState(new SleepingState());
$w->writeProgram();
}
}
}
}
//睡眠状态
class SleepingState extends State {
public function writeProgram($w) {
echo '当前时间:'.$w->hour.'点 不行了,睡着了。
';
}
}
//下班休息状态
class RestState extends State {
public function writeProgram($w) {
echo '当前时间:'.$w->hour.'点下班回家了。
';
}
}
//工作
class Work {
private $current;
public function __set($key, $value) {
$this->$key = $value;
}
public function __get($key) {
if(isset($this->$key)) {
return $this->$key;
} else {
return NULL;
}
}
public function __construct() {
$this->current = new ForenoonState();
}
private $hour;
private $TaskFinished = false;
public function setState($s) {
$this->current = $s;
}
public function writeProgram() {
$this->current->writeProgram($this);
}
}
$emergencyProjects = new Work();
$emergencyProjects->hour = 9;
$emergencyProjects->writeProgram();
$emergencyProjects->hour = 10;
$emergencyProjects->writeProgram();
$emergencyProjects->hour = 12;
$emergencyProjects->writeProgram();
$emergencyProjects->hour = 13;
$emergencyProjects->writeProgram();
$emergencyProjects->hour = 14;
$emergencyProjects->writeProgram();
$emergencyProjects->hour = 17;
$emergencyProjects->writeProgram();
$emergencyProjects->taskFinished = false;
$emergencyProjects->hour = 19;
$emergencyProjects->writeProgram();
$emergencyProjects->hour = 22;
$emergencyProjects->writeProgram();
二、状态模式与职责链模式区别
参考职责链模式VS状态模式
回忆一下职责链模式:
/*
* 先来一个程序猿 这里给他一个三万以内的随机值表示需要申请的差旅费
*/
ProgramApe ape = new ProgramApe((int) (Math.random() * 30000));
/*
* 再来四个老大
*/
Leader leader = new GroupLeader();
Leader director = new Director();
Leader manager = new Manager();
Leader boss = new Boss();
/*
* 设置老大的上一个老大
*/
leader.setLeader(director);
director.setLeader(manager);
manager.setLeader(boss);
// 处理申请
leader.handleRequest(ape);
}
职责链模式与状态模式的最大的不同是设置自己的下一级的问题上,状态模式是在类的设计阶段就定好的,不能在客户端改变,而职责链的下一级是在客户端自己来确定的。这样各有什么优缺点呢?
在类的设计阶段设定(状态模式)的好处是不用客户来确定下一状态,也就减少了客户设置错误的问题,客户也不用知道状态的具体结构,同时存在灵活性差,耦合度高的问题,从上面的例子可以看到,因为作用域的问题,ConStateC一定要写在ConStateB之前,ConStateB一定要写在ConStateA之前,顺序不能乱。而在客户端设定(职责链模式)要求客户对各个类的职责要有所了解,并能正确设置好职责链,并加大设置出错的风险。但是它也比较灵活,也不存在刚才在状态模式中说的耦合和作用域问题。
三、有限状态机
参考讲解“有限状态机”最简单最易懂的例子,来自《数学之美》
以下内容摘自吴军的《数学之美》第113页:
一个有限状态机是一个特殊的有向图,它包括一些状态(节点)和连接这些状态的有向弧。下图是一个识别中国地址的有限状态机的简单例子。
每一个有限状态机都有一个开始状态和一个终止状态,以及若干中间状态,每一条弧上带有从一个状态进入下一个状态的条件。比如,在上图中,当前的状态是“省”,如果遇到一个词组和(区)县名有关,就进入状态“区县”;如果遇到的下一个词组和城市有关,那么我们就进入“市”的状态,如此等等。如果一条地址能从状态机的起始状态经过状态机的若干中间状态,走到终止状态,那么这条地址则有效,否则无效。比如说,“北京市双清路83号”对于上面的有限状态来讲有效,而“上海市辽宁省马家庄”则无效(因为无法从“市”走回到“省”)。
四、有限状态机在游戏中的应用
参考Unity 游戏框架搭建 (四) 简易有限状态机
主角从跑状态切换到跳状态,从跳状态切换到二段跳状态,这里的切换就是指状态的转移。状态的转移是有条件的,比如主角从跑状态不可以直接切换到二段跳状态。但是可以从二段跳状态切换到跑状态。
另外,一个基本的状态有:进入状态、退出状态、接收输入、转移状态等动作。但是仅仅作为跑酷的角色的状态管理来说,只需要转移状态就足够了。有兴趣的同学可以自行扩展。
如何实现?
恰好之前看到过一个还算简易的实现(简易就是指我能看得懂- -,希望大家也是),原版是用lua实现的,我的跑酷游戏是用C#实现的,所以直接贴出C#代码。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FSM {
// 定义函数指针类型
public delegate void FSMTranslationCallfunc(); ///
/// 状态类
///
public class FSMState
{
public string name;
public FSMState(string name)
{
this.name = name;
}
///
/// 存储事件对应的条转
///
public Dictionary TranslationDict
= new Dictionary();
}
///
/// 跳转类
///
public class FSMTranslation
{
public FSMState fromState;
public string name;
public FSMState toState;
public FSMTranslationCallfunc callfunc; // 回调函数
public FSMTranslation(FSMState fromState,string name,
FSMState toState,FSMTranslationCallfunc callfunc)
{
this.fromState = fromState;
this.toState = toState;
this.name = name;
this.callfunc = callfunc;
}
}
// 当前状态
private FSMState mCurState;
Dictionary StateDict = new Dictionary();
///
/// 添加状态
///
/// State.
public void AddState(FSMState state)
{
StateDict [state.name] = state;
}
///
/// 添加条转
///
/// Translation.
public void AddTranslation(FSMTranslation translation)
{
StateDict [translation.fromState.name].TranslationDict [translation.name] = translation;
}
///
/// 启动状态机
///
/// State.
public void Start(FSMState state)
{
mCurState = state;
}
///
/// 处理事件
///
/// Name.
public void HandleEvent(string name)
{
if (mCurState != null && mCurState.TranslationDict.ContainsKey(name)) {
Debug.LogWarning ("fromState:" + mCurState.name);
mCurState.TranslationDict [name].callfunc ();
mCurState = mCurState.TranslationDict [name].toState;
Debug.LogWarning ("toState:" + mCurState.name);
}
}
}
// Idle, 闲置
// Run, 跑
// Jump, 一段跳
// DoubleJump, 二段跳
// Die, 挂彩
// 创建状态
FSM.FSMState idleState = new FSM.FSMState("idle");
FSM.FSMState runState = new FSM.FSMState("run");
FSM.FSMState jumpState = new FSM.FSMState("jump");
FSM.FSMState doubleJumpState = new FSM.FSMState("double_jump");
FSM.FSMState dieState = new FSM.FSMState("die");
// 创建跳转
FSM.FSMTranslation touchTranslation1 = new
FSM.FSMTranslation(runState,"touch_down",jumpState,Jump);
FSM.FSMTranslation touchTranslation2 = new
FSM.FSMTranslation(jumpState,"touch_down",doubleJumpState,DoubleJump);
FSM.FSMTranslation landTranslation1 = new FSM.FSMTranslation(jumpState,"land",runState,Run);
FSM.FSMTranslation landTranslation2 = new FSM.FSMTranslation(doubleJumpState,"land",runState,Run);
// 添加状态
PlayerModel.Instance ().fsm.AddState (idleState);
PlayerModel.Instance ().fsm.AddState (runState);
PlayerModel.Instance ().fsm.AddState (jumpState);
PlayerModel.Instance ().fsm.AddState (doubleJumpState);
PlayerModel.Instance ().fsm.AddState (dieState);
// 添加跳转
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation2);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation2);
PlayerModel.Instance ().fsm.Start (runState);