基本解析:
比如说一个人有多种状态(站着,说话,跑,攻击等),而人想要执行的动作全部由大脑控制执行的,大脑支配人的走,跑,所以大脑就相当于人执行动作的一个工具(FSM),这个工具连接着各种行为,而每一种行为有一套独立的程序(包括声音,动画等),人给他一个条件,满足这个条件,那么FSM就会工作,当有新的指令时,FSM会根据新的指令执行动作。其实他的作用就是把复杂的状态集合简单化,调理会更清晰(后面的例子会解释清楚),他的应用是比较广泛,可以把他看作是一种设计思想,只要是满足上述这种结构的地方都可以应用,包括UI
…………
1.有现状态机,只被继承,不挂
========================================
using UnityEngine;
using System.Collections.Generic;
using System;
//有现状态机,继承,不挂
//功能简介:
//1.加入状态
//2.移除状态
//3.切换状态
public class StateMgr
{
// 存放人物所能拥有的所有行为状态,都是继承自基类,所以都是Base类型
private List<StateBase> mStateList = new List<StateBase> ();
// 当前人物所处的状态
public StateBase mCurrentState = null;
// 加入状态方法
public void AddState (StateBase state)
{
// 如果当前状态列表中没这个状态
if (!mStateList.Contains (state)) {
// 给列表中加入传进来的状态
mStateList.Add (state);
// 若果当前列表中状态只有一个
if (mStateList.Count == 1) {
// 当前的状态等于传入的状态(切换状态的方法)
ChangeState (state.GetType ());
}
}
}
// 移除状态的方法
public void RemoveState (StateBase state)
{
// 如果包含当期状态,才能移除掉状态
if (mStateList.Contains (state)) {
mStateList.Remove (state); //移除元素
}
}
// 切换状态
public void ChangeState (Type state)
{
// 寻找标记,用于检测是否找到制定状态类型
bool isFind = false;
// 已经在当前状态, 就不要在切换了
if (mCurrentState != null) {
if (state == mCurrentState.GetType ()) {
return;
}
}
//遍历当前列表
foreach (StateBase s in mStateList) {
// 如果s的Type类型等于传入的state标记 说明找到了 则要从当前状态离开
if (s.GetType () == state) {
//如果状态不等于空
if (mCurrentState != null) {
//离开状态
mCurrentState.OnExit ();
}
//将当前状态设置成新状态
mCurrentState = s;
//然后调用新状态的进入方法
mCurrentState.OnEnter ();
isFind = true;
break;
}
}
if (!isFind) {
Debug.LogError ("没有找到状态: " + state.Name);
}
}
//当当前状态不为空时,时刻保持在持续状态
public void Update ()
{
if (mCurrentState != null) {
mCurrentState.OnStay ();
}
}
}
2.所有状态的基类,全部继承自他
========================================
using UnityEngine;
using System.Collections;
//所有状态的基类,全部继承自他
//一般基类的基本写法,而且还抽象的,
public abstract class StateBase {
//控制的对象
public Transform transform = null;
/// <summary>
/// 构造方法
/// </summary>
/// <param name="transform"></param>
public StateBase(Transform transform)
{
this.transform = transform;
}
//状态进入的回调
public abstract void OnEnter();
//OnStay方法
public abstract void OnStay();
//状态离开的回调
public abstract void OnExit();
}
3.追踪敌人的类,继承自基类,脚本不挂
========================================
using UnityEngine;
using System.Collections;
//追踪敌人的类,继承自基类,脚本不挂
public class MoveTo : StateBase
{
/// <summary>
///构造方法 /// </summary>
/// <param name="transform">Transform.</param>
public MoveTo (Transform transform) : base (transform)
{
this.transform = transform;
}
//定义一个动画集合
Animator m_ant;
//定义一个导航组建
UnityEngine.AI.NavMeshAgent m_nav;
//导航的目标跟踪点
Transform m_finishTarget;
//进入状态的回调
public override void OnEnter ()
{
//实例化相应的组件
m_ant = transform.GetComponent<Animator> ();
m_nav = transform.GetComponent<UnityEngine.AI.NavMeshAgent> ();
m_finishTarget = transform.GetComponent<SoldierController> ().m_finishTarget;
//开启导航
m_nav.Resume ();
//更改状态机的参数,播放动画
m_ant.SetBool ("Run", true);
//设置目标位置
m_nav.SetDestination (m_finishTarget.position);
}
//持续状态的回调
public override void OnStay ()
{
}
//离开状态的回调
public override void OnExit ()
{
//导航停止
m_nav.Stop ();
//还是通过更改状态机对应跑的动画的参数,改成false关闭动画
m_ant.SetBool ("Run", false);
}
}
4.追踪的状态
========================================
using UnityEngine;
using System.Collections;
//追踪的状态
public class FollowUp : StateBase
{
/// <summary>
/// 构造方法 /// </summary>
/// <param name="transform">Transform.</param>
public FollowUp (Transform transform) : base (transform)
{
this.transform = transform;
}
Animator m_ant;
UnityEngine.AI.NavMeshAgent m_nav;
Transform m_target;
public override void OnEnter ()
{
m_ant = transform.GetComponent<Animator> ();
m_nav = transform.GetComponent<UnityEngine.AI.NavMeshAgent> ();
m_target = transform.GetComponent<SoldierController> ().m_target;
//导航
m_nav.Resume ();
//动画
m_ant.SetBool ("Run", true);
//设置目标位置_最终目标
m_nav.SetDestination (m_target.position);
}
public override void OnStay ()
{
//如果追踪目标不等于空,则继续追踪,导航的目标是攻击目标
if (m_target != null) {
m_nav.SetDestination (m_target.position);
}
}
public override void OnExit ()
{
m_nav.Stop ();
m_ant.SetBool ("Run", false);
}
}
5.攻击状态
========================================
using UnityEngine;
using System.Collections;
//攻击状态
public class Attack : StateBase
{
public Attack (Transform transform) : base (transform)
{
this.transform = transform;
}
Animator m_ant;
Transform m_target;
public override void OnEnter ()
{
m_ant = transform.GetComponent<Animator> ();
//得到攻击目标
m_target = transform.GetComponent<SoldierController> ().m_target;
//动画
m_ant.SetBool ("Attack", true);
}
public override void OnStay ()
{
if (m_target != null) {
transform.LookAt (m_target);
//调用对方的受伤害的方法
//给对方(m_target)添加受伤害的Buffer
//检测对方是否死亡,更新攻击目标
}
}
public override void OnExit ()
{
m_ant.SetBool ("Attack", false);
}
}
6.死的状态
========================================
using UnityEngine;
using System.Collections;
//死的状态
public class Die : StateBase
{
public Die (Transform transform) : base (transform)
{
this.transform = transform;
}
Animator m_ant;
public override void OnEnter ()
{
m_ant = transform.GetComponent<Animator> ();
//动画
m_ant.SetBool ("Die", true);
}
public override void OnStay ()
{
//动画状态信息
AnimatorStateInfo info = m_ant.GetCurrentAnimatorStateInfo (0);
if (info.IsName ("Die")) {
// info.normalizedTime,该状态的归一化时间
//整数部分是时间状态的已循环数。小数部分是当前循环的百分比进程(0-1)。
if (info.normalizedTime > 1) {
//动画播放完成一遍
//等待一会销毁
GameObject.Destroy (transform.gameObject, 2f);
}
}
}
public override void OnExit ()
{
m_ant.SetBool ("Die", false);
}
}
7.状态控制器,
挂给要控制的游戏对象,游戏对象身上需要添加触发器和刚体组件
========================================
using UnityEngine;
using System.Collections;
//状态控制器
//对人物应该处于什么状态,进行判断并执行
public class SoldierController : MonoBehaviour
{
//声明一个有限状态机
StateMgr m_stateMgr;
//攻击目标
[HideInInspector]//隐藏公开变量
public Transform m_target;
//最终目标
public Transform m_finishTarget;
//血量
public float m_hp = 100f;
void Start ()
{
//实例化有限状态机的脚本
m_stateMgr = new StateMgr ();
//实例化各种状态,添加到状态列表里
MoveTo moveTo = new MoveTo (transform);
m_stateMgr.AddState (moveTo);
FollowUp followUp = new FollowUp (transform);
m_stateMgr.AddState (followUp);
Attack attack = new Attack (transform);
m_stateMgr.AddState (attack);
Die die = new Die (transform);
m_stateMgr.AddState (die);
//默认状态(向最终目标移动)
m_stateMgr.ChangeState (typeof(MoveTo));
}
//end_Start
void Update ()
{
//没真调用一下,m_stateMgr(有现状态机)里面的Uptate方法,始终保持持续状态
m_stateMgr.Update ();
//===========下面是对出于某种状态的判断==========
if (m_hp <= 0) {
//血量小于0时, 切换到死亡状态,返回,
m_stateMgr.ChangeState (typeof(Die));
return;
}
//如果没有攻击目标
if (m_target == null) {
//切换MoveTo
m_stateMgr.ChangeState (typeof(MoveTo));
} else {//有攻击目标,判断和攻击目标的距离,如果距离小于2.5f的话,进入攻击状态,如果大于2.5f,那么进入追踪状态
if (Vector3.Distance (transform.position, m_target.position) > 2.5f) {
//进入追踪状态
m_stateMgr.ChangeState (typeof(FollowUp));
} else {
//进入攻击状态
m_stateMgr.ChangeState (typeof(Attack));
}
}
}
//end_Update
//对攻击目标的检测,采用触发器
void OnTriggerEnter (Collider other)
{
//检测出发的人物身上是否有Player标签,以此来判断攻击目标
if (other.tag.Equals ("Player") && m_target == null) {
m_target = other.transform;
}
}
//如果攻击目标离开了检测范围,那么攻击取消,既m_terget为空
void OnTriggerExit (Collider other)
{
if (other.tag.Equals ("Player")) {
m_target = null;
}
}
}
可能有些朋友看到这会问,几个简单的动画就要写这么多脚本,太麻烦了,确实想想是很麻烦,但是如果状态很多的话,就会发现这种方法很实用了,至少不需要在状态机里链那些很复杂的线了。
回到Unity场景:
状态机简单连线
添加标签:
下次将介绍Buff系统……………………