首先知道简单状态机
这种方法中我们把状态和条件判断都集中在一个类里面,代码量冗多,不方便管理,很容易搞混
高级状态机则是通过把状态,状态引用,角色引用分离,是代码看起来十分清晰;
首先先定义一个抽象状态基类(FSMState),枚举切换状态的条件,状态若干;新建一个字典《看见什么条件,切换为什么状态》;添加字典是必须判断输入是否规范,增加程序的 健壮性。比如说条件是否为空,状态是否为空,条件是否已经存在。再定义一个类通过传条件获取当前的状态。接着定义虚方法进入状态之前和状态之后,定义抽象方法状态进行时和条件判断,最后引用一个状态机类(FSMSystem),在构造函数赋值;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum Transition
{
NullTransition=0,
SeePlayer,
LostPlayer,
}
public enum StateID
{
NullStateId=0,
Patrol,
Chase,
}
public abstract class FSMState{
protected StateID stateID;
public StateID ID { get { return stateID; } } //返回stateID
public Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
protected FSMSystem fsm; //引用的FSMSystem
public FSMState(FSMSystem fsm) //先调用构造函数更新引用的FSMSystem;
{
this.fsm = fsm;
}
public void AddTransition(Transition tran,StateID id) //添加转化条件(构造)
{
if (tran==Transition.NullTransition)
{
Debug.LogError("不允许条件为空");return;
}
if (id == StateID.NullStateId)
{
Debug.LogError("不允许状态为空"); return;
}
if (map.ContainsKey(tran))
{
Debug.LogError("条件已经存在"); return;
}
map.Add(tran,id);
}
public void DeleteTransition(Transition tran,StateID id)
{
if (tran == Transition.NullTransition)
{
Debug.LogError("不允许条件为空"); return;
}
if (map.ContainsKey(tran)==false)
{
Debug.LogError("条件不存在"); return;
}
map.Remove(tran);
}
public StateID GetState(Transition tran)
{
if (map.ContainsKey(tran))
{
return map[tran];
}
return StateID.NullStateId;
}
public virtual void BeforeEnter() { }
public virtual void AfterExit() { }
public abstract void Act(GameObject npc); //进入状态之时(抽象类每个子类不同必须重写)
public abstract void Reason(GameObject npc); //判断转换条件,条件机
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
定义一个状态机类(FSMSystem),为不用遍历采用字典《状态,状态抽象类》,变量有,当前状态和当前 抽象状态基类
函数一:通过传抽象状态基类为字典赋值
函数二:执行条件(如果条件没报错的话),赋值当前状态和当前 抽象状态基类。
函数三:传值gameobject给update;时刻调用状态进行时和条件判断
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FSMSystem { //状态机
private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>(); //状态,条件状态
private StateID currentStateID; //当前状态
private FSMState currentState; //当前状态和条件
public void AddState(FSMState s)
{
if (s==null)
{
Debug.LogError("FSM不能为空");return;
}
if (currentState ==null)
{
currentState = s;
currentStateID = s.ID;
}
if (states.ContainsKey(s.ID))
{
Debug.LogError("状态存在"+s.ID); return;
}
states.Add(s.ID,s);
}
public void DeleState(StateID id)
{
if (id==StateID.NullStateId)
{
Debug.LogError("无法删除空状态");return;
}
if (states.ContainsKey(id)==false)
{
Debug.LogError("无法删除不存在的状态"+id);
}
states.Remove(id);
}
public void PerformTransition(Transition tran) //传进来一个条件,执行条件
{
if (tran==Transition.NullTransition)
{
Debug.LogError("无法执行空的转换条件");return;
}
StateID id= currentState.GetState(tran); //通过条件获取ID
if (id==StateID.NullStateId)
{
Debug.LogError("该条件没有对应的状态");return;
}
if (states.ContainsKey(id)==false) //当前状态机字典里是否有这个状态
{
Debug.LogError("状态机不存在这个状态"); return;
}
FSMState state = states[id]; //把状态和条件赋值给 局部变量
currentState.AfterExit(); //播放状态离开部分
currentState = state; //赋值给当前条件和状态
currentStateID = id; //赋值给当前状态
currentState.BeforeEnter(); //播放状态进入之前
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
public void Update (GameObject npc) {
currentState.Act(npc); //调用当前状态的ACT
currentState.Reason(npc);
}
}
定义巡逻类,追寻类;重写构造方法,状态进行时和条件判断
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PatrolState : FSMState{
public List<Transform> path=new List<Transform>() ;
public int index = 0;
public Transform player;
public PatrolState(FSMSystem fsm) : base(fsm) //那么子类也要传一个FSM用于更新父类fsm
{
player = GameObject.Find("Play").transform;
stateID = StateID.Patrol; //继承并修改父类参数
Transform Path1 = GameObject.Find("Path").transform;
Transform[] children = Path1.GetComponentsInChildren<Transform>(); //这个位置组包含Path的位置
foreach (Transform child in children)
{
if (child!=Path1)
{
path.Add(child);
}
}
}
public override void Act(GameObject npc) //覆盖重写
{
npc.transform.LookAt(path[index].position);
npc.transform.Translate(Vector3.forward*Time.deltaTime*3);
if (Vector3.Distance(npc.transform.position,path[index].position)<1)
{
index++;
index %= path.Count;
}
}
public override void Reason(GameObject npc)
{
if (Vector3.Distance(player.position,npc.transform.position)<3)
{
fsm.PerformTransition(Transition.SeePlayer);
}
//throw new NotImplementedException();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chase : FSMState {
private Transform player;
public Chase(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Chase;
player = GameObject.Find("Play").transform;
}
public override void Act(GameObject npc)
{
npc.transform.LookAt(player.position);
npc.transform.Translate(Vector3.forward*Time.deltaTime*2);
}
public override void Reason(GameObject npc)
{
if (Vector3.Distance(player.position, npc.transform.position) > 6)
{
fsm.PerformTransition(Transition.LostPlayer);
}
//throw new NotImplementedException();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
定义敌人类
- new一个状态机,初始化,新建一个状态机,见一个巡逻类,赋值状态机给构造函数往字典《看见什么条件,切换为什么状态》加“看见敌人条件”,“追寻状态”。调用状态机添加巡逻状态的函数。
- 新建一个状态机,见一个追寻类,赋值状态机给构造函数往字典《看见什么条件,切换为什么状态》加“丢失敌人条件”,“巡逻状态”。调用状态机添加追寻状态的函数。
- update给状态机update添加this.gameobject'
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class enemy : MonoBehaviour {
public FSMSystem fsm;
// Use this for initialization
void Start () {
InitFSM();
}
void InitFSM()
{
fsm = new FSMSystem();
FSMState patrolState = new PatrolState(fsm);
patrolState.AddTransition(Transition.SeePlayer,StateID.Chase);
fsm.AddState(patrolState);
FSMState ChaseState = new Chase(fsm);
ChaseState.AddTransition(Transition.LostPlayer, StateID.Patrol);
fsm.AddState(ChaseState);
}
// Update is called once per frame
void Update () {
fsm.Update(this.gameObject);
}
}