Unity的狀態機設計

Unity的狀態機設計

 

       本人新手,隨便寫寫而已。html

       本文經過一個實例實現了在Unity下的有限狀態機(參考了wiki上的教程)。設計模式

      有限狀態機是一個設備具備有限數量的狀態,他能夠在任什麼時候間根據輸入進行操做,使得一個狀態進入另個一個狀態。一個狀態機在任何瞬間只能處於一種狀態。app

      具體能夠參考  狀態設計模式。ide

      本例是這樣一種狀態裝換。post

     遊戲人物NPC在空閒時處於巡邏狀態,當看見Player在視野內的時候,轉入轉入追逐Player狀態;一旦和Player距離拉大,便返回巡邏狀態。url

複製代碼
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
//不管是NPC仍是Player都有不少狀態,在狀態切換的時候爲了使得程序顯得清晰明瞭,採用了狀態機制
//首先實際狀態都是繼承自某個狀態抽象類的,這個抽象類定義了進入,退出一個狀態虛方法
//定義了檢測環境是否發生狀態改變,以及在該狀態下執行的動做的抽象方法
// 該實例主要涉及到一個NPC在指定的位置進行巡邏,當看到Playeer的時候切換狀態進入追逐Player狀態

public enum Translate    //若是進入一個新的狀態,須要一些觸發,好比NPC看到了Player,由巡邏狀態進入跟蹤狀態
{                        
    NullTrans,
    SeePlayer,
    LosePlayer
   
}

public enum StateID     //每一個狀態都應該有一個ID,做爲識別改狀態的標誌
{ 
    NullState,
    Chaseingplayer,
    FollowPath
 
}

public abstract class FMS_State    //定一個抽象類
{
    private StateID id;            //定一個狀態ID做爲變量來標識
    public StateID ID
    {
        set { id = value; }
        get { return id; }
    }

