FSM有限状态机学习及Unity3D案例讲解

引言:近日忙于毕业论文,今天看到涨了3个粉丝,不甚惊喜。遂今日更新FSM有限状态机学习,希望大家共同进步!

开发版本:Unity 2017.1.1f1、VS 2017

适合人群:初学Unity者

一.有限状态机定义

有限状态机(英文:Finite State Machine,简称FSM),是指在不同阶段呈现不同的运行状态的系统,这些状态是有限的,不重叠的。这样的系统在某一时刻一定会处于其中所有状态中的一个状态,此时它接收一部分的输入,产生相应的响应,并且迁移到可能的状态。

    一般有限状态机有一下几种类型的动作:
    1.进入动作:进入当前状态时执行

    2.退出动作:退出当前状态时执行

    3.输入动作:在当前状态时执行
    4.转移动作:在进行特定切换状态时执行

    有限状态机提供了描述和控制应用逻辑的强大方法,可以应用于控制NPC行为,图形界面管理等方面。FSM可以使得各个状态之间相互独立,互不影响,避免了状态与状态之间的耦合度,具有规则简单,可读性和可验证性等优点。

 

二.整理思路

简单的有限状态机就是switch语句,但如果状态过多的情况下,switch会使得代码显得臃肿,可扩展性很低。

假设敌人有四种状态,Idle、Patrol、Chase、Attack,switch实现方法如下:

public enum States
{
    Idle,//空闲状态
    Patrol,//巡逻状态
    Chase,//追逐状态
    Attack//攻击状态
}

public class SimpleFSM : MonoBehaviour
{
    private States currentState = States.Idle;

    private void Update()
    {
        switch (currentState)
        {
            case States.Idle:
                //TODO 空闲状态动作
                break;
            case States.Patrol:
                //TODO 巡逻状态动作
                break;
            case States.Chase:
                //TODO 追逐状态动作
                break;
            case States.Attack:
                //TODO 攻击状态动作
                break;
        }
    }
}

接下来整理“高级的”有限状态机实现方法,FSM由状态类和管理类组成,先创建一个状态基类FSMState,用于实现状态类的基本方法,每添加一种状态,就作为它的子类。然后,用一个管理类FSMSystem把所有状态管理起来,方便使用状态机。FSM类图如下所示:

三.实现代码

1.定义枚举类型

public enum StateID
{
    NullStateID,
}
public enum Transition
{
    NullTransition
}

定义每一种状态的ID和状态之间的转换条件

2.FSMState基类

public abstract class FSMState
{
    protected StateID stateID;
    public StateID ID { get { return this.stateID; } }
    protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
    protected FSMSystem fsm;

    public FSMState(FSMSystem fsm)
    {
        this.fsm = fsm;
    }

    /// <summary>
    /// 添加转换条件
    /// </summary>
    /// <param name="trans">转换条件</param>
    /// <param name="stateID">转换的目标状态</param>
    public void AddTransition(Transition trans, StateID stateID)
    {
        if (trans == Transition.NullTransition)
        {
            Debug.LogError(trans + "为空,转换条件不允许为空");return;
        }
        if (stateID == StateID.NullStateID)
        {
            Debug.LogError(stateID + "为空,状态ID不允许为空"); return;
        }
        if (map.ContainsKey(trans))
        {
            Debug.LogError(trans + "已经存在,请查看该转换条件是否正确");
        }
        else
        {
            map.Add(trans, stateID);
        }
    }

    /// <summary>
    /// 删除转换条件
    /// </summary>
    /// <param name="trans">需删除的转换条件</param>
    public void DeleteTransition(Transition trans)
    {
        if (trans == Transition.NullTransition)
        {
            Debug.LogError(trans + "为空,转换条件不允许为空"); return;
        }
        if (map.ContainsKey(trans) == false)
        {
            Debug.LogError(trans + "不存在,请查看该转换条件是否正确"); 
        }
        else
        {
            map.Remove(trans);
        }
    }

    /// <summary>
    /// 通过转换条件,得到目标状态
    /// </summary>
    /// <param name="trans">转换条件</param>
    /// <returns>返回目标状态</returns>
    public StateID GetTargetStateID(Transition trans)  
    {
        if (map.ContainsKey(trans) == false)
        {
            Debug.LogError(trans + "不存在,请查看该转换条件是否正确");
            return StateID.NullStateID;
        }
        else
        {
            return map[trans];
        }
    }

