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