---------框架
先声明两个枚举类型 一个存状态 一个存转换条件,
声明一个状态的类 里面创建一个字典进行储存条件和相应的状态
这个类里面有添加方法,删除方法,还有有进入和出去的虚方法,还有是否转换状态的抽象方法
声明一个管理状态机的类 里面实例化一下状态类,和状态,里面有添加状态和删除状态的方法,
还有执行过度的方法,传入的条件进行判断,如果状态类里面有条件的话,返回状态,然后进行遍历,
当在状态类找到状态的时候,执行进入前的方法,然后把当前状态切换为传入条件返回的那个状态,再执行
/*
如何使用:
1. 为有限状态系统的转换和状态加上标签
在相应的枚举中。
2. 编写从FSMState继承的新类,并用对(transition-state)填充每个类。
当FSMSystem处于S1, a状态时,这些对表示状态S2
跃迁T被触发,状态S1从它跃迁到S2。 记住这是一个确定性的FSM。
一个转变不可能导致两个不同的状态。
方法Reason用于确定应该触发哪个转换。
您可以编写代码在另一个地方触发转换,如果您
感觉更适合你的项目。
Method Act拥有执行NPC在此状态下应该执行的动作的代码。
您可以在另一个地方为操作编写代码,如果您
感觉更适合你的项目。
3. 创建一个FSMSystem类的实例,并向它添加状态。
4. 调用理由和行动(或任何你用来触发转换和制作npc的方法)
从你的Update或FixedUpdate方法。
从Unity引擎的异步转换,如OnTriggerEnter, SendMessage,也可以使用,
当事件发生时。从你的FSMSystem实例中调用方法PerformTransition就可以了
①
/// 这个类表示有限状态系统中的状态。
/// 每个状态都有一个显示成对(过渡状态)的Dictionary
/// 如果在此状态下触发一个过渡,则FSM应该处于哪个状态就是当前状态。
/// 方法Reason用于确定应该触发哪个转换。
/// Method Act有执行NPC在这个状态下应该执行的动作的代码。
②
/// FSMSystem类表示有限状态机类。
/// 它有一个带有NPC的状态的列表,以及添加、删除状态和改变机器当前状态的方法。
/// 这是唯一的方法进行FSM的状态转换
/// 不要直接改变 CurrentState
③
/// 这个方法在FSM中放置新的状态,
/// 或者如果状态已经在List中,则打印一个ERROR消息。
/// 第一个加入的状态也是初始状态。
④
/// 该方法尝试根据当前状态和通过的过渡来改变FSM所处的状态。
/// 如果当前状态没有传递的转换目标状态,则打印ERROR消息。
*/
using System.Collections.Generic;
using UnityEngine;
// 在这个枚举中放置过渡的标签。
public enum Transition
{
NullTransition = 0, // 使用此转换表示系统中不存在的转换
//看见玩家
SawPlayer,
//丢失玩家
LostPlayer,
}
// 在这个枚举中放置状态的标签。
public enum StateId
{
NullStateId = 0, // 此ID表示不存在的状态
//追玩家
Chasing,
//自动寻路
AutoPath,
}
// ①这个类表示有限状态系统中的状态
public abstract class FSMState
{
//设置一个成对的字典
protected Dictionary<Transition, StateId> map;
protected StateId stateID;
protected FSMState()
{
map = new Dictionary<Transition, StateId>();
}
//状态
public StateId ID
{
get { return stateID; }
}
//添加过度
public void AddTransition(Transition trans, StateId id)
{
// 检查是否有无效参数
if (trans == Transition.NullTransition)
{
Debug.LogError("FSMState ERROR: NullTransition 在实际转换中是不允许的");
return;
}
if (id == StateId.NullStateId)
{
Debug.LogError("FSMState ERROR: NullStateID 不允许被真实的ID使用");
return;
}
// 检测当前的过度是否已经存在在字典中
if (map.ContainsKey(trans))
{
Debug.LogError("FSMState ERROR: 状态 " + stateID.ToString() + " 已经有 "
+ trans.ToString() + "不能分配给另一个状态");
return;
}
//如果不存在就添加到字典中
map.Add(trans, id);
}
// 删除过度
public void DeleteTransition(Transition trans)
{
// 检查参数是否无效
if (trans == Transition.NullTransition)
{
Debug.LogError("FSMState ERROR: NullTransition 不允许删除");
return;
}
// 在删除之前检测是否存在在字典中
if (map.ContainsKey(trans))
{
map.Remove(trans);
return;
}
Debug.LogError("FSMState ERROR: 过度 " + trans.ToString() + " 交给 " + stateID.ToString() +
" 不在状态过度联系的名单上");
}
// 该方法返回FSM应该是的新状态
public StateId GetOutputState(Transition trans)
{
// 检查是否有这个过度
if (map.ContainsKey(trans))
{
return map[trans];
}
return StateId.NullStateId;
}
///进入状态
public virtual void DoBeforeEntering()
{
}
// 离开状态
public virtual void DoBeforeLeaving()
{
}
// 该方法决定是否将状态转移到列表中的另一个状态,NPC是这个类控制的对象的引用
public abstract void Reason(GameObject player, GameObject npc);
// 这个方法控制游戏世界中NPC的行为,NPC所做的每一个动作、移动或交流都应该放在这里
public abstract void Act(GameObject player, GameObject npc);
}
// 有限状态机类。
public class FsmSystem
{
private List<FSMState> states;
private StateId currentStateID;
private FSMState currentState;
public StateId CurrentStateID
{
get { return currentStateID; }
}
public FSMState CurrentState
{
get { return currentState; }
}
public FsmSystem()
{
states = new List<FSMState>();
}
// ③添加状态
public void AddState(FSMState s)
{
// 添加前检查空引用
if (s == null)
{
Debug.LogError("FSM ERROR: 不允许空引用");
}
// 初始状态
if (states.Count == 0)
{
states.Add(s);
currentState = s;
currentStateID = s.ID;
return;
}
// 如果状态不在列表中,则将其添加到列表中
foreach (FSMState state in states)
{
if (s != null && state.ID == s.ID)
{
Debug.LogError("FSM ERROR: 不可能添加状态 " + s.ID.ToString() +
" 因为这个状态已经添加");
return;
}
}
states.Add(s);
}
// 删除状态
public void DeleteState(StateId id)
{
// 检查状态是否为空
if (id == StateId.NullStateId)
{
Debug.LogError("FSM ERROR: NullStateID 不允许用于真实状态");
return;
}
// 如果它在里面,搜索列表并删除状态
foreach (FSMState state in states)
{
if (state.ID == id)
{
states.Remove(state);
return;
}
}
Debug.LogError("FSM ERROR: 不可能删除状态 " + id.ToString() +
"不在状态列表里");
}
// ④执行过度
public void PerformTransition(Transition trans)
{
// 检查当前过度是否被允许
if (trans == Transition.NullTransition)
{
Debug.LogError("FSM ERROR: NullTransition 不允许进行过渡");
return;
}
// 检查当前状态是否将转换作为参数传递
StateId id = currentState.GetOutputState(trans);
if (id == StateId.NullStateId)
{
Debug.LogError("FSM ERROR: 状态 " + currentStateID.ToString() +
" 没有转换的目标状态 " + trans.ToString());
return;
}
// 更新当前状态信息
currentStateID = id;
foreach (FSMState state in states)
{
if (state.ID == currentStateID)
{
// 设置新状态前做处理
currentState.DoBeforeLeaving();
currentState = state;
// 设置新状态后做处理
currentState.DoBeforeEntering();
break;
}
}
}
--例子
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class NPCControl : MonoBehaviour
{
public GameObject player;
public Transform[] path;
public FsmSystem fsm;
public void Start()
{
MakeFSM();
}
public void FixedUpdate()
{
fsm.CurrentState.Reason(player, gameObject);
fsm.CurrentState.Act(player, gameObject);
}
//NPC有两种状态:FollowPath和ChasePlayer
//如果它在第一个状态,并且SawPlayer转换被触发,它会变成ChasePlayer
//如果它在ChasePlayerState和LostPlayer转换被触发,它返回到FollowPath
private void MakeFSM()
{
ChasePlayerState chase = new ChasePlayerState();
chase.AddTransition(Transition.LostPlayer, StateId.AutoPath);
FollowPathState follow = new FollowPathState(path);
follow.AddTransition(Transition.SawPlayer, StateId.Chasing);
fsm = new FsmSystem();
fsm.AddState(chase);
fsm.AddState(follow);
}
}
//巡逻
public class FollowPathState : FSMState
{
private int currentWayPoint;
private Transform[] waypoints;
public FollowPathState(Transform[] wp)
{
waypoints = wp;
currentWayPoint = 0;
stateID = StateId.AutoPath;
}
public override void DoBeforeEntering()
{
Debug.Log("进入前做了一些事情...");
}
public override void DoBeforeLeaving()
{
Debug.Log("离开前做了一些事情...");
}
public override void Reason(GameObject player, GameObject npc)
{
// 如果玩家从NPC面前经过时距离不到15米
RaycastHit hit;
if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
{
if (hit.transform.gameObject.tag == "Player")
npc.GetComponent<NPCControl>().fsm.PerformTransition(Transition.SawPlayer);
}
}
public override void Act(GameObject player, GameObject npc)
{
Debug.Log("12");
// 沿着路标的路径走
// 找出当前航路点的方向
Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;
if (moveDir.magnitude < 1)
{
currentWayPoint++;
if (currentWayPoint >= waypoints.Length)
{
currentWayPoint = 0;
}
}
else
{
vel = moveDir.normalized * 10;
// 向目标点旋转
npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
Quaternion.LookRotation(moveDir),
5 * Time.deltaTime);
npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);
}
// 应用速度
npc.GetComponent<Rigidbody>().velocity = vel / 5;
}
}
/// <summary>
/// 追
/// </summary>
public class ChasePlayerState : FSMState
{
public ChasePlayerState()
{
stateID = StateId.Chasing;
}
public override void Reason(GameObject player, GameObject npc)
{
// 如果玩家已经离开NPC 30米远,发射LostPlayer过渡
if (Vector3.Distance(npc.transform.position, player.transform.position) >= 2)
npc.GetComponent<NPCControl>().fsm.PerformTransition(Transition.LostPlayer);
}
public override void Act(GameObject player, GameObject npc)
{
// 沿着路标的路径走
// 找到玩家的方向
Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
Vector3 moveDir = player.transform.position - npc.transform.position;
// 向航路点旋转
npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
Quaternion.LookRotation(moveDir),
5 * Time.deltaTime);
npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);
vel = moveDir.normalized * 10;
// 应用新的速度
npc.GetComponent<Rigidbody>().velocity = vel;
}
|