Unity FSM有限状态机
来源:互联网 发布:收淘宝店铺拿去干嘛 编辑:程序博客网 时间:2024/06/12 10:12
关于人物或敌人的多种状态之间的切换,我们平常的做法就是定义个枚举,把所需的状态放在枚举里面,我们可能要在项目很多地方调用switch方法或者是设置人物状态。当状态比较多的时候,对状态的管理和获取也会变得很麻烦。所以这个时候,我们需要一个有限状态机来统一管理我们的状态(state)。
有限状态机包括两个部分:(1)状态集 FSMState,(2)状态管理机 FSMSystem.
一. 状态集FSMState代表一组状态的集合,一个游戏中可能有多个状态集,所以我们再设计FSMState时最好将它设计为抽象类,这样就能通过继承FSMState拥有一些公共的属性和方法,同时又有自己特定的状态。
二. 一个状态管理机主要的功能有这两点:
1.能够添加和删除状态集(FSMState).
2.能够切换和获取 某个状态集(FSMState)当前的状态(state).
我们还是用个小例子来解析一下:场景有两个物体,一个NPC,一个Player,NPC平时就是巡逻的状态,当Player距离NPC多近时NPC开始追逐Player,超过多远之后就继续巡逻。
下面上脚本:
状态机脚本
using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// 为过度加入枚举标签,对应相应的状态/// </summary>public enum Transition{ NullTransition=0, //用这个过渡来代表你的系统中不存在的状态 SawPlayer, //这里是NPC的两个过渡 LostPlayer}/// <summary>/// 为状态加入枚举标签/// </summary>public enum StateID{ NullStateID=0, ChasingPlayer, //为配合NPC添加两个状态 FollowingPath }/// <summary>/// 状态类/// </summary>public abstract class FSMState { protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>(); /// <summary> /// 为每一个子类初始化当前的状态 /// </summary> protected StateID stateID; public StateID ID { get { return stateID; } } /// <summary> /// 添加过渡 /// </summary> /// <param name="trans"></param> /// <param name="id"></param> public void AddTransition(Transition trans,StateID id) { //验证每个参数是否合法 if(trans==Transition.NullTransition) { Debug.LogError("FSMState error:NullTransition is not allowed for a real transition"); return; } if(id==StateID.NullStateID) { Debug.LogError("FSMState error:NullStateID is not allowed for a real stateID"); return; } //要知道这是一个确定的有限状态机(每个过渡对应一种状态,不能产生分支) //检查当前的过渡是否已经在字典当中了 if(map.ContainsKey(trans)) { Debug.LogError("FSMState error:There already has transition " + trans.ToString() + "check for another"); return; } map.Add(trans, id); } /// <summary> /// 这个方法在状态地图中删除transition-stateid对 /// 如果过渡不存在地图中将打印一个错误 /// </summary> /// <param name="trans"></param> public void DeleteTransition(Transition trans) { //check for NullTransition if(trans==Transition.NullTransition) { Debug.LogError("FSMState error:NullTransition is not allowed"); return; } //删除之前确认键值对是否存在于状态地图中 if(map.ContainsKey(trans)) { map.Remove(trans); return; } Debug.LogError("FSMState error:Transition " + trans.ToString() + "passed to " + stateID.ToString() + "was not exist"); } /// <summary> /// 该方法在该状态接收到一个过渡时返回状态机需要成为的新状态 /// </summary> /// <param name="trans"></param> /// <returns></returns> public StateID GetOutputState(Transition trans) { //检查一下map状态字典中是否存在这个过渡 if(map.ContainsKey(trans)) { return map[trans]; } return StateID.NullStateID; }/// <summary> /// 这个方法用来设立进入状态前的条件 /// 在状态机分配它到当前状态之前他会被自动调用 /// </summary>public virtual void DoBeforeEntering() { } /// <summary> /// 这个方法用来让一切都是必要的,例如在有限状态机变化到另一个时重置变量 /// 在状态机切换到新的状态之前它会被自动调用 /// </summary> public virtual void DoBeforeLeaving() { } /// <summary> /// 要切换状态的条件,原因 /// NPC是被该类约束下对象的一个引用 /// </summary> /// <param name="player"></param> /// <param name="npc"></param> public abstract void Reason(GameObject player, GameObject npc); /// <summary> /// 切换状态后对应的行动 /// 表现->该方法用来控制NPC在游戏世界中的行为 /// NPC的任何动作,移动或者交流都需要放置在这 , /// NPC是被该类约束下对象的一个引用 /// </summary> /// <param name="player"></param> /// <param name="npc"></param> public abstract void Act(GameObject player, GameObject npc);}/// <summary>/// 该类便是有限状态机类/// 它持有NPC的状态集合,并且有添加,删除状态的方法,以及改变当前正在执行的状态/// </summary>public class FSMSystem{ private List<FSMState> states; //通过预装一个过渡的唯一方式来改变状态机的状态 //不要直接改变当前的状态 /// <summary> /// 当前的状态ID /// </summary> private StateID currentStateID; /// <summary> /// 当前的状态类信息 /// </summary> private FSMState currentFSMState; public StateID CurrentStateID { get { return currentStateID; } } public FSMState CurrentFSMState { get { return currentFSMState; } } public FSMSystem() { states = new List<FSMState>(); } /// <summary> /// 这个方法为有限状态机置入新的状态 /// 或者在改状态已经存在列表时打印错误信息 /// 第一个添加的状态也是最初的状态 /// </summary> public void AddState(FSMState s) { //添加前检查是否为空 if(s==null) { Debug.LogError("FSM error:Null reference is not allowed"); } //被装在的第一个状态也是初始状态 if(states.Count==0) { states.Add(s); currentFSMState = s; currentStateID = s.ID; return; } //如果该状态未被添加过,则加入集合 foreach(FSMState state in states) { if(state.ID==s.ID) { Debug.LogError("FSM error: There already exist " + s.ID.ToString()); return; } } states.Add(s); } /// <summary> /// 删除一个已存在状态机中的状态 /// 在它不存在时打印错误信息 /// </summary> /// <param name="id"></param> public void DeleteState(StateID id) { //在删除前检测其是否为空 if(id==StateID.NullStateID) { Debug.LogError("FSM error:NullStateID is not allowed for a real state"); return; } foreach(FSMState state in states) { if(state.ID==id) { states.Remove(state); return; } } Debug.LogError("FSM error:Can't delete state " + id.ToString() + ",it was not in list"); } /// <summary> /// 该方法基于当前状态和过渡状态是否通过来尝试改变状态机的状态,当当前状态没有目标状态用来过渡时则打印错误信息 /// 切换当前状态到下一个要转换的状态 /// </summary> /// <param name="trans"></param> public void PerformTransition(Transition trans) { //在改变当前状态前检测NullTransition if(trans==Transition.NullTransition) { Debug.LogError("FSM error:NullTransition is not allowed"); return; } //在改变状态前检测当前状态是否可作为过渡的参数 StateID id = currentFSMState.GetOutputState(trans); if(id==StateID.NullStateID) { Debug.LogError("FSM error: State " + currentFSMState.ID.ToString() + "does not allowed"); return; } //更新当前的状态机和状态编号 currentStateID = id; foreach(FSMState state in states) { if(state.ID==currentStateID) { //在状态变为新状态前执行后处理 currentFSMState.DoBeforeLeaving(); currentFSMState = state; //在状态可以使用Reason(动机)或者Act(行为)之前为它的决定条件重置它自己 currentFSMState.DoBeforeEntering(); break; } } }}
NPC逻辑与状态集继承脚本:
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;[RequireComponent(typeof(Rigidbody))]public class NPCController : MonoBehaviour{ public GameObject player; public Transform[] path; private FSMSystem fsm; public void SetTransition(Transition t) { //该方法用来改变有限状态机的状态,有限状态机基于当前的状态和通过的过渡状态 //如果当前的状态没有用来通过的过度状态,则会抛出错误 fsm.PerformTransition(t); } private void Start() { MakeFSM(); } private void FixedUpdate() { fsm.CurrentFSMState.Reason(player, gameObject); fsm.CurrentFSMState.Act(player, gameObject); } private void MakeFSM() //建造状态机 { FollowPathState follow = new FollowPathState(path); follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer); ChassPlayerState chass = new ChassPlayerState(); chass.AddTransition(Transition.LostPlayer, StateID.FollowingPath); fsm = new FSMSystem(); fsm.AddState(follow); fsm.AddState(chass); }}public class FollowPathState : FSMState{ private int currentWayPoint; private Transform[] waypoints; public FollowPathState(Transform[] wp) { waypoints = wp; currentWayPoint = 0; stateID = StateID.FollowingPath; } public override void DoBeforeEntering() { Debug.Log("Followingpath before entering"); } public override void DoBeforeLeaving() { Debug.Log("Followingpath before leaving"); } //要切换状态的条件 public override void Reason(GameObject player, GameObject npc) { RaycastHit hit; if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15f)) { if (hit.transform.gameObject.tag == "Player") { npc.GetComponent<NPCController>().SetTransition(Transition.SawPlayer); } } } //切换状态后的行动 public override void Act(GameObject player, GameObject npc) { 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; }}public class ChassPlayerState : FSMState{ public ChassPlayerState() { stateID = StateID.ChasingPlayer; } public override void DoBeforeEntering() { Debug.Log("Followingpath before entering"); } public override void DoBeforeLeaving() { Debug.Log("Followingpath before leaving"); } //要切换状态的原因,条件 public override void Reason(GameObject player, GameObject npc) { if (Vector3.Distance(npc.transform.position, player.transform.position) > 3) { npc.GetComponent<NPCController>().SetTransition(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; } }
完成.
阅读全文
0 0
- Unity FSM有限状态机
- unity游戏开发中的有限状态机FSM经验
- FSM 有限状态机
- 有限状态机(FSM)
- 有限状态机/FSM
- FSM有限状态机
- Unity 内的敌人AI 或者 有限状态机FSM实现AI
- 有限状态机(FSM)初探
- 简单有限状态机(FSM)
- 有限状态机FSM的理解
- Go语言 有限状态机FSM
- boost 有限状态机(FSM)
- 有限状态机FSM的理解
- 有限状态机编程FSM
- 有限状态机(FSM)
- Verilog FSM 有限状态机
- Unity3D-FSM有限状态机
- 时序(四)----有限状态机FSM
- mac 终端获取服务器公钥证书 pem证书 转 der证书(转)
- js天气接口
- 悲催的iOS 11!
- hive中解决中文乱码
- Spring注入方式
- Unity FSM有限状态机
- 《从Activiti Designer5.8升级到5.9遇到的问题》
- 工作问题总结
- 12月14日云栖精选夜读:活动推荐丨阿里云TechInsight论坛为什么这么火?
- 09_脏读
- TextView文本展开缩放
- python3安装与Ipython notebook的安装【Linux】
- oracle入门很简单:八、oracle数据表
- 常见算法基础题思路简析(一)-排序篇