大家好,欢迎来到IT知识分享网。
1. 概述
在游戏中存在各种的“状态”,不仅是角色的行为状态,还有例如场景的开始、加载、游戏中等状态,此次采用一个小案例实现有限状态机——一个NPC一个角色player,当角色player不在NPC视野内时NPC在固定的四个点之间巡逻,当player在NPC视野内时,NPC会追逐player,丢失跟踪后继续巡逻,player的移动采用在scene中手动移动。
2. FSMState.cs
FSMState为定义状态的基类,但也将状态转换条件和状态ID枚举定义放在了此类中,Transition中存了所有的状态转换条件,StateID中定义了所有的状态。状态定了一些状态的基本信息,有状态ID以及储存转换条件和状态ID的字典,持有一个状态管理的对象……….
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 状态转换的条件
/// </summary>
public enum Transition
{
NullTransition=0,
SawPlayer,//看到主角
LostPlayer//看不到主角
}
/// <summary>
/// 状态ID,每个状态唯一表示,不重复,用于区分不同状态
/// </summary>
public enum StateID
{
NullStateID=0,
Patrol,//巡逻
Chase//追逐主角
}
public abstract class FSMState{
protected StateID stateID;
public StateID ID
{
get { return stateID; }
}
protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
public FSMSystem fsm;
public void AddTransition(Transition tran,StateID id)
{
if(tran==Transition.NullTransition||id==StateID.NullStateID)
{
Debug.LogError("添加的状态转换条件或状态Id不能为空!");
return;
}
if(map.ContainsKey(tran))
{
Debug.LogError("状态转换条件 " + id + " 已经存在" + tran);
return;
}
map.Add(tran, id);
}
public void DeleteTransition(Transition tran)
{
if(map.ContainsKey(tran)==false)
{
Debug.LogWarning("想要删除的状态转换条件 " + tran + " 不存在!");
return;
}
map.Remove(tran);
}
/// <summary>
/// 根据传过来的转换条件,判断是否可以转换,然后返回状态ID
/// </summary>
/// <param name="tran"></param>
/// <returns></returns>
public StateID GetOutputState(Transition tran)
{
if(map.ContainsKey(tran))
{
return map[tran];
}
return StateID.NullStateID;
}
//进入当前状态前需做的
public virtual void DoBeforeEntering() { }
public virtual void DoBeforeLeaving() { }
public abstract void DoUpdate();//在当前状态机出于当前状态的时候,会一直调用
}
3. ChaseState.cs
定义的追逐状态。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChaseState : FSMState {
private GameObject npc;
private GameObject player;
private Rigidbody npcRig;
public ChaseState(GameObject npc,GameObject player)
{
stateID = StateID.Chase;
this.player = player;
this.npc = npc;
npcRig = npc.GetComponent<Rigidbody>();
}
public override void DoBeforeEntering()
{
Debug.Log("进入 "+ID+" 状态");
}
public override void DoUpdate()
{
ChaseMove();
CheckTransition();
}
public void CheckTransition()
{
if(Vector3.Distance(npc.transform.position,player.transform.position)>2)
{
fsm.PerformTransition(Transition.LostPlayer);
}
}
public void ChaseMove()
{
npcRig.velocity = npc.transform.forward * 3;
Transform targetTrans = player.transform;
Vector3 targetPosition = targetTrans.position;
targetPosition.y = npc.transform.position.y;
npc.transform.LookAt(targetPosition);
}
}
4. PatrolState.cs
定义的巡逻状态。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PatrolState : FSMState {
private int targetWaypoint;
private Transform[] wayPoints;//巡逻的路径点
private GameObject npc;
private Rigidbody npcRig;
private GameObject player;
public PatrolState(Transform[] wp,GameObject npc,GameObject player)
{
stateID = StateID.Patrol;
wayPoints = wp;
this.npc = npc;
targetWaypoint = 0;
npcRig = npc.GetComponent<Rigidbody>();
this.player = player;
}
public override void DoBeforeEntering()
{
Debug.Log("进入 " + ID + " 状态");
}
public override void DoUpdate()
{
PatrolMove();
CheckTransition();
}
private void PatrolMove()
{
npcRig.velocity = npc.transform.forward * 3;
Transform targetTrans = wayPoints[targetWaypoint];
Vector3 targetPosition = targetTrans.position;
targetPosition.y = npc.transform.position.y;
npc.transform.LookAt(targetPosition);
if (Vector3.Distance(npc.transform.position, targetPosition) < 1)
{
targetWaypoint++;
targetWaypoint = targetWaypoint % wayPoints.Length;//使targetWaypoint循环
}
}
private void CheckTransition()
{
if(Vector3.Distance(player.transform.position,npc.transform.position)<2)
{
fsm.PerformTransition(Transition.SawPlayer);
}
}
}
5. FSMSystem.cs
对所有状态的管理,用字典储存了所有的状态,持有一个记录当前状态的变量。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 有限状态机管理类,有限状态机系统类
/// </summary>
public class FSMSystem {
//当前状态机下面所有状态
private Dictionary<StateID, FSMState> statesDict;
//状态机当前所处的状态
private FSMState currentState;
public FSMState CurrentState
{
get { return currentState; }
}
public FSMSystem()
{
statesDict = new Dictionary<StateID, FSMState>();
}
/// <summary>
/// 向状态机中添加状态
/// </summary>
/// <param name="state"></param>
public void AddState(FSMState state)
{
if(state==null)
{
Debug.LogError("要添加的状态为空!");
return;
}
if(statesDict.ContainsKey(state.ID))
{
Debug.LogError("要添加的状态已经存在!StateID: "+state.ID);
return;
}
state.fsm = this;
statesDict.Add(state.ID, state);
}
/// <summary>
/// 从状态机中删除状态
/// </summary>
/// <param name="state"></param>
public void DeleteState(FSMState state)
{
if (state == null)
{
Debug.LogError("要删除的状态为空!");
return;
}
if (statesDict.ContainsKey(state.ID)==false)
{
Debug.LogError("要删除的 "+state+" 状态不存在!");
return;
}
statesDict.Remove(state.ID);
}
/// <summary>
/// 根据所传的转换条件,控制状态之间的转换
/// </summary>
/// <param name="tran"></param>
public void PerformTransition(Transition tran)
{
if(tran==Transition.NullTransition)
{
Debug.LogError("空的转换条件不能转换!");
return;
}
StateID id = currentState.GetOutputState(tran);
if(id==StateID.NullStateID)
{
Debug.Log("没有符合条件的转换!");
return;
}
FSMState state;
statesDict.TryGetValue(id,out state);
currentState.DoBeforeLeaving();
currentState = state;
currentState.DoBeforeEntering();
}
/// <summary>
/// 设置默认状态,启动状态机
/// </summary>
/// <param name="id"></param>
public void Start(StateID id)
{
FSMState state;
bool isGet = statesDict.TryGetValue(id, out state);
if(isGet)
{
state.DoBeforeEntering();
currentState = state;
}
else
{
Debug.LogError("状态 " + id + " 不存在");
}
}
}
6. NPCController.cs
挂载于NPC上的脚本,且需要刚体组件,并将player的标签设置为Player,用于查找到player;且赋值四个路标位置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NPCController : MonoBehaviour {
private FSMSystem fsmSystem;
private GameObject player;
public Transform[] wayPoints;
void Start () {
player = GameObject.FindGameObjectWithTag("Player");
InitFSM();
}
/// <summary>
/// 初始化状态机
/// </summary>
void InitFSM () {
fsmSystem = new FSMSystem();
PatrolState patrolState = new PatrolState(wayPoints,this.gameObject,player);
patrolState.AddTransition(Transition.SawPlayer, StateID.Chase);
ChaseState chaseState = new ChaseState(this.gameObject,player);
chaseState.AddTransition(Transition.LostPlayer, StateID.Patrol);
fsmSystem.AddState(patrolState);
fsmSystem.AddState(chaseState);
fsmSystem.Start(StateID.Patrol);
}
private void Update()
{
fsmSystem.CurrentState.DoUpdate();
}
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/14735.html