Unity3d实现有限状态机系统

发表于2018-03-25
评论0 1.1k浏览
在前面给大家介绍了可视化有限状态机编辑器插件PlayerMaker的使用,这篇文章主要是教大家如何在代码中实现一个状态机系统,想知道方法的可以看看。

首先创建一个脚本,来管理我们的各个状态
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using System.Linq;  
/// <summary>  
/// 状态ID  
/// </summary>  
public enum FSMStateID  
{  
    NullFSMStateID,  
    PatrolFSMStateID,//巡逻状态  
    ChaseFSMStateID,//追逐状态  
}  
/// <summary>  
/// 状态转化条件  
/// </summary>  
public enum FSMTransition  
{  
    SeePlayer,//看到主角(目标)  
    LeavePlayer,//远离敌人(目标)  
}  
public class FSMSystem  
{  
    private FSMStateID mCurrentStateID;  
    private FSMBaseState mCurrentState;  
    private Dictionary<FSMStateID, FSMBaseState> mFSMStateDic = new Dictionary<FSMStateID, FSMBaseState>();  
    public void AddFSMSate(FSMBaseState state)  
    {  
        if (state == null)  
        {  
            Debug.Log("角色状态为空,无法添加");  
            return;  
        }  
        if (mCurrentState == null)  
        {  
            //第一个添加的状态被作为系统首个运行的状态  
            mCurrentStateID = state.mStateID;  
            mCurrentState = state;  
            mCurrentState.StateStart();  
        }  
        if (mFSMStateDic.ContainsValue(state))  
        {  
            Debug.Log("容器内存在该状态");  
            return;  
        }  
        mFSMStateDic.Add(state.mStateID, state);  
    }  
    public void DeleteFSMSate(FSMBaseState state)  
    {  
        if (state == null)  
        {  
            Debug.Log("角色状态为空,无法添加");  
            return;  
        }  
        if (!mFSMStateDic.ContainsValue(state))  
        {  
            Debug.Log("容器内不存在该状态");  
            return;  
        }  
        mFSMStateDic.Remove(state.mStateID);  
    }  
    //更新(执行)系统  
    public void UpdateSystem()  
    {  
        if (mCurrentState != null)  
        {  
            mCurrentState.StateUpdate();  
            mCurrentState.TransitionReason();  
        }  
    }  
    //转换状态  
    public void TransitionFSMState(FSMTransition transition)  
    {  
        FSMStateID stateID = mCurrentState.GetStateIdByTransition(transition);  
        if (stateID != FSMStateID.NullFSMStateID)  
        {  
            mCurrentStateID = stateID;  
            mCurrentState.StateEnd();  
            //换状态  
            mCurrentState = mFSMStateDic.FirstOrDefault(q => q.Key == stateID).Value;  
            mCurrentState.StateStart();  
        }  
    }  
}  

各个状态(巡逻状态、追逐状态)抽象理解为一个对象
创建一个状态基类,各个状态子类中可以继承重写这个基类方法
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using System.Linq;  
public abstract class FSMBaseState  
{  
    public FSMStateID mStateID { get; set; }    //状态ID  
    public FSMSystem mFSMSystem { get; set; }   //该对象属于在哪个状态机  
    public Dictionary<FSMTransition, FSMStateID> mFSMStateIdDic = new Dictionary<FSMTransition, FSMStateID>();  
    public FSMBaseState(FSMSystem fsmSystem, FSMStateID stateID)  
    {  
        this.mFSMSystem = fsmSystem;  
        this.mStateID = stateID;  
    }  
    public void AddTransition(FSMTransition transition, FSMStateID stateID)  
    {  
        if (mFSMStateIdDic.ContainsKey(transition))  
        {  
            Debug.Log("本状态已经包含了该转换条件");  
            return;  
        }  
        mFSMStateIdDic.Add(transition, stateID);  
    }  
    public void DeleteTransition(FSMTransition transition)  
    {  
        if (!mFSMStateIdDic.ContainsKey(transition))  
        {  
            Debug.Log("容器中没有该转换条件");  
            return;  
        }  
        mFSMStateIdDic.Remove(transition);  
    }  
    public FSMStateID GetStateIdByTransition(FSMTransition transition)  
    {  
        if (!mFSMStateIdDic.ContainsKey(transition))  
        {  
            Debug.Log("容器内没有该转换条件,无法获取状态");  
            return FSMStateID.NullFSMStateID;  
        }  
        return mFSMStateIdDic.FirstOrDefault(q => q.Key == transition).Value;  
    }  
    public abstract void StateStart();  
    public abstract void StateUpdate();  
    public abstract void StateEnd();  
    //转化状态条件  
    public abstract void TransitionReason();  
}  

