状态模式 有限状态机

一、例子

参考大话设计模式-状态模式

abstract class State {
    public abstract function writeProgram($w);
}

//上午工作状态
class ForenoonState extends State {

    public function writeProgram($w) {
        if($w->hour < 12) {
            echo '当前时间:'.$w->hour.'点 上午工作,精神百倍。<br/>';
        } else {
            $w->setState(new NoonState());
            $w->writeProgram();
        }
    }

}

//中午工作状态
class NoonState extends State {
    public function writeProgram($w) {
        if($w->hour < 13) {
            echo '当前时间:'.$w->hour.'点 饿了,午饭;犯困,午休。<br/>';
        } else {
            $w->setState(new AfternoonState());
            $w->writeProgram();
        }
    }
}

//下午工作状态
class AfternoonState extends State {
    public function writeProgram($w) {
        if($w->hour < 17) {
            echo '当前时间:'.$w->hour.'点 下午工作状态还不错,继续努力。<br/>';
        } 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.'点 加班哦,疲劳至极。<br/>';
            } else {
                $w->setState(new SleepingState());
                $w->writeProgram();
            }
        }
    }
}

//睡眠状态
class SleepingState extends State {
    public function writeProgram($w) {
        echo '当前时间:'.$w->hour.'点 不行了,睡着了。<br/>';
    }
}

//下班休息状态
class RestState extends State {
    public function writeProgram($w) {
        echo '当前时间:'.$w->hour.'点下班回家了。<br/>';
    }
}

//工作
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页:

一个有限状态机是一个特殊的有向图,它包括一些状态(节点)和连接这些状态的有向弧。下图是一个识别中国地址的有限状态机的简单例子。


Paste_Image.png

每一个有限状态机都有一个开始状态和一个终止状态,以及若干中间状态,每一条弧上带有从一个状态进入下一个状态的条件。比如,在上图中,当前的状态是“省”,如果遇到一个词组和(区)县名有关,就进入状态“区县”;如果遇到的下一个词组和城市有关,那么我们就进入“市”的状态,如此等等。如果一条地址能从状态机的起始状态经过状态机的若干中间状态,走到终止状态,那么这条地址则有效,否则无效。比如说,“北京市双清路83号”对于上面的有限状态来讲有效,而“上海市辽宁省马家庄”则无效(因为无法从“市”走回到“省”)。

四、有限状态机在游戏中的应用

参考Unity 游戏框架搭建 (四) 简易有限状态机

一个跑酷的例子

主角从跑状态切换到跳状态,从跳状态切换到二段跳状态,这里的切换就是指状态的转移。状态的转移是有条件的,比如主角从跑状态不可以直接切换到二段跳状态。但是可以从二段跳状态切换到跑状态。
  另外,一个基本的状态有:进入状态、退出状态、接收输入、转移状态等动作。但是仅仅作为跑酷的角色的状态管理来说,只需要转移状态就足够了。有兴趣的同学可以自行扩展。

如何实现?

恰好之前看到过一个还算简易的实现(简易就是指我能看得懂- -,希望大家也是),原版是用lua实现的,我的跑酷游戏是用C#实现的,所以直接贴出C#代码。

using UnityEngine;  
using System.Collections;  
using System.Collections.Generic;  
public class FSM {  
    // 定义函数指针类型
    public delegate void FSMTranslationCallfunc();    /// <summary>
    /// 状态类
    /// </summary>
    public class FSMState
    {
        public string name;

        public FSMState(string name)
        {
            this.name = name;
        }
        /// <summary>
        /// 存储事件对应的条转
        /// </summary>
        public Dictionary <string,FSMTranslation> TranslationDict 
        = new Dictionary<string,FSMTranslation>();
    }
    /// <summary>
    /// 跳转类
    /// </summary>
    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 <string,FSMState> StateDict = new Dictionary<string,FSMState>();
    /// <summary>
    /// 添加状态
    /// </summary>
    /// <param name="state">State.</param>
    public void AddState(FSMState state)
    {
        StateDict [state.name] = state;
    }
    /// <summary>
    /// 添加条转
    /// </summary>
    /// <param name="translation">Translation.</param>
    public void AddTranslation(FSMTranslation translation)
    {
        StateDict [translation.fromState.name].TranslationDict [translation.name] = translation;
    }
    /// <summary>
    /// 启动状态机
    /// </summary>
    /// <param name="state">State.</param>
    public void Start(FSMState state)
    {
        mCurState = state;
    }
    /// <summary>
    /// 处理事件
    /// </summary>
    /// <param name="name">Name.</param>
    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);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,451评论 25 707
  • 1 场景问题# 1.1 申请聚餐费用## 来考虑这样一个功能:申请聚餐费用的管理。 很多公司都有这样的福利,就是项...
    七寸知架构阅读 3,093评论 3 58
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,901评论 1 15
  • 上篇文章浅谈了为什么写作的问题,这两天想想其原因应该不止于其三,还有更多更令人信服的原因值得去深究,没法一一罗列。...
    锋言锋语V阅读 323评论 1 0