此次的有限状态机是设置已知的条件,根据条件转换状态
状态条件转换图
1、设计战士状态转换条件枚举以及对应的战士状态
通过上面关系图可以得到以下几个战士状态转换条件
///
/// 战士转换条件
///
public enum SoldierTransition
{
NullTransition,
SeeEnemy,
NoEnemy,
CanAttack
}
对应的战士状态
///
/// 战士状态ID
///
public enum SoldierStateID
{
None,
Idle,
Chase,
Attack
}
2、设计战士状态及系统类
///
/// 战士状态机系统类
///
public class SoldierFSMSystem
{
}
3、设计战士状态机基类
///
/// 战士状态基类
///
public abstract class ISoldierState
{
///
/// 转换条件与状态ID映射
///
protected Dictionary mTransitionAndStateIdDic = new Dictionary();
protected SoldierStateID mStateId;
///
/// 当前状态ID
///
public SoldierStateID StateId
{
get { return mStateId; }
}
public ISoldierState(SoldierFSMSystem fsm, ICharacter character)
{
mFSM = fsm;
mCharacter = character;
}
///
/// 角色基类(战士类与敌人类的共有的基类)
///
protected ICharacter mCharacter;
protected SoldierFSMSystem mFSM;
///
/// 添加转换条件
///
///
///
public void AddTransition(SoldierTransition trans, SoldierStateID id)
{
if (trans == SoldierTransition.NullTransition || id == SoldierStateID.None)
{
Debug.LogErrorFormat("SoldierState Error:trans或者id不能为null");
return;
}
if (mTransitionAndStateIdDic.ContainsKey(trans))
{
Debug.LogErrorFormat("Soldier State Error:{0}已经添加了", trans);
return;
}
mTransitionAndStateIdDic.Add(trans, id);
}
///
/// 删除转换条件
///
///
public void DeleteTransition(SoldierTransition trans)
{
if (!mTransitionAndStateIdDic.ContainsKey(trans))
{
Debug.LogErrorFormat("SoldierState Error:trans不能为null");
return;
}
mTransitionAndStateIdDic.Remove(trans);
}
///
/// 获取当前状态ID根据转换条件
///
///
///
public SoldierStateID GetOutPutState(SoldierTransition trans)
{
if (!mTransitionAndStateIdDic.ContainsKey(trans))
{
Debug.LogErrorFormat("SoldierState Error:trans不存在");
return SoldierStateID.None;
}
return mTransitionAndStateIdDic[trans];
}
//进入状态
public virtual void DoBeforeEnter() { }
//离开状态
public virtual void DoBeforeLeave() { }
//状态更新函数,传入当前目标集合
public abstract void Reason(List targets);
public abstract void Action(List targets);
}
此类负责战士状态的添加、删除、和转换条件
using System.Collections.Generic;
using UnityEngine;
///
/// 战士状态机系统类
///
public class SoldierFSMSystem
{
///
/// 战士状态列表
///
private List mStates = new List();
private ISoldierState mCurState;
///
/// 当前状态
///
public ISoldierState CurState
{
get { return mCurState; }
}
///
/// 添加多个状态
///
///
public void AddStates(params ISoldierState[] states)
{
foreach (ISoldierState state in states)
{
AddState(state);
}
}
///
/// 添加状态
///
///
public void AddState(ISoldierState state)
{
if (state == null)
{
Debug.LogError("SoldierFSMSystem Error:状态为null");
return;
}
if (mStates.Count == 0)
{
mStates.Add(state);
mCurState = state;
return;
}
for (int i = 0; i < mStates.Count; i++)
{
if (mStates[i].StateId == state.StateId)
{
Debug.LogError("SoldierFSMSystem Error:状态已存在:" + state.StateId);
return;
}
}
mStates.Add(state);
}
///
/// 删除状态
///
///
public void DeleteState(SoldierStateID stateId)
{
if (stateId == SoldierStateID.None)
{
Debug.LogError("SoldierFSMSystem DeleteState Error:状态为null");
return;
}
for (int i = 0; i < mStates.Count; i++)
{
if (mStates[i].StateId == stateId)
{
mStates.Remove(mStates[i]);
return;
}
}
Debug.LogError("SoldierFSMSystem DeleteState Error:StateId不存在");
}
///
/// 转换状态根据转换条件
///
///
public void PerformTransition(SoldierTransition trans)
{
if (trans == SoldierTransition.NullTransition)
{
Debug.LogError("SoldierFSMSystem PerformTransition Error:转换条件为null");
return;
}
//根据转换条件获取当前对应的状态ID
SoldierStateID nextStateId = mCurState.GetOutPutState(trans);
if (nextStateId == SoldierStateID.None)
{
Debug.LogError("SoldierFSMSystem PerformTransition Error:获取下一个转换条件为null");
return;
}
foreach (ISoldierState state in mStates)
{
if (state.StateId == nextStateId)//如果存在这样的状态
{
mCurState.DoBeforeLeave();//当前状态执行离开操作
mCurState = state;//将新的状态赋值给当前状态
state.DoBeforeEnter();//新的状态执行进入状态操作
return;
}
}
}
}
1、添加战士待机状态
继承战士状态基类,在构造中设置当前状态ID为Idle,循环判断当有敌人时,切换设置状态条件为SeeEnemy,即切换到追击敌人Chase状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 战士待机状态类
///
public class SoldierIdleState : ISoldierState {
public SoldierIdleState(SoldierFSMSystem fsm, ICharacter character) : base(fsm,character)
{
mStateId = SoldierStateID.Idle;
}
public override void Reason(List targets)
{
if (targets != null && targets.Count > 0)
{
mFSM.PerformTransition(SoldierTransition.SeeEnemy);//如果存在敌人则执行切换到看到敌人条件,即追击敌人状态
}
}
public override void Action(List targets)
{
}
///
/// 状态进入时操作
///
public override void DoBeforeEnter()
{
mCharacter.PlayAnim("stand");//执行待机动画
}
}
2、添加追击敌人状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 战士追击敌人状态
///
public class SoldierChaseState : ISoldierState
{
public SoldierChaseState(SoldierFSMSystem fsm, ICharacter character) : base(fsm,character)
{
mStateId = SoldierStateID.Chase;
}
public override void Reason(List targets)
{
if (targets == null || targets.Count <= 0)//没有敌人时
{
mFSM.PerformTransition(SoldierTransition.NoEnemy);
return;
}
float distance = Vector3.Distance(targets[0].Position, mCharacter.Position);
if (distance < mCharacter.AttackRange)//如果在攻击范围内
{
mFSM.PerformTransition(SoldierTransition.CanAttack);//开始攻击
}
}
public override void Action(List targets)
{
if (targets != null && targets.Count > 0)//有敌人时
{
mCharacter.SetNavMoveTarget(targets[0].Position);//寻路到敌人的位置
}
}
}
上面代码其中mCharacter.SetNavMoveTarget(targets[0].Position);内容如下
///
/// 设置寻路目标
///
///
public void SetNavMoveTarget(Vector3 targetPos)
{
mNav.SetDestination(targetPos);//mNav为角色身上的NavMeshAgent组件
}
3、添加战士攻击敌人状态类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 战士攻击敌人类
///
public class SoldierAttackState : ISoldierState
{
private float mAttackTime = 1;
private float mAttackTimer = 0;
public SoldierAttackState(SoldierFSMSystem fsm, ICharacter character) : base(fsm,character)
{
mStateId = SoldierStateID.Attack;
}
///
/// 状态更新类,传入当前角色
///
///
public override void Reason(List targets)
{
if (targets == null || targets.Count <= 0)//没有敌人时
{
mFSM.PerformTransition(SoldierTransition.NoEnemy);
}
float distance = Vector3.Distance(mCharacter.Position, targets[0].Position);
if (distance > mCharacter.AttackRange)//没有在攻击范围内
{
mFSM.PerformTransition(SoldierTransition.SeeEnemy);//追击敌人
}
}
///
/// 状态更新类,传入当前角色
///
///
public override void Action(List targets)
{
if (targets == null || targets.Count <= 0) return;//没有敌人时
//有敌人时
mAttackTimer += Time.deltaTime;
if (mAttackTimer >= mAttackTime)//每隔一秒攻击一次
{
mCharacter.Attack(targets[0]);//执行战士攻击动作,传入当前攻击目标
mAttackTimer = 0;
}
}
}
战士基类继承自角色基类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 战士基类
///
public class ISoldier : ICharacter
{
///
/// 战士状态机
///
protected SoldierFSMSystem mFSMSystem;
public ISoldier() : base()
{
InitFSM();
}
///
/// 循环更新状态机
///
///
public void UpdateFSMAI(List targets)
{
mFSMSystem.CurState.Reason(targets);
mFSMSystem.CurState.Action(targets);
}
///
/// 初始化战士状态机
///
public void InitFSM()
{
mFSMSystem = new SoldierFSMSystem();
SoldierIdleState idleState = new SoldierIdleState(mFSMSystem, this);
idleState.AddTransition(SoldierTransition.SeeEnemy, SoldierStateID.Chase);
SoldierChaseState chaseState = new SoldierChaseState(mFSMSystem, this);
chaseState.AddTransition(SoldierTransition.NoEnemy, SoldierStateID.Idle);
chaseState.AddTransition(SoldierTransition.CanAttack, SoldierStateID.Attack);
SoldierAttackState attackState = new SoldierAttackState(mFSMSystem, this);
attackState.AddTransition(SoldierTransition.NoEnemy, SoldierStateID.Idle);
attackState.AddTransition(SoldierTransition.SeeEnemy, SoldierStateID.Chase);
mFSMSystem.AddStates(idleState,chaseState,attackState);
}
}