Unity3d实现有限状态机系统

来源:互联网 发布:java 方法名 编辑:程序博客网 时间:2024/06/01 08:24

原文地址:blog.liujunliang.com.cn

在之前有过介绍一个可视化有限状态机编辑器插件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();}}

原文地址:blog.liujunliang.com.cn