zoukankan      html  css  js  c++  java
  • Unity的状态机设计

    Unity的状态机设计

     

           本人新手,随便写写而已。

           本文通过一个实例实现了在Unity下的有限状态机(参考了wiki上的教程)。

          有限状态机是一个设备具有有限数量的状态,他可以在任何时间根据输入进行操作,使得一个状态进入另个一个状态。一个状态机在任何瞬间只能处于一种状态。

          具体可以参考  状态设计模式。

          本例是这样一种状态装换。

         游戏人物NPC在空闲时处于巡逻状态,当看见Player在视野内的时候,转入转入追逐Player状态;一旦和Player距离拉大,便返回巡逻状态。

    复制代码
    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);
    
    }
    复制代码

    随后根据这个基类派生出巡逻类,和追逐类。

    复制代码
    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();
    
             
    
            
         }
    
      
             
    }
    复制代码
  • 相关阅读:
    [报错]编译报错:clang: error: linker command failed with exit code 1及duplicate symbol xxxx in错误解决方法之一
    修改UISearBar的文字颜色,placehoder颜色及输入框颜色
    designated initializer和secondary initializer是什么?
    设置UIButton的文字居右显示 去掉点击默认置灰效果
    设置UITextField的placeholder的颜色
    Xcode8和iOS10的适配问题
    [转载]做一个 App 前需要考虑的几件事
    git 常用命令行整理
    Xcode中使用GitHub详解
    截取字符串
  • 原文地址:https://www.cnblogs.com/xiao-wei-wei/p/3526442.html
Copyright © 2011-2022 走看看