    private Dictionary<Translate, StateID> map = new Dictionary<Translate, StateID>();    //在某一狀態下,事件引發了觸發進入另外一個狀態
// 因而咱們定義了一個字典,存儲的即是觸發的類型,以及對應要進入的狀態
    public void addDictionary(Translate tr, StateID id1)  //向字典裏添加
    {
        if (tr == Translate.NullTrans)
        {
            Debug.LogError("Null Trans is not allower to adding into");
            return;
        }

        if (ID == StateID.NullState)
        {
            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 tr) //刪除字典裏存儲的一個狀態
    {
        if (tr == Translate.NullTrans)
        {
            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 GetOutState(Translate tr)  //由狀態的觸發枚舉類型返回一個對應的狀態類型
    {
        if (map.ContainsKey(tr))
        {
           // Debug.Log("Translate " + tr + "State" + map[tr]);
            return map[tr];

           
        }
        return StateID.NullState;
    }

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

    public abstract void Reason(GameObject player, GameObject NPC); //  抽象方法
    public abstract void Act(GameObject player, GameObject NPC);

}
複製代碼

隨後根據這個基類派生出巡邏類,和追逐類。spa

複製代碼
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
public class Follow_State:FMS_State  //派生出一個巡邏的類
{
    private GameObject[] waypoints;    //在這些位置巡邏
    private int currentWayPoint;    
    public Follow_State(GameObject[] ob)
    {
          waypoints= ob;  
          currentWayPoint = 0;
          ID = StateID.FollowPath;        //當前狀態的ID
    }

    public override void Reason(GameObject player, GameObject NPC)    //與環境交互,來判斷是否須要狀態切換
    {
        RaycastHit hit;
        if(Physics.Raycast(NPC.transform.position,NPC.transform.forward,out hit ,15))
        {
        if(hit.transform.tag=="Player")
        {
         FMS_Machine_Manage.GetInstance.changeState(Translate.SeePlayer);
        }
        }

    }

    public override void Act(GameObject player, GameObject NPC)   //在該狀態下改作點什麼呢
    {
        Vector3 vel = NPC.rigidbody.velocity;
        Vector3 dir =waypoints[currentWayPoint].transform.position- NPC.transform.position;

        if(dir.magnitude<1)
        {
            currentWayPoint++;

           // Debug.Log("currentwappoint " + currentWayPoint);
            if(currentWayPoint>=waypoints.Length)
            {
                currentWayPoint = 0;
            }
        }
        else
        {
            vel = dir.normalized*10;
            NPC.transform.rotation = Quaternion.Lerp(NPC.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 5);
            NPC.transform.eulerAngles = new Vector3(0, NPC.transform.localEulerAngles.y, 0);
        }

        NPC.rigidbody.velocity = vel;

    }

    public override void DoBeforeMoveing()
    {
       
    }

    public override void DoBeforeEnter()
    {
      
    }
}
複製代碼
複製代碼
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

public class Chaseing_State : FMS_State {   //派生出了一個追逐類

    public Chaseing_State()
    {
        ID = StateID.Chaseingplayer;   //定義  狀態ID
    
    }
    public override void Reason(GameObject player, GameObject NPC)
    {



       if(Vector3.Distance(player.transform.position,NPC.transform.position)>=5)
       {
         FMS_Machine_Manage.GetInstance.changeState(Translate.LosePlayer);
       }
    }

    public override void Act(GameObject player, GameObject NPC)
    {
        Vector3 vel = NPC.rigidbody.velocity;

        Vector3 moveDir = player.transform.position - NPC.transform.position;

        NPC.transform.rotation = Quaternion.Lerp(NPC.transform.rotation, Quaternion.LookRotation(moveDir), Time.deltaTime * 5);

        NPC.transform.eulerAngles = new Vector3(0, NPC.transform.eulerAngles.y, 0);
        vel = moveDir * 3;
        NPC.rigidbody.velocity = vel;
    }    
    }
複製代碼

一般對狀態的管理,咱們會創建一個stateMachine來管理。設計

複製代碼
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
//狀態管理類,對狀態進行管理

public class FMS_Machine_Manage:MonoBehaviour
{
    private List<FMS_State> states;//存儲全部狀態的的List
    private FMS_State currentState;  //當前狀態
    private StateID currentStateID;//當前狀態ID


    public FMS_State CurrentState {
        set { currentState = value; }
        get { return currentState; } }
 
    public StateID CurrentStateID {
        set { currentStateID = value; }
        get { return currentStateID; } }

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


    private static  FMS_Machine_Manage instance;

    public static  FMS_Machine_Manage GetInstance
    {
        get 
        {
            if (instance == null)
            {
               // instance = new FMS_Machine_Manage(); 
                GameObject n = new GameObject();
                n.name = "FMS_Machine_Manage"; 
                instance = n.AddComponent<FMS_Machine_Manage>() as FMS_Machine_Manage;
            }
         
                return instance;
        }

    }

    public void UpdateFunction()
    {
       
            CurrentState.Reason(player, NPC);
            CurrentState.Act(player, NPC);
        }
      

    public void Revert()
    { 
    
    
    }

    void Awake()
    {

        states = new List<FMS_State>();   //初始化
        NPC = GameObject.FindGameObjectWithTag("NPC");
      
        player = GameObject.FindGameObjectWithTag("Player");

        path = GameObject.FindGameObjectsWithTag("path");

    
    
    }


    public void MakeFMSMachine()
    {
        Follow_State follow = new Follow_State(path);
        follow.addDictionary(Translate.SeePlayer, StateID.Chaseingplayer);

        Chaseing_State chase = new Chaseing_State();
        chase.addDictionary(Translate.LosePlayer, StateID.FollowPath);

     
       GetInstance. AddFmsState(follow);

       GetInstance. AddFmsState(chase);
    
    }
    public void AddFmsState(FMS_State 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 state in states)
        {
            if (state == s)
            {
                Debug.LogError(s.ID.ToString() + "has already been added");
                return;
            }
        }
        states.Add(s);

    }

    public void delateFmsState(StateID id)
    {
        if (id == StateID.NullState)
        {

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

            return;
        }

        foreach (FMS_State state in states)
        {

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

        }
    }


    public void changeState(Translate tr)           //更改狀態
    {
        if (tr == Translate.NullTrans)
        {
            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 id = CurrentState.GetOutState(tr);   //當前狀態會進入的新的狀態
        currentStateID = id;

    //    Debug.Log("Prives" + Prives.ID);
        foreach (FMS_State state in states)          //經過註冊的全部狀態,進行搜索來獲取要進入的狀態實例
        {
            if (currentStateID == state.ID)
            {
                CurrentState.DoBeforeMoveing();     //退出狀態前,留下點什麼,好比揮舞下手臂
                currentState = state;
                CurrentState.DoBeforeEnter();     //進入狀態
                break;
            }

        }

    }




}
複製代碼
複製代碼
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;


public class NPC_Control : MonoBehaviour {
    public void Start()
    {

   

        FMS_Machine_Manage.GetInstance.MakeFMSMachine();
        
    }

    public void FixedUpdate()
     {

   


         FMS_Machine_Manage.GetInstance.UpdateFunction();

         

        
     }

  
         
}
複製代碼
相關文章
相關標籤/搜索