简述
参考教程:
Unity3D有限状态机(FSM)学习笔记【7】使用实例.
FSM有限状态机学习及Unity3D案例讲解.
游戏最终结果:
开始游戏的时候,Enemy会沿着设定好的PatrolPoints路径进行巡逻,如果Player与Enemy之间小于4时,Enemy就会追逐Player,当Player距离大于8的时候,Enemy会回去继续进行巡逻。
操作过程
-
建立一个简单场景,包括敌人(Enemy),玩家(Player),巡逻点多个(PatrolPoints)。
-
Enemy添加Enemy标签TAG,Player添加Player标签TAG。两个都要添加Character Controller组件。
-
Enemy添加Enemy脚本,Player添加Player脚本(用这2个脚本之前还有多个基本脚本)
定义枚举类型
public enum StateID
{
NullStateID,
PatrolState,
ChaseState
}
public enum Transition
{
NullTransition,
FindPlayer,
LosePlayer
}
FSMState基类
public abstract class FSMState
{
protected StateID stateID;
public StateID ID { get { return this.stateID; } }
protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
protected FSMSystem fsm;
public FSMState(FSMSystem fsm)
{
this.fsm = fsm;
}
/// <summary>
/// 添加转换条件
/// </summary>
/// <param name="trans">转换条件</param>
/// <param name="stateID">转换的目标状态</param>
public void AddTransition(Transition trans, StateID stateID)
{
if (trans == Transition.NullTransition)
{
Debug.LogError(trans + "为空,转换条件不允许为空"); return;
}
if (stateID == StateID.NullStateID)
{
Debug.LogError(stateID + "为空,状态ID不允许为空"); return;
}
if (map.ContainsKey(trans))
{
Debug.LogError(trans + "已经存在,请查看该转换条件是否正确");
}
else
{
map.Add(trans, stateID);
}
}
/// <summary>
/// 删除转换条件
/// </summary>
/// <param name="trans">需删除的转换条件</param>
public void DeleteTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError(trans + "为空,转换条件不允许为空"); return;
}
if (map.ContainsKey(trans) == false)
{
Debug.LogError(trans + "不存在,请查看该转换条件是否正确");
}
else
{
map.Remove(trans);
}
}
/// <summary>
/// 通过转换条件,得到目标状态
/// </summary>
/// <param name="trans">转换条件</param>
/// <returns>返回目标状态</returns>
public StateID GetTargetStateID(Transition trans)
{
if (map.ContainsKey(trans) == false)
{
Debug.LogError(trans + "不存在,请查看该转换条件是否正确");
return StateID.NullStateID;
}
else
{
return map[trans];
}
}
public virtual void DoBeforeEntering() { }//进入动作
public virtual void DoAfterLeaving() { }//离开动作
public abstract void Act();//输入动作
public abstract void Reason();//转移动作
}
FSMSystem管理类
public class FSMSystem
{
private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();
private FSMState currentState;
/// <summary>
/// 更新当前状态行为
/// </summary>
public void UpdateFSM()
{
currentState.Act();
currentState.Reason();
}
/// <summary>
/// 添加状态
/// </summary>
/// <param name="state">需管理的状态</param>
public void AddState(FSMState state)
{
if (state == null)
{
Debug.LogError(state + "为空"); return;
}
if (currentState == null)
{
currentState = state;
}
if (states.ContainsValue(state))
{
Debug.LogError(state + "已经存在");
}
else
{
states.Add(state.ID, state);
}
}
/// <summary>
/// 删除状态
/// </summary>
/// <param name="id">需要删除状态的ID</param>
/// /// <returns>删除成功返回true,否则返回false</returns>
public bool DeleteState(StateID id)
{
if (id == StateID.NullStateID)
{
Debug.LogError(id + "为空");
return false;
}
if (states.ContainsKey(id) == false)
{
Debug.LogError(id + "不存在");
return false;
}
else
{
states.Remove(id);
return true;
}
}
/// <summary>
/// 执行转换
/// </summary>
/// <param name="trans">转换条件</param>
public void PerformTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError(trans + "为空"); return;
}
StateID targetID = currentState.GetTargetStateID(trans);
if (states.ContainsKey(targetID) == false)
{
Debug.LogError(targetID + "不存在"); return;
}
FSMState targetState = states[targetID];
currentState.DoAfterLeaving();
targetState.DoBeforeEntering();
currentState = targetState;
}
}
玩家代码
public class PlayerFSM : MonoBehaviour
{
private CharacterController characherCon;
public float speed = 5;
public float smooth = 5;
private void Start()
{
characherCon = gameObject.GetComponent<CharacterController>();
}
private void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
transform.Rotate(Vector3.up, h * smooth);
characherCon.SimpleMove(transform.forward * v * speed);
if (Input.GetKeyDown(KeyCode.Space))
{
speed = 10;
}
if (Input.GetKeyUp(KeyCode.Space))
{
speed = 5;
}
}
}
敌人代码
public class EnemyFSM : MonoBehaviour
{
private FSMSystem fsm;
private void Start()
{
fsm = new FSMSystem();
FSMState patrolState = new PatrolState(fsm);
FSMState chaseState = new ChaseState(fsm);
fsm.AddState(patrolState);
fsm.AddState(chaseState);
patrolState.AddTransition(Transition.FindPlayer, StateID.ChaseState);
chaseState.AddTransition(Transition.LosePlayer, StateID.PatrolState);
}
private void Update()
{
fsm.UpdateFSM();
}
}
巡逻状态:
public class PatrolState : FSMState
{
private Transform enemy;
private Transform player;
private List<Transform> pathsList = new List<Transform>();
private int index = 0;
public float smooth = 3;
public PatrolState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.PatrolState;
enemy = GameObject.FindWithTag("Enemy").transform;
player = GameObject.FindWithTag("Player").transform;
Transform pathRoot = GameObject.Find("PatrolPoints").transform;
foreach (Transform pathPoint in pathRoot)
{
pathsList.Add(pathPoint);
}
}
public override void DoBeforeEntering()
{
Debug.Log("敌人开始巡逻了!");
}
public override void DoAfterLeaving()
{
Debug.Log("敌人发现玩家,结束巡逻!");
}
public override void Act()
{
Vector3 forward = pathsList[index].position - enemy.position;
forward = new Vector3(forward.x, 0, forward.z);
Quaternion targetQuaternion = Quaternion.LookRotation(forward, Vector3.up);
enemy.rotation = Quaternion.Slerp(enemy.rotation, targetQuaternion, Time.deltaTime * smooth);
enemy.Translate(Vector3.forward * Time.deltaTime * smooth);
if (Vector3.Distance(enemy.position, pathsList[index].position) < 2f)
{
index++;
index %= pathsList.Count;
}
}
public override void Reason()
{
if (Vector3.Distance(enemy.position, player.position) < 4)
{
fsm.PerformTransition(Transition.FindPlayer);
}
}
}
追逐状态:
public class ChaseState : FSMState
{
private Transform enemy;
private Transform player;
public float smooth = 3;
public float chaseSpeed = 5;
public ChaseState(FSMSystem fsm):base(fsm)
{
stateID = StateID.ChaseState;
enemy = GameObject.FindWithTag("Enemy").transform;
player = GameObject.FindWithTag("Player").transform;
}
public override void DoBeforeEntering()
{
Debug.Log("敌人开始追逐玩家了!");
}
public override void DoAfterLeaving()
{
Debug.Log("敌人跟丢玩家,继续巡逻了!");
}
public override void Act()
{
Vector3 forward = player.position - enemy.position;
forward = new Vector3(forward.x, 0, forward.z);
Quaternion targetQuaternion = Quaternion.LookRotation(forward, Vector3.up);
enemy.rotation = Quaternion.Slerp(enemy.rotation, targetQuaternion, Time.deltaTime * smooth);
enemy.Translate(Vector3.forward * Time.deltaTime * chaseSpeed);
}
public override void Reason()
{
if (Vector3.Distance(enemy.position, player.position) > 8)
{
fsm.PerformTransition(Transition.LosePlayer);
}
}
}
结果(红色敌人,蓝色玩家,绿色巡逻点)