精华内容
下载资源
问答
  • Unity FSM有限状态机

    2020-05-22 22:36:51
    https://blog.csdn.net/a592733740/article/details/106292148
  • 有限状态机FSM

    2018-09-19 04:52:25
    Unity C# 《有限状态机》的用法教程详解Demo 助力快速理解 FSM 有限状态机,完成游戏状态的切换 教程地址:https://blog.csdn.net/ChinarCSDN/article/details/82263126
  • Unity FSM 有限状态机

    2016-12-15 23:03:00
    翻译了一下unity wiki上对于有限状态机的案例,等有空时在详细写一下。在场景中添加两个游戏物体,一个为玩家并修改其Tag为Player,另一个为NPC为其添加NPCControl脚本,并为其将玩家角色和路径添加上去。(该案例...

    翻译了一下unity wiki上对于有限状态机的案例,等有空时在详细写一下。在场景中添加两个游戏物体,一个为玩家并修改其Tag为Player,另一个为NPC为其添加NPCControl脚本,并为其将玩家角色和路径添加上去。(该案例利用状态机简单的实现了一个NPC的简单AI---巡逻---看到玩家----追逐玩家----丢失玩家----巡逻

    效果:

     

    状态机:

      1 using System;
      2 using System.Collections;
      3 using System.Collections.Generic;
      4 using UnityEngine;
      5 
      6 /**
      7 A Finite State Machine System based on Chapter 3.1 of Game Programming Gems 1 by Eric Dybsand
      8  
      9 Written by Roberto Cezar Bianchini, July 2010
     10  
     11  
     12 How to use:
     13     1. Place the labels for the transitions and the states of the Finite State System
     14         in the corresponding enums.
     15  
     16     2. Write new class(es) inheriting from FSMState and fill each one with pairs (transition-state).
     17         These pairs represent the state S2 the FSMSystem should be if while being on state S1, a
     18         transition T is fired and state S1 has a transition from it to S2. Remember this is a Deterministic(确定的) FSM. 
     19         You can't have one transition leading to two different states.
     20  
     21        Method Reason is used to determine which transition should be fired.
     22        You can write the code to fire transitions in another place, and leave this method empty if you
     23        feel it's more appropriate 合适 to your project.
     24  
     25        Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
     26        You can write the code for the actions in another place, and leave this method empty if you
     27        feel it's more appropriate to your project.
     28  
     29     3. Create an instance of FSMSystem class and add the states to it.
     30  
     31     4. Call Reason and Act (or whichever methods you have for firing transitions and making the NPCs
     32          behave in your game) from your Update or FixedUpdate methods.
     33  
     34     Asynchronous transitions from Unity Engine, like OnTriggerEnter, SendMessage, can also be used, 
     35     just call the Method PerformTransition from your FSMSystem instance with the correct Transition 
     36     when the event occurs 重现.
     37  
     38  
     39  
     40 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
     41 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
     42 AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
     43 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
     44 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     45 */
     46 
     47 
     48 /// <summary>
     49 /// Place the labels for the Transitions in this enum.
     50 /// Don't change the first label, NullTransition as FSMSystem class uses it.
     51 /// 为过渡加入枚举标签
     52 /// 不要修改第一个标签,NullTransition会在FSMSytem类中使用
     53 /// </summary>
     54 public enum Transition
     55 {
     56     NullTransition = 0, // Use this transition to represent a non-existing transition in your system
     57     //用这个过度来代表你的系统中不存在的状态
     58     SawPlayer,//这里配合NPCControl添加两个NPC的过渡
     59     LostPlayer,
     60 }
     61 
     62 /// <summary>
     63 /// Place the labels for the States in this enum.
     64 /// Don't change the first label, NullStateID as FSMSystem class uses it.
     65 /// 为状态加入枚举标签
     66 /// 不要修改第一个标签,NullStateID会在FSMSytem中使用 
     67 /// </summary>
     68 public enum StateID
     69 {
     70     NullStateID = 0, // Use this ID to represent a non-existing State in your syste
     71     //使用这个ID来代表你系统中不存在的状态ID    
     72     ChasingPlayer,//这里配合NPCControl添加两个状态
     73     FollowingPath,
     74 
     75 }
     76 
     77 /// <summary>
     78 /// This class represents the States in the Finite State System.
     79 /// Each state has a Dictionary with pairs (transition-state) showing
     80 /// which state the FSM should be if a transition is fired while this state
     81 /// is the current state.
     82 /// Method Reason is used to determine which transition should be fired .
     83 /// Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
     84 /// 这个类代表状态在有限状态机系统中
     85 /// 每个状态都有一个由一对搭档(过渡-状态)组成的字典来表示当前状态下如果一个过渡被触发状态机会进入那个状态
     86 /// Reason方法被用来决定那个过渡会被触发
     87 /// Act方法来表现NPC出在当前状态的行为
     88 /// </summary>
     89 public abstract class FSMState
     90 {
     91     protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
     92     protected StateID stateID;
     93     public StateID ID { get { return stateID; } }
     94 
     95     public void AddTransition(Transition trans, StateID id)
     96     {
     97         // Check if anyone of the args is invalid
     98         //验证每个参数是否合法
     99         if (trans == Transition.NullTransition)
    100         {
    101             Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
    102             return;
    103         }
    104 
    105         if (id == StateID.NullStateID)
    106         {
    107             Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
    108             return;
    109         }
    110 
    111         // Since this is a Deterministic FSM,
    112         //   check if the current transition was already inside the map
    113         //要知道这是一个确定的有限状态机(每个状态后金对应一种状态,而不能产生分支)
    114         //检查当前的过渡是否已经在地图字典中了
    115         if (map.ContainsKey(trans))
    116         {
    117             Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
    118                            "Impossible to assign to another state");
    119             return;
    120         }
    121 
    122         map.Add(trans, id);
    123     }
    124 
    125     /// <summary>
    126     /// This method deletes a pair transition-state from this state's map.
    127     /// If the transition was not inside the state's map, an ERROR message is printed.
    128     /// 这个方法用来在状态地图中删除transition-state对儿
    129     /// 如果过渡并不存在于状态地图中,那么将会打印出一个错误
    130     /// </summary>
    131     public void DeleteTransition(Transition trans)
    132     {
    133         // Check for NullTransition
    134         if (trans == Transition.NullTransition)
    135         {
    136             Debug.LogError("FSMState ERROR: NullTransition is not allowed");
    137             return;
    138         }
    139 
    140         // Check if the pair is inside the map before deleting
    141         //再删除之前确认该键值对是否存在于状态地图中(键值对集合)
    142         if (map.ContainsKey(trans))
    143         {
    144             map.Remove(trans);
    145             return;
    146         }
    147         Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
    148                        " was not on the state's transition list");
    149     }
    150 
    151     /// <summary>
    152     /// This method returns the new state the FSM should be if
    153     ///    this state receives a transition and 
    154     /// 该方法在该状态接收到一个过渡时返回状态机需要成为的新状态
    155     /// </summary>
    156     public StateID GetOutputState(Transition trans)
    157     {
    158         // Check if the map has this transition
    159         if (map.ContainsKey(trans))
    160         {
    161             return map[trans];
    162         }
    163         return StateID.NullStateID;
    164     }
    165 
    166     /// <summary>
    167     /// This method is used to set up the State condition before entering it.
    168     /// It is called automatically by the FSMSystem class before assigning it
    169     /// to the current state.
    170     /// 这个方法用来设立进入状态前的条件
    171     /// 在状态机分配它到当前状态之前他会被自动调用
    172     /// </summary>
    173     public virtual void DoBeforeEntering() { }
    174 
    175     /// <summary>
    176     /// This method is used to make anything necessary, as reseting variables
    177     /// before the FSMSystem changes to another one. It is called automatically
    178     /// by the FSMSystem before changing to a new state.
    179     /// 这个方法用来让一切都是必要的,例如在有限状态机变化的另一个时重置变量。
    180     /// 在状态机切换到新的状态之前它会被自动调用。
    181     /// </summary>
    182     public virtual void DoBeforeLeaving() { }
    183 
    184     /// <summary>
    185     /// This method decides if the state should transition to another on its list
    186     /// 动机-->这个方法用来决定当前状态是否需要过渡到列表中的其他状态
    187     /// NPC is a reference to the object that is controlled by this class
    188     /// NPC是被该类约束下对象的一个引用
    189     /// </summary>
    190     public abstract void Reason(GameObject player, GameObject npc);
    191 
    192     /// <summary>
    193     /// This method controls the behavior of the NPC in the game World.
    194     /// 表现-->该方法用来控制NPC在游戏世界中的行为
    195     /// Every action, movement or communication the NPC does should be placed here
    196     /// NPC的任何动作,移动或者交流都需要防止在这儿
    197     /// NPC is a reference to the object that is controlled by this class
    198     /// NPC是被该类约束下对象的一个引用
    199     /// </summary>
    200     public abstract void Act(GameObject player, GameObject npc);
    201 
    202 } // class FSMState
    203 
    204 
    205 /// <summary>
    206 /// FSMSystem class represents the Finite State Machine class.
    207 ///  It has a List with the States the NPC has and methods to add,
    208 ///  delete a state, and to change the current state the Machine is on.
    209 /// 该类便是有限状态机类
    210 /// 它持有者NPC的状态集合并且有添加,删除状态的方法,以及改变当前正在执行的状态
    211 /// </summary>
    212 public class FSMSystem
    213 {
    214     private List<FSMState> states;
    215 
    216     // The only way one can change the state of the FSM is by performing a transition
    217     // Don't change the CurrentState directly
    218     //通过预装一个过渡的唯一方式来盖面状态机的状态
    219     //不要直接改变当前的状态
    220     private StateID currentStateID;
    221     public StateID CurrentStateID { get { return currentStateID; } }
    222     private FSMState currentState;
    223     public FSMState CurrentState { get { return currentState; } }
    224 
    225     public FSMSystem()
    226     {
    227         states = new List<FSMState>();
    228     }
    229     /// <summary>
    230     /// This method places new states inside the FSM,
    231     /// or prints an ERROR message if the state was already inside the List.
    232     /// First state added is also the initial state.
    233     /// 这个方法为有限状态机置入新的状态
    234     /// 或者在该状态已经存在于列表中时打印错误信息
    235     /// 第一个添加的状态也是最初的状态!
    236     /// </summary>
    237     public void AddState(FSMState s)
    238     {
    239         // Check for Null reference before deleting
    240         //在添加前检测空引用
    241         if (s == null)
    242         {
    243             Debug.LogError("FSM ERROR: Null reference is not allowed");
    244         }
    245 
    246        
    247 
    248         // First State inserted is also the Initial state,
    249         //   the state the machine is in when the   begins
    250         //被装在的第一个状态也是初始状态
    251         //这个状态便是状态机开始时的状态
    252         if (states.Count == 0)
    253         {
    254             states.Add(s);
    255             currentState = s;
    256             currentStateID = s.ID;
    257             return;
    258         }
    259 
    260         // Add the state to the List if it's not inside it
    261         //如果该状态未被添加过,则加入集合
    262         foreach (FSMState state in states)
    263         {
    264             if (state.ID == s.ID)
    265             {
    266                 Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
    267                                " because state has already been added");
    268                 return;
    269             }
    270         }
    271         states.Add(s);
    272     }
    273 
    274     /// <summary>
    275     /// This method delete a state from the FSM List if it exists, 
    276     ///   or prints an ERROR message if the state was not on the List.
    277     /// 该方法删除一个已存在以状态几个中的状态
    278     /// 在它不存在时打印错误信息
    279     /// </summary>
    280     public void DeleteState(StateID id)
    281     {
    282         // Check for NullState before deleting
    283         //在删除前检查其是否为空状态
    284         if (id == StateID.NullStateID)
    285         {
    286             Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
    287             return;
    288         }
    289 
    290         // Search the List and delete the state if it's inside it
    291         //遍历集合如果存在该状态则删除它
    292         foreach (FSMState state in states)
    293         {
    294             if (state.ID == id)
    295             {
    296                 states.Remove(state);
    297                 return;
    298             }
    299         }
    300         Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
    301                        ". It was not on the list of states");
    302     }
    303 
    304     /// <summary>
    305     /// This method tries to change the state the FSM is in based on
    306     /// the current state and the transition passed. If current state
    307     ///  doesn't have a target state for the transition passed, 
    308     /// an ERROR message is printed.
    309     /// 该方法基于当前状态和过渡是否通过来尝试改变状态机的状态,当当前的状态没有目标状态用来过渡(叫通道应该更合适吧)时通过时则打印错误消息
    310     /// </summary>
    311     public void PerformTransition(Transition trans)
    312     {
    313         // Check for NullTransition before changing the current state
    314         //在改变当前状态前检测NullTransition
    315         if (trans == Transition.NullTransition)
    316         {
    317             Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
    318             return;
    319         }
    320 
    321         // Check if the currentState has the transition passed as argument
    322         //在改变当前状态前检测当前状态是否可作为过渡的参数
    323 
    324         StateID id = currentState.GetOutputState(trans);
    325         if (id == StateID.NullStateID)
    326         {
    327             Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
    328                            " for transition " + trans.ToString());
    329             return;
    330         }
    331 
    332         // Update the currentStateID and currentState        
    333         //更新当前的状态个和状态编号
    334         currentStateID = id;
    335         foreach (FSMState state in states)
    336         {
    337             if (state.ID == currentStateID)
    338             {
    339                 // Do the post processing of the state before setting the new one
    340                 //在状态变为新状态前执行后处理
    341                 currentState.DoBeforeLeaving();
    342 
    343                 currentState = state;
    344 
    345                 // Reset the state to its desired condition before it can reason or act
    346                 //在状态可以使用Reason(动机)或者Act(行为)之前为它的的决定条件重置它自己
    347                 currentState.DoBeforeEntering();
    348                 break;
    349             }
    350         }
    351 
    352     } // PerformTransition()
    353 
    354 } //class FSMSystem

    NPCControl:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Text;
      4 using UnityEngine;
      5 
      6 [RequireComponent(typeof(Rigidbody))]
      7 public class NPCControl : MonoBehaviour
      8 {
      9     public GameObject player;
     10     public Transform[] path;
     11     private FSMSystem fsm;
     12 
     13     public void SetTransition(Transition t)
     14     {
     15         //该方法用来改变有限状态机的状体,有限状态机基于当前的状态和通过的过渡状态。
     16         //如果当前的状态没有用来通过的过度状态,则会抛出错误
     17         fsm.PerformTransition(t);
     18     }
     19 
     20     public void Start()
     21     {
     22         MakeFSM();
     23     }
     24 
     25     public void FixedUpdate()
     26     {
     27         fsm.CurrentState.Reason(player, gameObject);
     28         fsm.CurrentState.Act(player, gameObject);
     29     }
     30 
     31     
     32     //NPC有两个状态分别是在路径中巡逻和追逐玩家
     33     //如果他在第一个状态并且SawPlayer 过度状态被出发了,它就转变到ChasePlayer状态
     34     //如果他在ChasePlayer状态并且LostPlayer状态被触发了,它就转变到FollowPath状态
     35 
     36     private void MakeFSM()//建造状态机
     37     {
     38         FollowPathState follow = new FollowPathState(path);
     39         follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);
     40 
     41         ChasePlayerState chase = new ChasePlayerState();
     42         chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);
     43 
     44         fsm = new FSMSystem();
     45         fsm.AddState(follow);//添加状态到状态机,第一个添加的状态将作为初始状态
     46         fsm.AddState(chase);
     47     }
     48 }
     49 
     50 public class FollowPathState : FSMState
     51 {
     52     private int currentWayPoint;
     53     private Transform[] waypoints;
     54 
     55     //构造函数装填自己
     56     public FollowPathState(Transform[] wp)
     57     {
     58         waypoints = wp;
     59         currentWayPoint = 0;
     60         stateID = StateID.FollowingPath;//别忘设置自己的StateID
     61     }
     62 
     63     public override void DoBeforeEntering()
     64     {
     65         Debug.Log("FollowingPath BeforeEntering--------");
     66     }
     67 
     68     public override void DoBeforeLeaving()
     69     {
     70         Debug.Log("FollowingPath BeforeLeaving---------");
     71     }
     72 
     73     //重写动机方法
     74     public override void Reason(GameObject player, GameObject npc)
     75     {
     76         // If the Player passes less than 15 meters away in front of the NPC
     77         RaycastHit hit;
     78         if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
     79         {
     80             if (hit.transform.gameObject.tag == "Player")
     81                 npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);
     82         }
     83     }
     84 
     85     //重写表现方法
     86     public override void Act(GameObject player, GameObject npc)
     87     {
     88         // Follow the path of waypoints
     89         // Find the direction of the current way point 
     90         Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
     91         Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;
     92 
     93         if (moveDir.magnitude < 1)
     94         {
     95             currentWayPoint++;
     96             if (currentWayPoint >= waypoints.Length)
     97             {
     98                 currentWayPoint = 0;
     99             }
    100         }
    101         else
    102         {
    103             vel = moveDir.normalized * 10;
    104 
    105             // Rotate towards the waypoint
    106             npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
    107                                                       Quaternion.LookRotation(moveDir),
    108                                                       5 * Time.deltaTime);
    109             npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);
    110 
    111         }
    112 
    113         // Apply the Velocity
    114         npc.GetComponent<Rigidbody>().velocity = vel;
    115     }
    116 
    117 } // FollowPathState
    118 
    119 public class ChasePlayerState : FSMState
    120 {
    121     //构造函数装填自己
    122     public ChasePlayerState()
    123     {
    124         stateID = StateID.ChasingPlayer;
    125     }
    126 
    127     public override void DoBeforeEntering()
    128     {
    129         Debug.Log("ChasingPlayer BeforeEntering--------");
    130     }
    131 
    132     public override void DoBeforeLeaving()
    133     {
    134         Debug.Log("ChasingPlayer BeforeLeaving---------");
    135     }
    136 
    137     public override void Reason(GameObject player, GameObject npc)
    138     {
    139         // If the player has gone 30 meters away from the NPC, fire LostPlayer transition
    140         if (Vector3.Distance(npc.transform.position, player.transform.position) >= 3)
    141             npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
    142     }
    143 
    144     public override void Act(GameObject player, GameObject npc)
    145     {
    146         // Follow the path of waypoints
    147         // Find the direction of the player         
    148         Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
    149         Vector3 moveDir = player.transform.position - npc.transform.position;
    150 
    151         // Rotate towards the waypoint
    152         npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
    153                                                   Quaternion.LookRotation(moveDir),
    154                                                   5 * Time.deltaTime);
    155         npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);
    156 
    157         vel = moveDir.normalized * 10;
    158 
    159         // Apply the new Velocity
    160         npc.GetComponent<Rigidbody>().velocity = vel;
    161     }
    162 
    163 } // ChasePlayerState

     

    Unity最受欢迎的插件,可以让您的游戏如虎添翼,为您节省大量时间可以投入在游戏的创意和细节上

    转载于:https://www.cnblogs.com/Firepad-magic/p/6185201.html

    展开全文
  • 当然无论是FSM、行为树、亦或者简单粗暴的ifelse都能制作AI,但是考虑到FSM比ifelse、switch架构更清晰方便管理,比行为树更好把控(毕竟自己纯手撸代码),最后决定使用FSM。 核心代码 FSM管理类 using System; ...

    前言

    这段时间写游戏遇到实现敌人AI的需求,打算使用FSM。当然无论是FSM、行为树、亦或者简单粗暴的ifelse都能制作AI,但是考虑到FSM比ifelse、switch架构更清晰方便管理,比行为树更好把控(毕竟自己纯手撸代码),最后决定使用FSM。

    代码多处使用到里氏替换原则,不了解的同学可以先了解一下更便于理解。

    核心代码

    FSM管理类

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.Linq;
    
    //AI状态
    public enum FSMStateType
    {
        Idle, Patrol, Seek, Attack,LookAt, Hit, Death
    }
    
    public class FSM : MonoBehaviour
    {
    	//这里使用list主要考虑到有可能需要状态并行
        private List<IState> currentState = new List<IState>();
        //存储所有状态
        protected Dictionary<FSMStateType, IState> states = new Dictionary<FSMStateType, IState>();
    	
    	//初始化状态机,传入所需状态并切换到初始状态
        protected void InitFSM(Dictionary<FSMStateType, IState> states, List<FSMStateType> startState)
        {
            this.states = states;
            TransitionState(startState);
        }
    
    	//执行所有状态的OnUpdate方法
        void Update()
        {
            if(currentState.Count != 0)
            {
                for (int i = 0; i < currentState.Count; i++)
                {
                    currentState[i].OnUpdate();
                }
            }
        }
    	
    	//状态切换
        public void TransitionState(List<FSMStateType> type)
        {
        	//执行当前状态的OnExit
            if (currentState != null)
                currentState.ForEach(_ => _.OnExit());
    		
    		//切换成传入的新状态
            currentState.Clear();
            List<IState> stateList = new List<IState>();
            type.ForEach(_ => stateList.Add(states[_]));
            currentState = stateList;
    		
    		//执行新状态的OnEnter
            currentState.ForEach(_ => _.OnEnter());
        }
    }
    

    IState类,所实现的具体状态类需集成该接口

    public interface IState
    {
        void OnEnter();
    
        void OnUpdate();
    
        void OnExit();
    }
    

    实际应用

    具体的AI类,需继承FSM类。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Enemy1AI : FSM
    {
    	//实现的状态类
        private SeekState seekState;
        private EnemyAttackState attackState;
        private LookAtTargetState lookAtState;
        private EnemyDeathState deathState;
    
        private void Start()
        {
            seekState = GetComponent<SeekState>();
            attackState = GetComponent<EnemyAttackState>();
            lookAtState = GetComponent<LookAtTargetState>();
            deathState = GetComponent<EnemyDeathState>();
    		
    		//初始化FSM
            InitFSM(new Dictionary<FSMStateType, IState>() { 
                { FSMStateType.Seek, seekState },
                { FSMStateType.Attack, attackState },
                { FSMStateType.LookAt,lookAtState},
                { FSMStateType.Death,deathState}
            }, new List<FSMStateType>() { FSMStateType.Seek });
        }
    }
    

    具体实现状态,这里是实现了一个寻路状态。注意继承IState接口。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class SeekState : MonoBehaviour,IState
    {
        private NavMeshAgent agent;
    
        public string tagStr; //根据标签寻找
        public Transform target; //目标
        public Vector3 targetPos; //目标点
        public float arriveDis; //距离多少为到达
        public float speed; //移速
        public float acceleration; //加速度
        public float angularSpeed; //角速度
        public List<string> nextState; //完成后是否切换到下一个状态
    
        bool isComplete;
    
        void Start()
        {
            agent = GetComponent<NavMeshAgent>();
        }
    
        public void OnEnter()
        {
            isComplete = false;
    
            if (tagStr != string.Empty)
            {
                target = GameObject.FindWithTag(tagStr).transform;
            }
        }
    
        public void OnUpdate()
        {
            //Debug.Log(Vector3.Distance(target.position, transform.position));
            if (isComplete)
                return;
    
            if (target != null)
            {
                DoSeek(target.position);
            }
            else
            {
                DoSeek(targetPos);
            }
        }
    
        public void OnExit()
        {
        }
    
        void DoSeek(Vector3 pos)
        {
            agent.stoppingDistance = arriveDis;
            agent.angularSpeed = angularSpeed;
            agent.speed = speed;
            agent.acceleration = acceleration;
            agent.SetDestination(pos);
    
            if (Vector3.Distance(pos, transform.position) <= arriveDis)
            {
                //Debug.Log(Vector3.Distance(pos, transform.position));
                //Debug.Log("完成");
    
                isComplete = true;
    
                if (nextState.Count != 0)
                {
                    List<FSMStateType> temp = new List<FSMStateType>();
                    nextState.ForEach(_ => temp.Add((FSMStateType)Enum.Parse(typeof(FSMStateType), _)));
    
    				//由于里氏替换原则我们不需要获取Enemy1AI直接获取FSM即可
                    GetComponent<FSM>().TransitionState(temp);
                }
            }
        }
    }
    
    
    展开全文
  • unity3D FSM有限状态机(状态设计模式)

    万次阅读 多人点赞 2018-07-12 14:07:50
    为了解决游戏过于麻烦的状态转换(人物动画过多),使用有限状态机 缺点:代码量大 编写 使用多态和Switch new(状态设计模式) 类的介绍 PlyayCtrl:挂载在角色身上的脚本,用来控制状态机,(控制状态机...

    前言

    为了解决游戏过于麻烦的状态转换(人物动画过多),使用有限状态机。
    缺点:代码量大

    编写

    1. 使用多态和Switch
    2. new(状态设计模式)

    类的介绍

    PlyayCtrl:挂载在角色身上的脚本,用来控制状态机,(控制状态机中状态的切换)。
    Machine:状态机器类,储存所有状态,对状态进行切换,和状态的保持工作。
    Base: 状态的基类,给子类提供接口(方法)
    Template:泛型类当前状态的拥有者
    Idle Run Attack …等各个具体状态的实现(播放动画)
    1

    各个类的关系图介绍

    状态模式

    脚本代码

    1.状态基类

    /// <summary>
    /// 状态的基础类:给子类提供方法
    /// </summary>
    /// 
    public class StateBase {
    
        //给每个状态设置一个ID
        public int ID{get;set;}
    
        //被当前机器所控制
        public StateMachine machine;
    
        public StateBase(int id)
        {
            this.ID = id;
        }
    
        //给子类提供方法
        public virtual void OnEnter(params object[] args){}
        public virtual void OnStay(params object[] args){}
        public virtual void OnExit(params object[] args){}
    
    }

    2.状态拥有者类(泛型类)

    /// <summary>
    /// 状态拥有者
    /// </summary>
    public class StateTemplate<T> : StateBase {
    
        public T owner;   //拥有者(范型)
    
        public StateTemplate(int id,T o):base(id)
        {
            owner = o;
        }
    }

    3.玩家脚本类

    /// <summary>
    /// PlayerCtrl
    /// 挂载在角色身上的脚本,用来控制状态机器类
    /// </summary>
    /// 
    public enum PlayerState
    {
        None,
        Idle,
        Run,
        Attack
    }
    
    public class Player : MonoBehaviour {
    
        public Animation ani;
        public PlayerState ps = PlayerState.Idle;
    
        //控制机器
        public StateMachine machine;
    
        void Start()
        {
            ani = GetComponent<Animation> ();
    
            IdleState idle = new IdleState (1, this);
            RunState run = new RunState (2, this);
            AttackState attack = new AttackState (3, this);
    
            machine = new StateMachine (idle);
            machine.AddState (run);
            machine.AddState (attack);
        }
    
        void Update()
        {
            if (Input.GetMouseButtonDown (0)) {
                ps = PlayerState.Attack;
            }
            if (Input.GetKey (KeyCode.A)) {
                ps = PlayerState.Run;
            }
            if (Input.GetKeyUp (KeyCode.A)) {
                ps = PlayerState.Idle;
            }
    
            //根据枚举 让状态机器类去切换状态
            UpdateAnimation ();
        }
        private void UpdateAnimation()
        {
            switch (ps) {
            case PlayerState.Idle:
                machine.TranslateState (1);
                break;
            case PlayerState.Run:
                machine.TranslateState (2);
                break;
            case PlayerState.Attack:
                machine.TranslateState (3);
                break;
            }
        }
    
        void LateUpdate()
        {
            machine.Update ();
        }
    }
    

    4.状态机类

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    /// <summary>
    /// 状态机器类:由Player控制。完成状态的存储,切换,和状态的保持
    /// </summary>
    
    public class StateMachine {
    
        //用来存储当前机器所控制的所有状态
        public Dictionary<int,StateBase> m_StateCache;
    
        //定义上一个状态
        public StateBase m_prviousState;
        //定义当前状态
        public StateBase m_currentState;
    
        //机器初始化时,没有上一个状态
        public StateMachine(StateBase beginState)
        {
            m_prviousState = null;
            m_currentState = beginState;
    
            m_StateCache = new Dictionary<int, StateBase> ();
            //把状态添加到集合中
            AddState (beginState);
            m_currentState.OnEnter ();
        }
    
        public void AddState(StateBase state)
        {
            if (!m_StateCache.ContainsKey (state.ID)) {
                m_StateCache.Add (state.ID, state);
                state.machine = this;
            }
        }
    
        //通过Id来切换状态
        public void TranslateState(int id)
        {
            if (!m_StateCache.ContainsKey (id)) {
                return;
            }
    
            m_prviousState = m_currentState;
            m_currentState = m_StateCache[id];
            m_currentState.OnEnter ();
        }
    
        //状态保持
        public void Update()
        {
            if (m_currentState != null) {
                m_currentState.OnStay ();
            }
        }
    }

    5.人物普通状态类

    /// <summary>
    /// Idle状态
    /// </summary>
    public class IdleState : StateTemplate<Player> {
    
        public IdleState(int id,Player p):base(id,p){
        }
    
        public override void OnEnter (params object[] args)
        {
            base.OnEnter (args);
            owner.ani.Play ("Idle");
        }
        public override void OnStay (params object[] args)
        {
            base.OnStay (args);
        }
        public override void OnExit (params object[] args)
        {
            base.OnExit (args);
        }
    }

    6.跑步状态类

    using UnityEngine;
    using System.Collections;
    
    /// <summary>
    /// Run状态
    /// </summary>
    
    public class RunState : StateTemplate<Player> {
    
        public RunState(int id,Player p):base(id,p){
        }
    
        public override void OnEnter (params object[] args)
        {
            base.OnEnter (args);
            owner.ani.Play ("Run");
        }
        public override void OnStay (params object[] args)
        {
            base.OnStay (args);
        }
        public override void OnExit (params object[] args)
        {
            base.OnExit (args);
        }
    }

    7.攻击状态类

    using UnityEngine;
    using System.Collections;
    
    /// <summary>
    /// Attack状态
    /// </summary>
    public class AttackState : StateTemplate<Player> {
    
        public AttackState(int id,Player p):base(id,p){
        }
    
        public override void OnEnter (params object[] args)
        {
            base.OnEnter (args);
            owner.ani.Play ("Attack");
        }
        public override void OnStay (params object[] args)
        {
            base.OnStay (args);
            if (!owner.ani.IsPlaying ("Attack")) {
                OnExit ();
            }
        }
        public override void OnExit (params object[] args)
        {
            base.OnExit (args);
            owner.ps = PlayerState.Idle;
        }
    }
    

    总结

    通过即只添加代码,不删除代码的设计原则来写有限状态机。
    FSM简单来说就是
    4个类
    1. 状态基类 状态基类需要3个虚方法,以及虚拟机对象
    2. 拥有者泛型类 继承状态基类,构造出使用者对象
    3. 玩家脚本类
    在Start方法里构造new各个状态机类(传入枚举id,使用者this(自己)),new状态机对象,添加各个状态类对象。
    在Update通过按键,并一直调用状态机的切换方法。
    在LateUpdate()调用状态机的保持状态方法
    4. 状态机类
    包含添加状态类,切换状态类方法(控制Enter()),保存状态类方法(控制(Stay()))
    加若干个状态类
    每个状态具体有3种状态方法(OnEnter,OnStay,OnExit)需要重写来具体实现,通过OnExit()方法改变使用者的枚举id来切换状态

    展开全文
  • //石头人各种状态 这里就先写三个 代码可视化 private void Awake() { Instance = this; } public enum StoneStates { StoneIdle, StoneHit3, StoneWalk, Max//数组长度时用到 } public void ChangeStoneAnimation...

    做了一个非常简单的demo,用来演示上一次封装框架的用法,只用了三个动作。这个框架够用,但是还是要完善一下,目前来说最大的缺陷是切换动作时会打断其他动作的播放,在OnStay返回值改为bool,ChangeState的时候如果动作没有完成就return这样应该就可以了。
    在这里插入图片描述

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class StoneIdle : FSMBase
    {
        Animator animator;
        public StoneIdle(Animator animator)
        {
            this.animator = animator;
        }
        public override void OnEnter()
        {
            animator.SetInteger("Index", (int)StoneCtrl.StoneStates.StoneIdle);
        }
    }
    public class StoneHit3: FSMBase
    {
        //判断该动画是否播放完毕
       
        Animator animator;
        public StoneHit3(Animator animator)
        {
            this.animator = animator;
        }
        public override void OnEnter()
        {
           
            animator.SetInteger("Index", (int)StoneCtrl.StoneStates.StoneHit3);
        }
        //扩展
        //像攻击这样的特殊动作 可以在这里写攻击判定
        public override void OnStay()
        {
             base.OnStay();
            if (timeCount>3.3f)
            {
                timeCount = 0;
                StoneCtrl.Instance.ChangeStoneAnimation(StoneCtrl.StoneStates.StoneIdle);
            }
          
        }
    }
    public class StoneWalk : FSMBase
    {
        Animator animator;
        public StoneWalk(Animator animator)
        {
            this.animator = animator;
        }
        public override void OnEnter()
        {
            animator.SetInteger("Index", (int)StoneCtrl.StoneStates.StoneWalk);
        }
    }
    
    
    public class StoneCtrl : MonoBehaviour
    {
        public static StoneCtrl Instance;
        Animator animator;
        FSMManager fsmMrg = new FSMManager((int)StoneStates.Max);
        //石头人各种状态 这里就先写三个 代码可视化
        private void Awake()
        {
            Instance = this;
        }
        public enum StoneStates
        {
            StoneIdle,
            StoneHit3,
            StoneWalk,
            Max//数组长度时用到
        }
        public void ChangeStoneAnimation(StoneStates states)
        {
            fsmMrg.ChangeState((int)states);
        }
        void AddStates()
        {
            animator = GetComponent<Animator>();
            fsmMrg.AddState(new StoneIdle(animator));
            fsmMrg.AddState(new StoneHit3(animator));
            fsmMrg.AddState(new StoneWalk(animator));
        }
        void Start()
        {
            AddStates();
        }
       
        void Update()
        { 
            if (fsmMrg!=null)
            {
                fsmMrg.Update();
            }
           
        }
    }
    
    
    展开全文
  • using System.Collections; using System.Collections.Generic;... //进入这个状态 public abstract void OnEnter(); //正处在这个状态 public virtual void OnStay() { } //退出这个状态 public virtual void
  • FSM有限状态机学习及Unity3D案例讲解

    千次阅读 2018-04-08 17:31:55
    遂今日更新FSM有限状态机学习,希望大家共同进步! 开发版本:Unity 2017.1.1f1、VS 2017 适合人群:初学Unity者 一.有限状态机定义 有限状态机(英文:Finite State Machine,简称FSM),是指在不同阶段呈现不同的...
  • Unity FSM(有限状态机)

    千次阅读 2020-04-14 01:12:47
    FSM有限状态机,一个可以枚举出有限个状态,并且这些状态在特定条件下是能够来回切换的。 在游戏中经常看到的一些AI,如敌人巡逻,巡逻过程中看到玩家就追击,追上了就攻击,追不上并且有了一定的距离就返回去...
  • Asynchronous transitions from Unity Engine, like OnTriggerEnter, SendMessage, can also be used, just call the Method PerformTransition from your FSMSystem instance with the correct Transition when ...
  • Unity fsm有限状态机的实现

    千次阅读 2021-12-21 10:20:07
    FSM using UnityEngine; public class FSM<T> { private T owner; public IFSMState<T> CurrentState { get; private set; } public IFSMState<T> PreviousState { get; private set; } ...
  • NPC是游戏中很重要的一部分,功能复杂,行为智能的NPC可以给玩家带来更加优秀的游戏体验,我们可以通过对NPC的行为模式的设计来理解有限状态机 而对于游戏开发者而言,要实现一个功能完备,逻辑清晰的AI状态图可不是...
  • FSM有限状态机

    2018-10-30 15:55:18
    有限状态机,简单易懂。自带Demo,进入状态有 进入 更新 退出 三个生命周期
  • Unity中实现FSM有限状态机

    千次阅读 2017-01-09 21:27:50
    简介 有限状态机,(英语:Finite-state machine, FSM),又称有限状态...有限状态机(FSM)是游戏AI的一种常用模型,我做一个简单的记录,在Unity中实现有限状态机模型. FSM的结构和逻辑 整体结构分为4个部分 AIContr
  • /// /// 巡逻状态 /// 在有限的几个点内来回行动 /// public class EnemyPatrol : FSMStateBase { public override FSMState fsmState => FSMState.Patrol; private EnemyAIData data; private Vector3 curPoint; ...
  • Unity--FSM有限状态机

    千次阅读 2018-08-10 15:28:05
    有限状态机,先理解是干什么的,有限表示这个是有限度的不是无限的,状态,指的是所拥有的所有状态,这么来理解,人有情绪,比如说生气,无感,喜悦,难过,生气,幸福等,那么这些情绪是固有的几种,是所谓有限,...
  • FSM有限状态机 一、设计思路 1.共同的状态父类,提供可重写的进入,保持,退出该状态的生命周期方法; 2.状态机,管理所有状态(增删查改),状态机运行方法(Run); 3.在角色控制器中,实例化状态机,并初始化添加...
  • FSM 有限状态机 概念 如其名,在有限的状态下,管理状态的转移的一种设计模式 基本思路 如 状态a 要切换到 状态b,需要通过中介也就是状态管理机进行切换,而不是两种状态直接切换,降低了耦合 另外每种状态...
  • Unity有限状态机 FSM 游戏引擎:Unity 版本:2019.4.6f1 【2017版本以上均可】 编译平台:Visual Studio 2019   一、什么是FSM   FSM 全名 [Finite State Machine]。中文名 [有限状态机],又称 [有限状态...
  • FSM_有限状态机

    2018-06-12 13:43:23
    相关链接 https://blog.csdn.net/qq_35433081/article/details/80664096 https://indienova.com/indie-game-development/game-programming-patterns-3/
  • Unity中的有限状态机其实就是控制人物状态的一种模式,就是通过两个具体的枚举进行切换,通过这种方式来给我们做NPCAI的时候进行解耦,今天我制作了一个有限状态机,在状态机中主要就是两个类一个是FSMstate类...
  • FSM 用C#编写的分层有限状态机
  • unity3D有限状态机

    2017-02-17 11:14:50
    有限状态机,附带工程及源码,清晰注释
  • 本系列笔记转载自游戏蛮牛专栏作家Jackel的论坛文章,详细介绍了FSM的创建与使用,特与众分享。链接:http://www.manew.com/thread-37136-1-1.html 一、实例图例 该实例主要是按钮状态的转换。 给大家解析一下,...
  • Unity中使用状态机控制角色行为

    千次阅读 2020-04-05 02:52:04
    接着上一篇说的随谈过去的2019,这篇专门记录下FSM的使用和别人写的一个具体实现。 其实很多普遍功能,网上一搜一般都可以找到具体实现,而且有很多重复的文章。我平时要了解学习某个功能,一般都会看多篇文章,而...
  • 上篇文章讲了通用状态机的实现(文末),这次讲讲如何在实际情况中去使用。这次先做一个简单的控制Animator的状态机。        鉴于我用来做动画的模型只有Idle、Run、Die三种...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 932
精华内容 372
关键字:

unityfsm有限状态机