FSM状态机

基本解析:
比如说一个人有多种状态(站着,说话,跑,攻击等),而人想要执行的动作全部由大脑控制执行的,大脑支配人的走,跑,所以大脑就相当于人执行动作的一个工具(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系统……………………





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值