using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public enum Transition
{
//定义了一个Transition(转换)类型的枚举变量,所以我们接下来要根据实际情况扩展此枚举变量。
NullTransition = 0,
}
public enum StateID
{
//定义了一个StateId(状态ID)类型的枚举变量,所以我们接下来也要根据实际情况扩展此枚举变量。
NullStateID = 0,
}
public abstract class FSMState//抽象类,我们必须继承它才可以在脚本中实例化并使用它
{
protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
/*这个成员变量是一个Dictionary类型,就相当于java中的Map类型,存储的是一个个的关联对。此刻我们存储的关联对类型就是上面我们定义的连个枚举类型。那么接下来我们猜也能才出来我们一定会向其添加关联对,可能还会移除此关联对。那么这个东西的用处我们现在还是很迷茫,不要紧,继续向下看吧!没问题的。*/
protected StateID stateID;
public StateID ID { get { return stateID; } }
public void AddTransition(Transition trans, StateID id)//增加关联对(转换,状态ID)
{
// Check if anyone of the args is invalid
if (trans == Transition.NullTransition)//如果增加的转换是个NullTransition(空转换),直接Debug.LogError,然后返回
{
Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
return;
}
if (id == StateID.NullStateID)//如果状态ID是NullStateID(空状态ID),怎么办?还是Debug.LoError,然后返回
{
Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
return;
}
if (map.ContainsKey(trans))//如果将要增加的关联对是之前就存在与关联容器中,也照样Debug.LogError,之后返回被调用处
{
Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
"Impossible to assign to another state");
return;
}
public void DeleteTransition(Transition trans)//删除关联对函数,前提是里面要有这个关联对啊!
{
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 on the state's transition list");
}
public StateID GetOutputState(Transition trans)//此函数由下面这个脚本FSMSystem.cs中的PerformTransition函数调用。是用来检索状态的。
{
if (map.ContainsKey(trans))
{
return map[trans];
}
return StateID.NullStateID;
}
public virtual void DoBeforeEntering() { }//从名字就可以看出它的作用是什么,但是我们得在FSMSystem.cs中得到答案。
public virtual void DoBeforeLeaving() { }
public abstract void Reason(GameObject player, GameObject npc);
/*这个函数与下面这个函数是这个类中最重要的函数。Reason函数负责监听环境条件的改变并触发相应的事件转换。Act函数的作用在于表现当前状态下NPC的行为。我们得在这个抽象类的子类中覆写这两个方法。
*/
public abstract void Act(GameObject player, GameObject npc);
}
FSMSystem.cs:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
foreach (FSMState 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;
}
}
states.Add(s);//这一句代码第一次不执行,因为第一次states是空的,执行到上面的if里面后立即返回了
}
public void DeleteState(StateID id)//跟据ID来从容器states中定向移除FSMState实例
{
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: Impossible to delete state " + id.ToString() +
". It was not on the list of states");
}
public void PerformTransition(Transition trans)//执行转换
{
if (trans == Transition.NullTransition)
{
Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
}
// Check if the currentState has the transition passed as argument
StateID id = currentState.GetOutputState(trans);//这下我们得回到当初我所说讲到的FSMState.cs中的那个检索状态的函数。如果检索不出来,就返回NullStateId,即执行下面if语句。
if (id == StateID.NullStateID)
{
Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
" for transition " + trans.ToString());
return;
}
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class NPCControl : MonoBehaviour
{
public GameObject player;//主角
public Transform[] path;//多个寻路点
private FSMSystem fsm;//内置一个fsm
public void SetTransition(Transition t) //转换状态
{
fsm.PerformTransition(t);
}
public void Start()
{
MakeFSM();//首先初始化状态机,执行MakeFSM函数
}
public void FixedUpdate()//作为驱动源
{
fsm.CurrentState.Reason(player, gameObject);//定期(默认是0.02秒,在Edit->rojectSetting->Time中可以发现)调用当前FSMState中的Reason函数,用以检测外界环境是否发生变化,并且根据发生的变化来执行某些事件
fsm.CurrentState.Act(player, gameObject);//定期执行当前状态下的某些行为
}
// The NPC has two states: FollowPath and ChasePlayer
// If it's on the first state and SawPlayer transition is fired, it changes to ChasePlayer
// If it's on ChasePlayerState and LostPlayer transition is fired, it returns to FollowPath
private void MakeFSM()
{
FollowPathState follow = new FollowPathState(path);//定义并实例化FSMState
follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);//向其添加转换对
那个实心的箭头代表的代码就是上面圆角矩形里面的代码。看了之后我们因该明白了那两句代码的现实意义了吧!即定义转换,也就是floow状态可以与chase互相转换,如果我们填充的状态中出现了别的状态比如说:state0,此时状态floow就不能转换到state0了,同样state0也无法转换到floow。
***************************************************
fsm = new FSMSystem();//实例化fsm
fsm.AddState(follow);//将follow装载到fsm中
fsm.AddState(chase);//将chase装载到fsm中
}
}
public class FollowPathState : FSMState
/*继承抽象类FSMState,但是得注意一点:我们得在抽象类FSMState脚本中的两个枚举变量分别加入对应的枚举变量,比如在Transition中加入SawPlayer,LostPlayer;在StateID中加入ChasingPlayer,FollowingPath。*/
{
private int currentWayPoint;
private Transform[] waypoints;
public override void Reason(GameObject player, GameObject npc)
{
// If the Player passes less than 15 meters away in front of the NPC
RaycastHit hit;
if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
{
if (hit.transform.gameObject.tag == "layer")
npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);//当射线射到的物体的标签为Player时,触发转换。
}
}
public override void Act(GameObject player, GameObject npc)//当NPC当前状态为follow时不断执行以下行为。下面那个类的用法也是一样的。
{
// Follow the path of waypoints
// Find the direction of the current way point
Vector3 vel = npc.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;
// Rotate towards the waypoint
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);
}
// Apply the Velocity
npc.rigidbody.velocity = vel;
}
} // FollowPathState
public class ChasePlayerState : FSMState//同上。
{
public ChasePlayerState()
{
stateID = StateID.ChasingPlayer;
}
public override void Reason(GameObject player, GameObject npc)
{
// If the player has gone 30 meters away from the NPC, fire LostPlayer transition
if (Vector3.Distance(npc.transform.position, player.transform.position) >= 30)
npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
}
public override void Act(GameObject player, GameObject npc)
{
// Follow the path of waypoints
// Find the direction of the player
Vector3 vel = npc.rigidbody.velocity;
Vector3 moveDir = player.transform.position - npc.transform.position;
// Rotate towards the waypoint
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;
// Apply the new Velocity
npc.rigidbody.velocity = vel;
}