目前包括脚本:FSMState,PatrolState,ChaseState,AttackState / FSMSystem /Enemy
备注:其中 Animator.SetInteger("action",n) 是我自己给Monster做的动画状态,用于切换;
代码中多处判断是否为Null,是为了开发中避免极小概率事件的发生,防止游戏奔溃做的处理。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum Transition
{
NullTransition=0,
SeePlayer,
LostPlayer,
AttackPlayer
}
public enum StateID
{
NullStateID=0,
Patrol,
Chase,
Attack
}
public abstract class FSMState{
protected StateID stateID;
public StateID ID { get { return stateID; } }
protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
protected FSMSystem fsm;
public FSMState(FSMSystem fsm)
{
this.fsm = fsm;
Debug.Log(stateID);
}
public void AddTransition(Transition trans,StateID id)
{
if (trans == Transition.NullTransition)
{
Debug.LogError("不允许NullTransition");return;
}
if (id == StateID.NullStateID)
{
Debug.LogError("不允许NullStateID"); return;
}
if (map.ContainsKey(trans))
{
Debug.LogError("添加转换条件的时候," + trans + "已经存在于map中");return;
}
map.Add(trans, id);
}
public void DeleteTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError("不允许NullTransition"); return;
}
if (map.ContainsKey(trans)==false)
{
Debug.LogError("删除转换条件的时候," + trans + "不存在于map中"); return;
}
map.Remove(trans);
}
public StateID GetOutputState(Transition trans)
{
if (map.ContainsKey(trans))
{
return map[trans];
}
return StateID.NullStateID;
}
public virtual void DoBeforeEntering() { }
public virtual void DoAfterLeaving() { }
public abstract void Act(GameObject npc);
public abstract void Reason(GameObject npc);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FSMSystem {
private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();
private StateID currentStateID;
private FSMState currentState;
public void Update(GameObject npc)
{
currentState.Act(npc);
currentState.Reason(npc);
}
public void AddState(FSMState s)
{
if (s == null)
{
Debug.LogError("FSMState不能为空");return;
}
if (currentState == null)
{
currentState = s;
currentStateID = s.ID;
}
if (states.ContainsKey(s.ID))
{
Debug.LogError("状态" + s.ID + "已经存在,无法重复添加");return;
}
states.Add(s.ID, s);
}
public void DeleteState(StateID id)
{
if (id == StateID.NullStateID)
{
Debug.LogError("无法删除空状态");return;
}
if (states.ContainsKey(id) == false)
{
Debug.LogError("无法删除不存在的状态:" + id);return;
}
states.Remove(id);
}
public void PerformTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError("无法执行空的转换条件");return;
}
StateID id= currentState.GetOutputState(trans);
if (id == StateID.NullStateID)
{
Debug.LogWarning("当前状态" + currentStateID + "无法根据转换条件" + trans + "发生转换");return;
}
if (states.ContainsKey(id) == false)
{
Debug.LogError("在状态机里面不存在状态" + id + ",无法进行状态转换!");return;
}
FSMState state = states[id];
currentState.DoAfterLeaving();
currentState = state;
currentStateID = id;
currentState.DoBeforeEntering();
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChaseState : FSMState
{
private Transform playerTransform;
public ChaseState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Chase;
playerTransform = GameObject.Find("Boy").transform;
}
public override void Act(GameObject npc)
{
Animator ani = npc.transform.GetComponent<Animator>();
ani.SetInteger("action", 1);
npc.transform.LookAt(playerTransform.position);
npc.transform.Translate(Vector3.forward * 2 * Time.deltaTime);
}
public override void Reason(GameObject npc)
{
if (Vector3.Distance(playerTransform.position, npc.transform.position) > 10)
{
fsm.PerformTransition(Transition.LostPlayer);
}
if (Vector3.Distance(playerTransform.position,npc.transform.position)<1)
{
fsm.PerformTransition(Transition.AttackPlayer);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PatrolState : FSMState
{
private List<Transform> path = new List<Transform>();
private int index = 0;
private Transform playerTransform;
public PatrolState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Patrol;
Transform pathTransform = GameObject.Find("Path").transform;
Transform[] children= pathTransform.GetComponentsInChildren<Transform>();
foreach(Transform child in children)
{
if (child != pathTransform)
{
path.Add(child);
}
}
playerTransform = GameObject.Find("Boy").transform;
}
public override void Act(GameObject npc)
{
npc.transform.LookAt(path[index].position);
npc.transform.Translate(Vector3.forward * Time.deltaTime * 3);
Animator ani = npc.transform.GetComponent<Animator>();
ani.SetInteger("action", 1);
if (Vector3.Distance(npc.transform.position, path[index].position) < 1)
{
//index++;
//index %= path.Count;
//index = new System.Random().Next(path.Count);
index = UnityEngine.Random.Range(0,path.Count);
}
}
public override void Reason(GameObject npc)
{
if (Vector3.Distance(playerTransform.position, npc.transform.position) < 6)
{
fsm.PerformTransition(Transition.SeePlayer);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AttackState : FSMState {
private Transform playerTransform;
Animator m_aniMonster;
public AttackState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Attack;
playerTransform = GameObject.Find("Boy").transform;
}
public override void Act(GameObject npc)
{
m_aniMonster = npc.transform.GetComponent<Animator>();
m_aniMonster.SetInteger("action", 2);
npc.transform.LookAt(playerTransform.position);
}
public override void Reason(GameObject npc)
{
if (Vector3.Distance(playerTransform.position, npc.transform.position) > 1)
{
fsm.PerformTransition(Transition.SeePlayer);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour {
private FSMSystem fsm;
void Start () {
InitFSM();
}
void InitFSM()
{
fsm = new FSMSystem();
FSMState patrolState = new PatrolState(fsm);
patrolState.AddTransition(Transition.SeePlayer, StateID.Chase);
FSMState chaseState = new ChaseState(fsm);
chaseState.AddTransition(Transition.LostPlayer, StateID.Patrol);
chaseState.AddTransition(Transition.AttackPlayer, StateID.Attack);
FSMState attackState = new AttackState(fsm);
attackState.AddTransition(Transition.SeePlayer, StateID.Chase);
fsm.AddState(patrolState);
fsm.AddState(chaseState);
fsm.AddState(attackState);
}
void Update () {
fsm.Update(this.gameObject);
}
}