以下是巡逻状态
using System;  
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
public class FSMPatrolState : FSMBaseState  
{  
    //路径点  
    private List<Transform> mStargetPointTransform = new List<Transform>();  
    //路径点索引  
    private int mPointIndex = 0;  
    //士兵  
    private GameObject mSliderObj { get; set; }  
    //主角  
    private GameObject mPlayerObj { get; set; }  
    //士兵移动速度  
    private float mMoveSpeed = 4f;  
    public FSMPatrolState(FSMSystem fsmSystem) : base(fsmSystem, FSMStateID.PatrolFSMStateID) { }  
    public override void StateStart()  
    {  
        //获取路径点  
        Transform[] transforms = GameObject.Find("Points").GetComponentsInChildren<Transform>();  
        foreach (var m_transform in transforms)  
        {  
            if (m_transform != GameObject.Find("Points").transform)  
            {  
                mStargetPointTransform.Add(m_transform);  
                Debug.Log(m_transform.position);  
            }  
        }  
        //获取士兵对象  
        mSliderObj = GameObject.Find("Slider");  
        //获取主角对象  
        mPlayerObj = GameObject.Find("Player");  
    }  
    public override void StateUpdate()  
    {  
        //确实目标点并移动    
        mSliderObj.transform.LookAt(this.mStargetPointTransform[this.mPointIndex].position);  
        mSliderObj.transform.Translate(Vector3.forward * Time.deltaTime * mMoveSpeed);  
        if (Vector3.Distance(mSliderObj.transform.position, this.mStargetPointTransform[this.mPointIndex].position) < 0.5f)  
        {  
            //切换目标点  
            this.mPointIndex++;  
            if (this.mPointIndex >= this.mStargetPointTransform.Count)  
            {  
                this.mPointIndex = 0;  
            }      
        }  
    }  
    public override void StateEnd()  
    {  
    }  
    public override void TransitionReason()  
    {  
        if (Vector3.Distance(mSliderObj.transform.position, mPlayerObj.transform.position) <= 2.0f)  
        {  
            //转化状态  
            if (this.mFSMSystem == null)  
            {  
                Debug.Log("目标状态机为空");  
                return;  
            }  
            mFSMSystem.TransitionFSMState(FSMTransition.SeePlayer);  
        }  
    }  
}  

以下是追逐状态
using System;  
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
public class FSMChaseState : FSMBaseState  
{  
    private GameObject mPlayerObj { get; set; }  
    private GameObject mSliderObj { get; set; }  
    private float mSliderMoveSpeed = 6.0f;  
    public FSMChaseState(FSMSystem fsmSystem) : base(fsmSystem, FSMStateID.ChaseFSMStateID) { }  
    public override void StateStart()  
    {  
        mPlayerObj = GameObject.Find("Player");  
        mSliderObj = GameObject.Find("Slider");  
    }  
    public override void StateUpdate()  
    {  
        if (Vector3.Distance(mPlayerObj.transform.position, mSliderObj.transform.position) <= 10.0f)  
        {  
            //开始面向主角  
            mSliderObj.transform.LookAt(mPlayerObj.transform.position);  
            //开始追逐  
            mSliderObj.transform.Translate(Vector3.forward * Time.deltaTime * mSliderMoveSpeed);  
        }  
    }  
    public override void StateEnd()  
    {  
    }  
    public override void TransitionReason()  
    {  
        //当主角远离敌人  
        if (Vector3.Distance(mPlayerObj.transform.position, mSliderObj.transform.position) > 10.0f)  
        {  
            //转化状态  
            if (this.mFSMSystem == null)  
            {  
                Debug.Log("目标状态机为空");  
                return;  
            }  
            mFSMSystem.TransitionFSMState(FSMTransition.LeavePlayer);  
        }  
    }  
}  

该状态机的优点在于当有不同类型的状态时候,可以直接添加到状态系统内,而不要需要状态系统内部的运行逻辑
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
public class Slider : MonoBehaviour  
{  
    private FSMSystem fsmSystem { get; set; }  
    void Start ()  
    {  
        fsmSystem = new FSMSystem();  
        //巡逻状态,在构造参数传一个系统参数,确定该状态是在哪个状态系统中管理的,状态转换的时候调用  
        FSMBaseState patrolState = new FSMPatrolState(fsmSystem);  
        patrolState.AddTransition(FSMTransition.SeePlayer, FSMStateID.ChaseFSMStateID);//巡逻状态转化条件  
        //追逐状态  
        FSMBaseState chaseState = new FSMChaseState(fsmSystem);  
        chaseState.AddTransition(FSMTransition.LeavePlayer, FSMStateID.PatrolFSMStateID);  
        fsmSystem.AddFSMSate(patrolState);  
        fsmSystem.AddFSMSate(chaseState);  
    }  
    void Update ()  
    {  
        fsmSystem.UpdateSystem();     
    }  
}  

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引