在网上查阅了很多资料,然后根据一些自己的需求进行了一些修改。
需要的是:状态父类,状态机系统(增删状态等管理状态类),一个敌人单位的控制类(继承MonoBehaviour)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//敌人状态类型
public enum Enemy_StateID
{
NullStateID=0,
Idle,
Patrol,
Attack,
Skill,
Execute,
Trace,
}
//敌人状态转化
public enum Enemy_Transition
{
NullTransition=0,
Idle,
Patrol,
Attack,
Skill,
Execute,
Trace,
}
//状态机系统
public class EnemyFSMSystem
{
private List<EnemyFSMState> states;
public EnemyManager manager;
private Enemy_StateID currentStateID;
public Enemy_StateID CurrentStateID { get { return currentStateID; } }
private EnemyFSMState currentState;
public EnemyFSMState CurrentState { get { return currentState; } }
public EnemyFSMSystem()
{
states = new List<EnemyFSMState>();
}
/// <summary>
/// 添加状态
/// </summary>
public void AddState(EnemyFSMState s)
{
// Check for Null reference before deleting
if (s == null)
{
Debug.LogError("FSM ERROR: Null reference is not allowed");
}
// 第一个状态默认为初始状态
if (states.Count == 0)
{
states.Add(s);
currentState = s;
currentStateID = s.ID;
return;
}
// 如果有重复状态则报错
foreach (EnemyFSMState state in states)
{
if (state.ID == s.ID)
{
Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
" because state has already been added");
return;
}
}
s.system = this;
Debug.Log(s.system);
states.Add(s);
}
/// <summary>
/// 删除状态
/// </summary>
public void DeleteState(Enemy_StateID id)
{
// 检测为空,报错
if (id == Enemy_StateID.NullStateID)
{
Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
return;
}
// 搜索到就删除
foreach (EnemyFSMState state in states)
{
if (state.ID == id)
{
states.Remove(state);
return;
}
}
Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
". It was not on the list of states");
}
/// <summary>
/// 执行转换,必须通过转换改变状态
/// </summary>
public void PerformTransition(Enemy_Transition trans)
{
if (trans == Enemy_Transition.NullTransition)
{
Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
return;
}
// Check if the currentState has the transition passed as argument
Enemy_StateID id = currentState.GetOutputState(trans);
if (id == Enemy_StateID.NullStateID)
{
Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
" for transition " + trans.ToString());
return;
}
// Update the currentStateID and currentState
currentStateID = id;
foreach (EnemyFSMState state in states)
{
if (state.ID == currentStateID)
{
// Do the post processing of the state before setting the new one
currentState.DoBeforeLeaving();
currentState = state;
// Reset the state to its desired condition before it can reason or act
currentState.DoBeforeEntering();
break;
}
}
}
状态父类,用于其他状态类继承
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyFSMState
{
protected Dictionary<Enemy_Transition, Enemy_StateID> map = new Dictionary<Enemy_Transition, Enemy_StateID>();
protected Enemy_StateID stateID;
public EnemyFSMSystem system;
public Enemy_StateID ID { get { return stateID; } }
public void AddTransition(Enemy_Transition trans, Enemy_StateID id)
{
// Check if anyone of the args is invalid
if (trans == Enemy_Transition.NullTransition)
{
Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
return;
}
if (id == Enemy_StateID.NullStateID)
{
Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
return;
}
// Since this is a Deterministic FSM,
// check if the current transition was already inside the map
if (map.ContainsKey(trans))
{
Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
"Impossible to assign to another state");
return;
}
map.Add(trans, id);
}
/// <summary>
/// This method deletes a pair transition-state from this state's map.
/// If the transition was not inside the state's map, an ERROR message is printed.
/// </summary>
public void DeleteTransition(Enemy_Transition trans)
{
// Check for NullTransition
if (trans == Enemy_Transition.NullTransition)
{
Debug.LogError("FSMState ERROR: NullTransition is not allowed");
return;
}
// Check if the pair is inside the map before deleting
if (map.ContainsKey(trans))
{
map.Remove(trans);
return;
}
Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
" was not on the state's transition list");
}
/// <summary>
/// This method returns the new state the FSM should be if
/// this state receives a transition and
/// </summary>
public Enemy_StateID GetOutputState(Enemy_Transition trans)
{
// Check if the map has this transition
if (map.ContainsKey(trans))
{
return map[trans];
}
return Enemy_StateID.NullStateID;
}
/// <summary>
/// 进入状态时执行
/// </summary>
public virtual void DoBeforeEntering()
{
}
/// <summary>
///离开状态时执行
/// </summary>
public virtual void DoBeforeLeaving() { }
/// <summary>
/// 在此状态执行
/// </summary>
public virtual void StateUpdate()
{
}
}
状态类,就拿待机状态举个例子
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyIdleState : EnemyFSMState
{
public EnemyIdleState()
{
//赋予此状态对应的枚举ID
stateID = Enemy_StateID.Idle;
//添加转化,给予一个可以进入Trace状态的一个转化。
AddTransition(Enemy_Transition.Trace, Enemy_StateID.Trace);
}
public override void DoBeforeEntering()
{
}
public override void DoBeforeLeaving()
{
}
public override void StateUpdate()
{
//通过状态及系统获取敌人控制类里的方法来判断条件是否达成
if (system.manager.IsTracePlayer())
{
//进入转化
system.PerformTransition(Enemy_Transition.Trace);
}
}
}
敌人控制类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum EnemyType
{
Test
}
public class EnemyManager : MonoBehaviour
{
private EnemyMessage message;
/// <summary>
/// 敌人信息
/// </summary>
public EnemyMessage Message { get { return message; } }
private EnemyFSMSystem fsmSystem;
private float speedUPTimer = 0;
private Animator enemy_Animator;
private bool isGround;
public bool IsGround { get { return isGround; } }
private GameObject player;
[SerializeField] private float traceDistance = 5f;
public void Awake()
{
player = GameObject.FindGameObjectWithTag("Player");
message = GetComponent<EnemyMessage>();
//新建一个状态机系统
fsmSystem = new EnemyFSMSystem();
//将此脚本引入进去,这样状态类执行时就可以通过状态机系统调用EnemyManager的方法
fsmSystem.manager = this;
//新建一个待机状态
EnemyFSMState idleState = new EnemyIdleState();
//将待机状态导入进状态机系统
fsmSystem.AddState(idleState);
//将状态里面添加进入状态机系统
idleState.system = fsmSystem;
EnemyFSMState traceState = new EnemyTraceState();
fsmSystem.AddState(traceState);
traceState.system = fsmSystem;
}
private void FixedUpdate()
{
//通过状态机系统去调用当前状态的StateUpdate
if (fsmSystem != null)
fsmSystem.CurrentState.StateUpdate();
}
public void SelectInit(EnemyType type)
{
switch (type)
{
case EnemyType.Test:
break;
default:
break;
}
}
//玩家是否靠近追踪范围
public bool IsTracePlayer()
{
if ((transform.position - player.transform.position).magnitude < traceDistance)
{
return true;
}
return false;
}
public void TracePlayer()
{
transform.position = Vector3.MoveTowards(transform.position, player.transform.position, Time.deltaTime * message.Walkspeed);
}
}
当然我这个写的很菜,很多方法不应该写到Enemymanager里面,应该是通过manager去获取其他对应的功能类的方法,还有很多地方需要改进