    public virtual void DoBeforeEntering() { }//进入动作
    public virtual void DoAfterLeaving() { }//离开动作
    public abstract void Act();//输入动作
    public abstract void Reason();//转移动作
}

状态类主要存储转换条件和转换状态的字典,可以添加删除转换条件,根据条件返回对应的状态,并定义状态类的四种动作。

3.FSMSystem管理类

public class FSMSystem
{
    private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();
    private FSMState currentState;

    /// <summary>
    /// 更新当前状态行为
    /// </summary>
    public void UpdateFSM()
    {
        currentState.Act();
        currentState.Reason();
    }

    /// <summary>
    /// 添加状态
    /// </summary>
    /// <param name="state">需管理的状态</param>
    public void AddState(FSMState state)
    {
        if (state == null)
        {
            Debug.LogError(state + "为空"); return;
        }
        if (currentState == null)
        {
            currentState = state;
        }
        if (states.ContainsValue(state))
        {
            Debug.LogError(state + "已经存在");
        }
        else
        {
            states.Add(state.ID, state);
        }
    }

    /// <summary>
    /// 删除状态
    /// </summary>
    /// <param name="id">需要删除状态的ID</param>
    /// /// <returns>删除成功返回true,否则返回false</returns>
    public bool DeleteState(StateID id)
    {
        if (id == StateID.NullStateID)
        {
            Debug.LogError(id + "为空");
            return false;
        }
        if (states.ContainsKey(id) == false)
        {
            Debug.LogError(id + "不存在");
            return false;
        }
        else
        {
            states.Remove(id);
            return true;
        }
    }

    /// <summary>
    /// 执行转换
    /// </summary>
    /// <param name="trans">转换条件</param>
    public void PerformTransition(Transition trans)
    {
        if (trans == Transition.NullTransition)
        {
            Debug.LogError(trans + "为空");return;
        }
        StateID targetID = currentState.GetTargetStateID(trans);
        if (states.ContainsKey(targetID) == false)
        {
            Debug.LogError(targetID + "不存在");return;
        }
        FSMState targetState = states[targetID];
        currentState.DoAfterLeaving();
        targetState.DoBeforeEntering();
        currentState = targetState;
    }
}

 

FSMSystem用来管理各个状态,字典存储ID和对应的状态,可以添加删除状态,并执行状态转换。

四.案例讲解

假设敌人按照路线巡逻,当发现玩家的时候,开始追逐。但玩家跑出追击范围的时候,敌人继续回去巡逻。

项目的源文件在文末,有需要的童鞋可以自行下载。

 

敌人巡逻效果如下:

敌人发现玩家开始追逐,当玩家逃离后,返回继续巡逻,效果如下:

 

分析此时敌人状态有两种,巡逻和追逐,转换条件也对应有两种

public enum StateID
{
    NullStateID,
    PatrolState,
    ChaseState
}
public enum Transition
{
    NullTransition,
    FindPlayer,
    LosePlayer
}

 

创建两个子类PatrolState和ChaseState都继承自FSMState,分别用来实现巡逻和追逐的状态行为。

在Enemy类中,实例化FSMSystem对象,添加巡逻和追逐状态,还有之间的转换条件

public class Enemy : MonoBehaviour
{
    private FSMSystem fsm;

    private void Start()
    {
        fsm = new FSMSystem();
        FSMState patrolState = new PatrolState(fsm);
        FSMState chaseState = new ChaseState(fsm);
        fsm.AddState(patrolState);
        fsm.AddState(chaseState);
        patrolState.AddTransition(Transition.FindPlayer, StateID.ChaseState);
        chaseState.AddTransition(Transition.LosePlayer, StateID.PatrolState);
    }

    private void Update()
    {
        fsm.UpdateFSM();
    }
}

 

FSM有限状态机除了实现敌人的行为外,还可以用来管理UI界面的切换,使用方式基本相似。如果大神发现我分享的学习记录中有错误,还请不吝赐教。

案例百度云链接:链接:https://pan.baidu.com/s/1MVpKQ5U5eJE7zJTMXSMj3A  提取码:coh5 

觉得我分享的学习记录不错的,点赞关注我哦!

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值