unity3d中使用状态机

使用状态机的目的就是对角色复杂的行为逻辑代码进行解耦。在同一个act根据状态id不同,调用不同的类执行代码。

以一个士兵,有查找敌人、移动、攻击、胜利、自身死亡,这五种状态为例。


FMS_State_ShiBing.cs是状态机的父类。主要完成定义状态枚举和构建状态字段

using UnityEngine;
using System.Collections;
using System.Collections.Generic;


public enum Translate_ShiBing    //如果进入一个新的状态,需要一些触发,比如NPC看到了Player,由巡逻状态进入跟踪状态
{
    Unknow,
    ShiBing_FindObject,
    ShiBing_MoveToObjct,
    ShiBing_ActObject,
    ShiBing_Victory,
    ShiBing_Death
}

public enum StateID_ShiBing     //每个状态都应该有一个ID,作为识别改状态的标志
{
    Unknow,
    ShiBing_FindObject,
    ShiBing_MoveToObjct,
    ShiBing_ActObject,
    ShiBing_Victory,
    ShiBing_Death
}

public abstract class FMS_State_ShiBing 
{

    private StateID_ShiBing id;            //定一个状态ID作为变量来标识
    public StateID_ShiBing ID
    {
        set { id = value; }
        get { return id; }
    }

    private Dictionary<Translate_ShiBing, StateID_ShiBing> map = new Dictionary<Translate_ShiBing, StateID_ShiBing>();    //在某一状态下,事件引起了触发进入另一个状态
    // 于是我们定义了一个字典,存储的便是触发的类型,以及对应要进入的状态
    public void addDictionary(Translate_ShiBing tr, StateID_ShiBing id1)  //向字典里添加
    {
        if (tr == Translate_ShiBing.Unknow)
        {
            Debug.LogError("Null Trans is not allower to adding into");
            return;
        }

        if (ID == StateID_ShiBing.Unknow)
        {
            Debug.LogError("Null State id not ~~~");
            return;

        }
        if (map.ContainsKey(tr))              //NPC  任何时候都只能出于一种状态,所以一旦定义了一个触发的枚举类型,对应的只能是接下来的一种状态
        {
            Debug.LogError(id1.ToString() + "is already added to");
            return;
        }

        map.Add(tr, id1);
    }


    public void deleateDictionary(Translate_ShiBing tr) //删除字典里存储的一个状态
    {
        if (tr == Translate_ShiBing.Unknow)
        {
            Debug.LogError("TransNull is not allowed to delate");
            return;
        }
        if (map.ContainsKey(tr))
        {

            map.Remove(tr);
            return;
        }
        Debug.LogError(tr.ToString() + "are not exist");
    }

    public StateID_ShiBing GetOutState(Translate_ShiBing tr)  //由状态的触发枚举类型返回一个对应的状态类型
    {
        if (map.ContainsKey(tr))
        {
            // Debug.Log("Translate " + tr + "State" + map[tr]);
            return map[tr];


        }
        return StateID_ShiBing.Unknow;
    }

    public virtual void DoBeforeEnter() { }    //虚方法
    public virtual void DoBeforeMoveing() { }

  //  public abstract void Reason(); //  抽象方法
    public abstract void Act();



}



