Unity3d实现有限状态机系统

扈韬
2023-12-01

原文地址: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



 类似资料: