• Unity 状态转化机器


    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    
    
    /**
    有限状态机系统基于Eric Dybsand的《游戏编程精粹》(Game Programming Gems)第3.1章 
     
    
     
     
    如何使用:
        1. 在状态转化机中的相应枚举类中,声明 transitions 和 states 的label.
     
        2. 写新的类集成 FSMState, 对于写的每一个类都要写上“转换条件” pairs (transition-state).
            这些转换条件表明 represent the state S2 the FSMSystem should be if while being on state S1, a
            transition T is fired and state S1 has a transition from it to S2. 切记这是确定的FSM. 
            不能有 transition 导致两种 states.
     
            “Reason” 方法用来决定transition 是否应该 fired.
           如果你感觉爽,可以把这个方法留空,在别的地方写代码用来 fire transitions.
     
           “Act” 用来写代码表现NPC在当前状态的 the actions.
           如果你感觉爽,可以把这个方法留空,在别的地方写代码用来  code for the actions.
    
     
        3. 创建 FSMSystem 类的实例,然后给他增加 states.
     
        4. 在你的 UPdate或 FixedUpdate 方法中调用 Reason 和 Act (或者其他你用来 firing transitions 和 making the NPCs
             behave 的方法).
     
        Asynchronous transitions from Unity Engine, 例如 OnTriggerEnter, SendMessage, 依然可用, 
        只需要调用你的 PerformTransition 方法 from your FSMSystem 实例 with the correct Transition 
        when the event occurs.
     
    */
    
    
    /// <summary>
    /// 在这个枚举类中声明 Transitions  的labels.
    /// 不要改变第一个 label ( NullTransition ) 因为 FSMSystem 类将会使用它.
    /// </summary>
    public enum Transition
    {
        NullTransition = 0, // 使用这个 transition 用来在你的系统中表示一个不存在的 transition
        SawPlayer,
        LostPlayer
    }
    
    /// <summary>
    /// 在这个枚举类中声明 States  的labels.
    /// 不要改变第一个 label( NullTransition ) 因为 FSMSystem 类将会使用它.
    /// </summary>
    public enum StateID
    {
        NullStateID = 0, // 使用这个 ID 用来在你的系统中表示一个不存在的 State    
        ChasingPlayer,
        FollowingPath
    }
    
    /// <summary>
    /// 这个类表示有限状态机中的 States.
    /// 每个状态有一个 Dictionary with pairs (transition-state) showing
    /// which state the FSM should be if a transition is fired while this state
    /// is the current state.
    ///  Reason 方法用来判断 transition 是否可行.
    ///  Act 方法用来表现NPC的动作.
    /// </summary>
    public abstract class FSMState
    {
        protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
        protected StateID stateID;
        public StateID ID { get { return stateID; } }
    
        public void AddTransition(Transition trans, StateID id)
        {
            // 检查trans和id是否为空
            if (trans == Transition.NullTransition)
            {
                Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
                return;
            }
    
            if (id == StateID.NullStateID)
            {
                Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
                return;
            }
    
            // 因为这是一个确定的 FSM,
            //   检查当前的 transition 是否已经在 the map
            if (map.ContainsKey(trans))
            {
                Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() + 
                    "Impossible to assign to another state");
                return;
            }
    
            map.Add(trans, id);
        }
    
        /// <summary>
        /// 这个方法用来用state's map中删除 transition-state对.
        /// 如果 transition 不在 state's map但中,打印一个错误信息.
        /// </summary>
        public void DeleteTransition(Transition trans)
        {
            // 检查trans是否为空
            if (trans == Transition.NullTransition)
            {
                Debug.LogError("FSMState ERROR: NullTransition is not allowed");
                return;
            }
    
            //删除钱检查 pair i是否在map当中
            if (map.ContainsKey(trans))
            {
                map.Remove(trans);
                return;
            }
            Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() + 
                " was not on the state's transition list");
        }
    
        /// <summary>
        /// 这个方法用来返回  new state the FSM should be if
        ///    this state receives a transition and 
        /// </summary>
        public StateID GetOutputState(Transition trans)
        {
            //检查 map 是否含有 transition
            if (map.ContainsKey(trans))
            {
                return map[trans];
            }
            return StateID.NullStateID;
        }
    
        /// <summary>
        /// 这个函数用来设置 the State condition before entering it.
        /// 在进入这个状态之前 FSMSystem 自动调用这个方法.
        /// </summary>
        public virtual void DoBeforeEntering() { }
    
        /// <summary>
        /// 这个方法用来做必须的工作,在状态转化之前例如重新设置变量
        /// 在进入到新状态之前 FSMSystem 会自动调用这个方法.
        /// </summary>
        public virtual void DoBeforeLeaving() { } 
    
        /// <summary>
        /// This method decides if the state should transition to another on its list
        /// NPC is a reference to the object that is controlled by this class
        /// </summary>
        public abstract void Reason(GameObject player, GameObject npc);
    
        /// <summary>
        /// This method controls the behavior of the NPC in the game World.
        /// Every action, movement or communication the NPC does should be placed here
        /// NPC is a reference to the object that is controlled by this class
        /// </summary>
        public abstract void Act(GameObject player, GameObject npc);
    
    } // class FSMState
    
    
    /// <summary>
    /// FSMSystem class represents the Finite State Machine class.
    ///  It has a List with the States the NPC has and methods to add,
    ///  delete a state, and to change the current state the Machine is on.
    /// </summary>
    public class FSMSystem
    {
        private List<FSMState> states;
    
        // The only way one can change the state of the FSM is by performing a transition
        // Don't change the CurrentState directly
        private StateID currentStateID;
        public StateID CurrentStateID { get { return currentStateID; } }
        private FSMState currentState;
        public FSMState CurrentState { get { return currentState; } }
    
        public FSMSystem()
        {
            states = new List<FSMState>();
        }
    
        /// <summary>
        /// This method places new states inside the FSM,
        /// or prints an ERROR message if the state was already inside the List.
        /// First state added is also the initial state.
        /// </summary>
        public void AddState(FSMState s)
        {
            // Check for Null reference before deleting
            if (s == null)
            {
                Debug.LogError("FSM ERROR: Null reference is not allowed");
            }
    
            // First State inserted is also the Initial state,
            //   the state the machine is in when the simulation begins
            if (states.Count == 0)
            {
                states.Add(s);
                currentState = s;
                currentStateID = s.ID;
                return;
            }
    
            // Add the state to the List if it's not inside it
            foreach (FSMState state in states)
            {
                if (state.ID == s.ID)
                {
                    Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() + 
                        " because state has already been added");
                    return;
                }
            }
            states.Add(s);
        }
    
        /// <summary>
        /// This method delete a state from the FSM List if it exists, 
        ///   or prints an ERROR message if the state was not on the List.
        /// </summary>
        public void DeleteState(StateID id)
        {
            // Check for NullState before deleting
            if (id == StateID.NullStateID)
            {
                Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
                return;
            }
    
            // Search the List and delete the state if it's inside it
            foreach (FSMState state in states)
            {
                if (state.ID == id)
                {
                    states.Remove(state);
                    return;
                }
            }
            Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() + 
                ". It was not on the list of states");
        }
    
        /// <summary>
        /// This method tries to change the state the FSM is in based on
        /// the current state and the transition passed. If current state
        ///  doesn't have a target state for the transition passed, 
        /// an ERROR message is printed.
        /// </summary>
        public void PerformTransition(Transition trans)
        {
            // Check for NullTransition before changing the current state
            if (trans == Transition.NullTransition)
            {
                Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
                return;
            }
    
            // Check if the currentState has the transition passed as argument
            StateID id = currentState.GetOutputState(trans);
            if (id == StateID.NullStateID)
            {
                Debug.LogError("FSM ERROR: State " + currentStateID.ToString() +  " does not have a target state " + 
                    " for transition " + trans.ToString());
                return;
            }
    
            // Update the currentStateID and currentState       
            currentStateID = id;
            foreach (FSMState state in states)
            {
                if (state.ID == currentStateID)
                {
                    // Do the post processing of the state before setting the new one
                    currentState.DoBeforeLeaving();
    
                    currentState = state;
    
                    // Reset the state to its desired condition before it can reason or act
                    currentState.DoBeforeEntering();
                    break;
                }
            }
    
        } // PerformTransition()
    
    } //class FSMSystem
    

    NPC 身上挂在的脚本

    using System;
    using System.Collections.Generic;
    using System.Text;
    using UnityEngine;
    
    [RequireComponent(typeof(Rigidbody))]
    public class NPCControl : MonoBehaviour
    {
        public GameObject player;
        public Transform[] path;
    
    
        private FSMSystem fsm;
    
        public void SetTransition(Transition t) { fsm.PerformTransition(t); }
    
        public void Start()
        {
            MakeFSM();
        }
    
        // The NPC 有两个状态 FollowPath 和 ChasePlayer
        private void MakeFSM()
        {
            FollowPathState follow = new FollowPathState(path);
            follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);
    
            ChasePlayerState chase = new ChasePlayerState();
            chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);
    
            fsm = new FSMSystem();
            fsm.AddState(follow);
            fsm.AddState(chase);
        }
    
    
        public void FixedUpdate()
        {
            fsm.CurrentState.Reason(player, gameObject);
            fsm.CurrentState.Act(player, gameObject);
        }
    
    
    }
    
    
    //跟随固定路径状态定义
    public class FollowPathState : FSMState
    {
        private int currentWayPoint;
        private Transform[] waypoints;
    
        //初始化
        public FollowPathState(Transform[] wp) 
        { 
            waypoints = wp;
            currentWayPoint = 0;
            stateID = StateID.FollowingPath;
        }
    
        public override void Reason(GameObject player, GameObject npc)
        {
            // 如果 Player 距离NPC小于2米
    		float distance = (player.transform.position-npc.transform.position).magnitude;
    		Debug.Log(distance);
    		if (distance<2)
            {
            	//转换条件
                npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);
            }
        }
    
        public override void Act(GameObject player, GameObject npc)
        {
            // Follow the path of waypoints
            // Find the direction of the current way point 
    
            Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
            Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;
    
            if (moveDir.magnitude < 1)
            {
                currentWayPoint++;
                if (currentWayPoint >= waypoints.Length)
                {
                    currentWayPoint = 0;
                }
            }
            else
            {
                vel = moveDir.normalized * 3;
            }
    
            // 应用速度
            npc.GetComponent<Rigidbody>().velocity = vel;
        }
    
    } 
    
    
    //追逐英雄状态
    
    public class ChasePlayerState : FSMState
    {
        //初始化函数
        public ChasePlayerState()
        {
            stateID = StateID.ChasingPlayer;
        }
    
        public override void Reason(GameObject player, GameObject npc)
        {
            // player已经距离 NPC超过30米, fire LostPlayer transition
            if (Vector3.Distance(npc.transform.position, player.transform.position) >= 30)
                npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
        }
    
        public override void Act(GameObject player, GameObject npc)
        {
            // Follow the path of waypoints
            //找到玩家 player 的方向        
            Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
            Vector3 moveDir = player.transform.position - npc.transform.position;
    
            // Rotate towards the waypoint
            vel = moveDir.normalized * 1;
    
            // 使用速度
            npc.GetComponent<Rigidbody>().velocity = vel;
        }
    
    }
    
  • 相关阅读:
    发送邮件时,报错:AttributeError: 'list' object has no attribute 'encode'
    快速统计字符出现次数
    vscode快捷键
    win7系统部署django项目
    记录一个小问题,django+Apache+win7,启动Apache后,打开网页,一直转圈圈,停不下来
    django ORM 按月分组统计
    哈希表的应用
    查找算法
    递归的应用
    递归
  • 原文地址:https://www.cnblogs.com/yufenghou/p/5918090.html
Copyright © 2020-2023  润新知