FMS_Machine_Manage_ShiBing.cs 这个类主要是实例化状态机,默认为第一个添加的状态。并且声明了当前状态允许跳转的状态清单

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class FMS_Machine_Manage_ShiBing : MonoBehaviour {


    private List<FMS_State_ShiBing> states;//存储所有状态的的List
    private FMS_State_ShiBing currentState;  //当前状态
    private StateID_ShiBing currentStateID;//当前状态ID


    public FMS_State_ShiBing CurrentState
    {
        set { currentState = value; }
        get { return currentState; }
    }

    public StateID_ShiBing CurrentStateID
    {
        set { currentStateID = value; }
        get { return currentStateID; }
    }

    public GameObject player;
    public GameObject[] path;
    public GameObject NPC;


    public FMS_Machine_Manage_ShiBing()
    {

        states = new List<FMS_State_ShiBing>();   //初始化
    }

    public void UpdateFunction()
    {

       // CurrentState.Reason();
        CurrentState.Act();
    }


    public void Revert()
    {


    }

    void Awake()
    {

        

    }


    public void MakeFMSMachine(GameObject MySelf)
    {
        FindObject_State_ShiBing FindObject = new FindObject_State_ShiBing(MySelf,this);
        FindObject.addDictionary(Translate_ShiBing.ShiBing_MoveToObjct, StateID_ShiBing.ShiBing_MoveToObjct);//放的是下个状态的,并且一次只能跳转到一个状态,但是好像也可以是多个
        FindObject.addDictionary(Translate_ShiBing.ShiBing_Victory, StateID_ShiBing.ShiBing_Victory);
        AddFmsState(FindObject);


        MoveToObject_State_ShiBing MoveToObject = new MoveToObject_State_ShiBing(MySelf,this);
        MoveToObject.addDictionary(Translate_ShiBing.ShiBing_ActObject, StateID_ShiBing.ShiBing_ActObject);
        MoveToObject.addDictionary(Translate_ShiBing.ShiBing_FindObject, StateID_ShiBing.ShiBing_FindObject);
        AddFmsState(MoveToObject);

        ActTarget_State_ShiBing ActTarget = new ActTarget_State_ShiBing(MySelf,this);
        ActTarget.addDictionary(Translate_ShiBing.ShiBing_FindObject, StateID_ShiBing.ShiBing_FindObject);
        AddFmsState(ActTarget);

        Victory_State_ShiBing VictoryState = new Victory_State_ShiBing(MySelf, this);
        VictoryState.addDictionary(Translate_ShiBing.ShiBing_FindObject, StateID_ShiBing.ShiBing_FindObject);
        AddFmsState(VictoryState);

    }

    public void AddFmsState(FMS_State_ShiBing s)//注册所有状态
    {
        if (s == null)
        {
            Debug.LogError(" Null reference is not allowed");
        }

        if (states.Count == 0)
        {
            states.Add(s);                   //设置默认状态(important);
            currentState = s;
            currentStateID = s.ID;
            return;
        }
        foreach (FMS_State_ShiBing state in states)
        {
            if (state == s)
            {
                Debug.LogError(s.ID.ToString() + "has already been added");
                return;
            }
        }
        states.Add(s);

    }

    public void delateFmsState(StateID_ShiBing id)
    {
        if (id == StateID_ShiBing.Unknow)
        {

            Debug.LogError("NullStateID is not allowed for a real state");

            return;
        }

        foreach (FMS_State_ShiBing state in states)
        {

            if (state.ID == id)
            {
                states.Remove(state);
                return;
            }

        }
    }


    public void changeState(Translate_ShiBing tr)           //更改状态
    {
        if (tr == Translate_ShiBing.Unknow)
        {
            Debug.LogError("NullTransition is not allowed for a real transition");
            return;
        }


        //if (currentState.GetOutState(tr) == StateID.NullState)
        //{
        //    Debug.Log("translate" + "          " + tr + "           " + currentState.GetOutState(tr)+"         "+CurrentStateID);
        //    Debug.LogError("1234");
        //    return;

        //}

        StateID_ShiBing id = CurrentState.GetOutState(tr);   //当前状态会进入的新的状态
        currentStateID = id;

        //    Debug.Log("Prives" + Prives.ID);
        foreach (FMS_State_ShiBing state in states)          //通过注册的所有状态,进行搜索来获取要进入的状态实例
        {
            if (currentStateID == state.ID)
            {
                CurrentState.DoBeforeMoveing();     //退出状态前,留下点什么,比如挥舞下手臂
                currentState = state;
                CurrentState.DoBeforeEnter();     //进入状态
                break;
            }

        }

    }

}


NPC_Control_ShiBing.cs这个类是用来挂在实际pre士兵身上的,其中UpDate实现了动作的心跳执行。

using UnityEngine;
using System.Collections;

public class NPC_Control_ShiBing : MonoBehaviour {

    //要攻击的目标
    public GameObject TargetObject;

    //自己
    public GameObject MySelfObject;

    public float Speed;//速度

    public float TurnSpeed;//旋转速度 

    public float ActRange;//火力范围

    public int damage;//攻击力



    public int HP;
    private bool IsDestroy = false;



    private FMS_Machine_Manage_ShiBing _FMS_Machine_Manage_ShiBing;
	// Use this for initialization
	void Start () {
        Speed = 50f;
        HP = 100;
        TurnSpeed = 0.3f;
        ActRange = 3.0f;
        damage = 25;

        _FMS_Machine_Manage_ShiBing = new FMS_Machine_Manage_ShiBing();

        _FMS_Machine_Manage_ShiBing.MakeFMSMachine(MySelfObject);
	}
	
	// Update is called once per frame
	void Update () {

        _FMS_Machine_Manage_ShiBing.UpdateFunction();
	}


    public bool GetDestroy()
    {
        return IsDestroy;
    }

    public void DestroySetTrue()
    {
        IsDestroy = true;
        Destroy(gameObject, 1.01f);//1.01秒后销毁炸弹
    }

}

FindObject_State_ShiBing.cs是具体实现功能的类,查找目标。找到后移动,找不到说明没有敌人了,进入胜利状态。找到目标后进入攻击状态

using UnityEngine;
using System.Collections;

public class FindObject_State_ShiBing : FMS_State_ShiBing//查找目标
{
    public GameObject[] aEnemy;//所有的敌人
    private GameObject _MySelf;//自己
    private NPC_Control_ShiBing _NPC_Control_ShiBing;//自己的其他属性
    private FMS_Machine_Manage_ShiBing _FMS_Machine_Manage_ShiBing;

