本人新手,随便写写而已。
本文通过一个实例实现了在Unity下的有限状态机(参考了wiki上的教程)。
有限状态机是一个设备具有有限数量的状态,他可以在任何时间根据输入进行操作,使得一个状态进入另个一个状态。一个状态机在任何瞬间只能处于一种状态。
具体可以参考 状态设计模式。
本例是这样一种状态装换。
游戏人物NPC在空闲时处于巡逻状态,当看见Player在视野内的时候,转入转入追逐Player状态;一旦和Player距离拉大,便返回巡逻状态。
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
//无论是NPC还是Player都有很多状态,在状态切换的时候为了使得程序显得清晰明了,采用了状态机制
//首先实际状态都是继承自某个状态抽象类的,这个抽象类定义了进入,退出一个状态虚方法
//定义了检测环境是否发生状态改变,以及在该状态下执行的动作的抽象方法
// 该实例主要涉及到一个NPC在指定的位置进行巡逻,当看到Playeer的时候切换状态进入追逐Player状态
public enum Translate //如果进入一个新的状态,需要一些触发,比如NPC看到了Player,由巡逻状态进入跟踪状态
{
NullTrans,
SeePlayer,
LosePlayer
}
public enum StateID //每个状态都应该有一个ID,作为识别改状态的标志
{
NullState,
Chaseingplayer,
FollowPath
}
public abstract class FMS_State //定一个抽象类
{
private StateID id; //定一个状态ID作为变量来标识
public StateID ID
{
set { id = value; }
get { return id; }
}
private Dictionary<Translate, StateID> map = new Dictionary<Translate, StateID>(); //在某一状态下,事件引起了触发进入另一个状态
// 于是我们定义了一个字典,存储的便是触发的类型,以及对应要进入的状态
public void addDictionary(Translate tr, StateID id1) //向字典里添加
{
if (tr == Translate.NullTrans)
{
Debug.LogError("Null Trans is not allower to adding into");
return;
}
if (ID == StateID.NullState)
{
Debug.LogError("Null State id not ~~~");
return;
}
if (map.ContainsKey(tr)) //NPC 任何时候都只能出于一种状态,所以一旦定义了一个触发的枚举类型,对应的只能是接下来的一种状态
{
Debug.LogError(id1.ToString() + "is already added to");
return;
}
map.Add(tr, id1);
}
public void deleateDictionary(Translate tr) //删除字典里存储的一个状态
{
if (tr == Translate.NullTrans)
{
Debug.LogError("TransNull is not allowed to delate");
return;
}
if (map.ContainsKey(tr))
{
map.Remove(tr);
return;
}
Debug.LogError(tr.ToString() + "are not exist");
}
public StateID GetOutState(Translate tr) //由状态的触发枚举类型返回一个对应的状态类型
{
if (map.ContainsKey(tr))
{
// Debug.Log("Translate " + tr + "State" + map[tr]);
return map[tr];
}
return StateID.NullState;
}
public virtual void DoBeforeEnter() { } //虚方法
public virtual void DoBeforeMoveing() { }
public abstract void Reason(GameObject player, GameObject NPC); // 抽象方法
public abstract void Act(GameObject player, GameObject NPC);
}
随后根据这个基类派生出巡逻类,和追逐类。
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
public class Follow_State:FMS_State //派生出一个巡逻的类
{
private GameObject[] waypoints; //在这些位置巡逻
private int currentWayPoint;
public Follow_State(GameObject[] ob)
{
waypoints= ob;
currentWayPoint = 0;
ID = StateID.FollowPath; //当前状态的ID
}
public override void Reason(GameObject player, GameObject NPC) //与环境交互,来判断是否需要状态切换
{
RaycastHit hit;
if(Physics.Raycast(NPC.transform.position,NPC.transform.forward,out hit ,15))
{
if(hit.transform.tag=="Player")
{
FMS_Machine_Manage.GetInstance.changeState(Translate.SeePlayer);
}
}
}
public override void Act(GameObject player, GameObject NPC) //在该状态下改做点什么呢
{
Vector3 vel = NPC.rigidbody.velocity;
Vector3 dir =waypoints[currentWayPoint].transform.position- NPC.transform.position;
if(dir.magnitude<1)
{
currentWayPoint++;
// Debug.Log("currentwappoint " + currentWayPoint);
if(currentWayPoint>=waypoints.Length)
{
currentWayPoint = 0;
}
}
else
{
vel = dir.normalized*10;
NPC.transform.rotation = Quaternion.Lerp(NPC.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 5);
NPC.transform.eulerAngles = new Vector3(0, NPC.transform.localEulerAngles.y, 0);
}
NPC.rigidbody.velocity = vel;
}
public override void DoBeforeMoveing()
{
}
public override void DoBeforeEnter()
{
}
}
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
public class Chaseing_State : FMS_State { //派生出了一个追逐类
public Chaseing_State()
{
ID = StateID.Chaseingplayer; //定义 状态ID
}
public override void Reason(GameObject player, GameObject NPC)
{
if(Vector3.Distance(player.transform.position,NPC.transform.position)>=5)
{
FMS_Machine_Manage.GetInstance.changeState(Translate.LosePlayer);
}
}
public override void Act(GameObject player, GameObject NPC)
{
Vector3 vel = NPC.rigidbody.velocity;
Vector3 moveDir = player.transform.position - NPC.transform.position;
NPC.transform.rotation = Quaternion.Lerp(NPC.transform.rotation, Quaternion.LookRotation(moveDir), Time.deltaTime * 5);
NPC.transform.eulerAngles = new Vector3(0, NPC.transform.eulerAngles.y, 0);
vel = moveDir * 3;
NPC.rigidbody.velocity = vel;
}
}
通常对状态的管理,我们会建立一个stateMachine来管理。
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
//状态管理类,对状态进行管理
public class FMS_Machine_Manage:MonoBehaviour
{
private List<FMS_State> states;//存储所有状态的的List
private FMS_State currentState; //当前状态
private StateID currentStateID;//当前状态ID
public FMS_State CurrentState {
set { currentState = value; }
get { return currentState; } }
public StateID CurrentStateID {
set { currentStateID = value; }
get { return currentStateID; } }
public GameObject player;
public GameObject[] path;
public GameObject NPC;
private static FMS_Machine_Manage instance;
public static FMS_Machine_Manage GetInstance
{
get
{
if (instance == null)
{
// instance = new FMS_Machine_Manage();
GameObject n = new GameObject();
n.name = "FMS_Machine_Manage";
instance = n.AddComponent<FMS_Machine_Manage>() as FMS_Machine_Manage;
}
return instance;
}
}
public void UpdateFunction()
{
CurrentState.Reason(player, NPC);
CurrentState.Act(player, NPC);
}
public void Revert()
{
}
void Awake()
{
states = new List<FMS_State>(); //初始化
NPC = GameObject.FindGameObjectWithTag("NPC");
player = GameObject.FindGameObjectWithTag("Player");
path = GameObject.FindGameObjectsWithTag("path");
}
public void MakeFMSMachine()
{
Follow_State follow = new Follow_State(path);
follow.addDictionary(Translate.SeePlayer, StateID.Chaseingplayer);
Chaseing_State chase = new Chaseing_State();
chase.addDictionary(Translate.LosePlayer, StateID.FollowPath);
GetInstance. AddFmsState(follow);
GetInstance. AddFmsState(chase);
}
public void AddFmsState(FMS_State s)//注册所有状态
{
if (s == null)
{
Debug.LogError(" Null reference is not allowed");
}
if (states.Count == 0)
{
states.Add(s); //设置默认状态(important);
currentState = s;
currentStateID = s.ID;
return;
}
foreach (FMS_State state in states)
{
if (state == s)
{
Debug.LogError(s.ID.ToString() + "has already been added");
return;
}
}
states.Add(s);
}
public void delateFmsState(StateID id)
{
if (id == StateID.NullState)
{
Debug.LogError("NullStateID is not allowed for a real state");
return;
}
foreach (FMS_State state in states)
{
if (state.ID == id)
{
states.Remove(state);
return;
}
}
}
public void changeState(Translate tr) //更改状态
{
if (tr == Translate.NullTrans)
{
Debug.LogError("NullTransition is not allowed for a real transition");
return;
}
//if (currentState.GetOutState(tr) == StateID.NullState)
//{
// Debug.Log("translate" + " " + tr + " " + currentState.GetOutState(tr)+" "+CurrentStateID);
// Debug.LogError("1234");
// return;
//}
StateID id = CurrentState.GetOutState(tr); //当前状态会进入的新的状态
currentStateID = id;
// Debug.Log("Prives" + Prives.ID);
foreach (FMS_State state in states) //通过注册的所有状态,进行搜索来获取要进入的状态实例
{
if (currentStateID == state.ID)
{
CurrentState.DoBeforeMoveing(); //退出状态前,留下点什么,比如挥舞下手臂
currentState = state;
CurrentState.DoBeforeEnter(); //进入状态
break;
}
}
}
}
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
public class NPC_Control : MonoBehaviour {
public void Start()
{
FMS_Machine_Manage.GetInstance.MakeFMSMachine();
}
public void FixedUpdate()
{
FMS_Machine_Manage.GetInstance.UpdateFunction();
}
}