    public FindObject_State_ShiBing(GameObject MySelf, FMS_Machine_Manage_ShiBing __FMS_Machine_Manage_ShiBing)
    {
        _FMS_Machine_Manage_ShiBing = __FMS_Machine_Manage_ShiBing;
        ID = StateID_ShiBing.ShiBing_FindObject;
        _MySelf = MySelf;//自己
        _NPC_Control_ShiBing = _MySelf.GetComponent("NPC_Control_ShiBing") as NPC_Control_ShiBing;//自己的属性
    }



    public override void Act()   //在该状态下改做点什么呢
    {
        //所有的敌人
        aEnemy = GameObject.FindGameObjectsWithTag("Enemy");

        //没有敌人就是胜利了
        //if (aJianZhu.Length == 0)
        //{
        //    _FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_Victory);
        //    return;
        //}

        //最近的敌人
        GameObject NearlyObject = null;

        float TempDistance = 999999.0f;

        bool AnyJianZhu = false;


        foreach (GameObject enemy_GameObject in aEnemy)
        {


            NPC_Control_Enemy _NPC_Control_Enemy = enemy_GameObject.GetComponent("NPC_Control_Enemy") as NPC_Control_Enemy;
            //如果被销毁,不计算当前敌人
            if (_NPC_Control_Enemy.GetDestroy())
            {
                continue;
            }
            //还有敌人
            AnyJianZhu = true;

            //计算最小距离,这里最好的做法是在对象初始化的时候得到一个减去值,然后每次得到Y的初始位置
            // JianZhu_GameObject.transform.position
            //_MySelf.transform.position
            float _distance = Vector3.Distance(enemy_GameObject.transform.position, _MySelf.transform.position);
            if (_distance < TempDistance)
            {
                TempDistance = _distance;
                NearlyObject = enemy_GameObject;
            }
        }

        if (AnyJianZhu)
        {
            _NPC_Control_ShiBing.TargetObject = NearlyObject;

            //找到目标后转向
            _FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_MoveToObjct);
        }
        else
        {
            _FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_Victory);
        }

        
    }


    public override void DoBeforeMoveing()
    {

    }

    public override void DoBeforeEnter()
    {

    }
	
}

ActTarget_State_ShiBing.cs攻击状态,持续对敌人造成伤害。如果敌人死亡,进入查找敌人状态


using UnityEngine;
using System.Collections;

public class ActTarget_State_ShiBing : FMS_State_ShiBing
{

    private GameObject _MySelf;//自己
    private NPC_Control_ShiBing _NPC_Control_ShiBing;//自己的其他属性
    private FMS_Machine_Manage_ShiBing _FMS_Machine_Manage_ShiBing;
    private GameObject _target;//目标敌人
    private NPC_Control_Enemy _NPC_Control_Enemy;//目标敌人的脚本
    private float m_fireTimer = 0.0f;

    public ActTarget_State_ShiBing(GameObject MySelf, FMS_Machine_Manage_ShiBing __FMS_Machine_Manage_ShiBing)
    {
        ID = StateID_ShiBing.ShiBing_ActObject;
        _FMS_Machine_Manage_ShiBing = __FMS_Machine_Manage_ShiBing;
        _MySelf = MySelf;//自己
        _NPC_Control_ShiBing = _MySelf.GetComponent("NPC_Control_ShiBing") as NPC_Control_ShiBing;//自己的属性

    }

    public override void Act()   //在该状态下改做点什么呢
    {
        // if (_target == null)
        // {
        _target = _NPC_Control_ShiBing.TargetObject;
        //  获取一个对象上的脚本
        _NPC_Control_Enemy = _target.GetComponent("NPC_Control_Enemy") as NPC_Control_Enemy;
        //TargetHP = JianZhuObject.HP;

        //  }



        if (_NPC_Control_Enemy.GetDestroy())
        {
            _FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_FindObject);
        }
        else
        {
            //每两秒开一次火
            m_fireTimer -= Time.deltaTime;
            if (m_fireTimer <= 0)
            {
                // JianZhuObject.Jian_BeiGongJiFX();//如果士兵死了要调用一下
                m_fireTimer = 2;
                _NPC_Control_Enemy.HP = _NPC_Control_Enemy.HP - _NPC_Control_ShiBing.damage;

                //如果敌人没血了,设置一个建筑的标志为摧毁,如果当前目标摧毁了,进入寻找状态

                if (_NPC_Control_Enemy.HP <= 0)
                {
                    _NPC_Control_Enemy.DestroySetTrue();
                }
            }
        }






    }

    public override void DoBeforeMoveing()
    {

    }

    public override void DoBeforeEnter()
    {

    }

}


其他的都同理我就不一一列出了。奉上源代码

http://pan.baidu.com/s/1i5wdVyX

博客不让上传附件太蛋疼了






  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值