• Unity中FSM有限状态机


    什么是FSM

    FSM 即有限状态机,它是一个状态管理系统,表示一个对象的几种状态在指定条件下转移行为,即随着条件的不断改变内部状态不断地切换。

    FSM用处或者使用背景

    通常使用FSM去实现一些简单的AI逻辑,对于游戏中的每个对象都可以在其生命周期中分出一些状态,比如一个小兵,他可能在休息,或者巡逻,当敌人出现时,他的状态可能切换为追逐敌人或者攻击敌人,当某些条件成立时,状态机从当前状态转移到下一状态,在不同状态下有不同的任务,所以要使用有限状态机去实现。

    FSM使用的必要性

    当需要实现角色的状态时以及状态间的切换时,相信第一时间想到的应该是if-else,但是如果状态的切换条件表达式过于复杂,if-else就显得臃肿麻烦了。再加上所有条件的判断全在一起,状态一多也容易出现Bug,扩展也不好,使用if-else就会相当臃肿,所以在这种条件下,有限状态机的使用很有必要,当然如果相对简单的状态切换,使用有限状态机就没必要了,需要根据个人需求。

    FSM使用注意点

    FSM有两个重要的概念:状态和转移,转移是切换条件,必须有一个初始状态,并保存当前状态,以及注意每种状态的转移的必要条件。

    FSM优点

    使整个状态切换逻辑比较清晰,增强代码的可扩展性,也便于后期维护。

    具体实现代码

    下面就实现一下FSM,使用怪物AI的例子

    1.创建状态基类FSMstate  ,此类负责处理一个状态的周期,状态的进入前,状态中,离开状态等。以及状态切换条件的增删。具体如下:

      1 using System.Collections;
      2 using System.Collections.Generic;
      3 using UnityEngine;
      4 /// <summary>
      5 /// 状态ID
      6 /// </summary>
      7 public enum StateID
      8 {
      9     NoneStateID,
     10     Parol,//巡逻状态
     11     Chase,//追逐状态
     12 }
     13 /// <summary>
     14 /// 状态切换条件
     15 /// </summary>
     16 public enum Transition
     17 {
     18     NoneTransition,
     19     SeePlayer,//看到玩家
     20     LosePlayer,//看不到玩家
     21 }
     22 
     23 /// <summary>
     24 /// FSM中状态基类(实现状态的基本方法)
     25 /// </summary>
     26 public abstract class FSMstate  {
     27 
     28     protected StateID stateID;//状态对应的ID
     29     public StateID ID { get { return stateID; } }
     30     protected Dictionary<Transition, StateID> Transition_StateIDDic = new Dictionary<Transition, StateID>();//存储转换条件和状态的ID
     31     protected FSMsystem fsmSystem;//管理状态对象(因为要状态更新需要通过FSMsystem去管理实现的,所以需要一个管理对象)
     32 
     33     public FSMstate(FSMsystem fsm)
     34     {
     35         this.fsmSystem = fsm;
     36     }
     37     /// <summary>
     38     ///增加转条件
     39     /// </summary>
     40     /// <param name="trans"></param>
     41     /// <param name="id"></param>
     42     public void AddTransition(Transition trans,StateID id)
     43     {
     44         if (trans==Transition.NoneTransition)
     45         {
     46             Debug.Log("添加的转换条件不能为null");
     47             return;
     48         }
     49         if (id==StateID.NoneStateID)
     50         {
     51             Debug.Log("添加的状态ID不能为null");
     52             return;
     53         }
     54         if (Transition_StateIDDic.ContainsKey(trans))
     55         {
     56             Debug.Log("添加转换条件的时候," + trans + "已经存在于Transition_StateIDDic中");
     57             return;
     58         }
     59         Transition_StateIDDic.Add(trans,id);
     60     }
     61     /// <summary>
     62     /// 删除转换条件
     63     /// </summary>
     64     /// <param name="trans"></param>
     65     public void DeleteTransition(Transition trans)
     66     {
     67         if (trans == Transition.NoneTransition)
     68         {
     69             Debug.Log("删除的转换条件不能为null");
     70             return;
     71         }
     72         if (!Transition_StateIDDic.ContainsKey(trans))
     73         {
     74             Debug.Log("删除转换条件的时候," + trans + "不存在于Transition_StateIDDic中");
     75             return;
     76         }
     77         Transition_StateIDDic.Remove(trans);
     78     }
     79     /// <summary>
     80     /// 根据转换条件获得状态ID
     81     /// </summary>
     82     /// <param name="trans"></param>
     83     /// <returns></returns>
     84     public StateID GetStateID(Transition trans)
     85     {
     86         if (Transition_StateIDDic.ContainsKey(trans))
     87         {
     88             return Transition_StateIDDic[trans];
     89         }
     90         return StateID.NoneStateID;
     91     }
     92 
     93     /// <summary>
     94     ///转换到此状态前要执行的逻辑
     95     /// </summary>
     96     public virtual void DoBeforeEnterAcion() { }
     97     /// <summary>
     98     /// 离开此状态前要执行的逻辑
     99     /// </summary>
    100     public virtual void DoAfterLevAction(){ }
    101     /// <summary>
    102     /// 处在本状态时要执行的逻辑
    103     /// </summary>
    104     /// <param name="TargetObj"></param>
    105     public abstract void CurrStateAction(GameObject TargetObj);
    106     /// <summary>
    107     /// 切换到下一状态需要执行的逻辑            
    108     /// </summary>
    109     /// <param name="TargetObj"></param>
    110     public abstract void NextStateAction(GameObject TargetObj);
    111 
    112 
    113 }

    2. 创建状态管理类FSMSystem的创建。用来管理所有的状态(状态的添加,删除,切换,更新等)。

     1 using System.Collections;
     2 using System.Collections.Generic;
     3 using UnityEngine;
     4 /// <summary>
     5 /// 状态处理类(添加,删除,切换,更新等管理所有状态)
     6 /// </summary>
     7 public class FSMsystem  {
     8 
     9     private Dictionary<StateID, FSMstate> StateDic = new Dictionary<StateID, FSMstate>();//保存状态ID以及ID对应的状态
    10     private StateID _CurrentStateID;//当前处于的状态ID
    11     private FSMstate _CurrentState;//当前处于的状态
    12 
    13     /// <summary>
    14     /// 添加状态
    15     /// </summary>
    16     /// <param name="state">需管理的状态</param>
    17     public void AddState(FSMstate state) {
    18         if (state==null)
    19         {
    20             Debug.Log("添加的状态"+state+"不能为null");
    21             return;
    22         }
    23         if (_CurrentState==null)
    24         {
    25             _CurrentState = state;
    26             _CurrentStateID = state.ID;
    27         }
    28         if (StateDic.ContainsKey(state.ID))
    29         {
    30             Debug.Log("状态机 "+state.ID+"已经存在,无法添加");
    31             return;
    32         }
    33         StateDic.Add(state.ID,state);
    34     }
    35     /// <summary>
    36     /// 删除状态
    37     /// </summary>
    38     /// <param name="stateID">删除要管理状态的ID</param>
    39     public void DeleteState(StateID  stateID)
    40     {
    41         if (stateID==StateID.NoneStateID)
    42         {
    43             Debug.Log("无法删除Null的状态");
    44             return;
    45         }
    46         if (!StateDic.ContainsKey(stateID) )
    47         {
    48             Debug.Log("无法删除不存在的状态:" + stateID);
    49             return;
    50         }
    51         StateDic.Remove(stateID);
    52     }
    53     /// <summary>
    54     /// 状态转换(状态的切换是根据转换条件的变化)
    55     /// </summary>
    56     /// <param name="trans">转换条件</param>
    57     public void PerformTranstion(Transition trans)
    58     {
    59         if (trans == Transition.NoneTransition)
    60         {
    61             Debug.Log("无法执行NULL的转换条件");
    62             return;
    63         }
    64         StateID stateId = _CurrentState.GetStateID(trans);
    65         if (stateId==StateID.NoneStateID)
    66         {
    67             Debug.Log("要转换的状态ID为null");
    68             return;
    69         }
    70         if (!StateDic.ContainsKey(stateId))
    71         {
    72             Debug.Log("状态机中没找到状态ID  "+stateId+"  无法转换状态");
    73             return;
    74         }
    75         FSMstate state = StateDic[stateId];//根据状态ID获取要转换的状态
    76         _CurrentState.DoAfterLevAction();//执行离开上一状态逻辑
    77         _CurrentState = state;//更新当前状态
    78         _CurrentStateID = stateId;//更新当前状态ID
    79         _CurrentState.DoBeforeEnterAcion();//执行进入当前状态前要执行的逻辑
    80     }
    81 
    82     /// <summary>
    83     /// 更新当前状态行为
    84     /// </summary>
    85     /// <param name="TargetObj"></param>
    86     public void UpdateState(GameObject TargetObj)
    87     {
    88         _CurrentState.CurrStateAction(TargetObj);
    89         _CurrentState.NextStateAction(TargetObj);
    90     }
    91 
    92 }

    3.那我们用怪物巡逻,追逐玩家的例子来实现状态机。

    3.1. 增加一个PartalState类,用怪物的巡逻。

     1 using System.Collections;
     2 using System.Collections.Generic;
     3 using UnityEngine;
     4 
     5 /// <summary>
     6 /// 怪物巡逻状态类
     7 /// </summary>
     8 public class PatrolState : FSMstate {
     9     private List<Transform> path = new List<Transform>();//巡逻点
    10     private int index = 0;
    11     private Transform PlayerTrasform;
    12 
    13     /// <summary>
    14     /// 初始化巡逻状态数据
    15     /// </summary>
    16     /// <param name="fsm"></param>
    17     public PatrolState(FSMsystem fsm):base(fsm)
    18     {
    19         stateID = StateID.Parol;
    20         //路点
    21         Transform pathTransform = GameObject.Find("Path").transform;
    22         Transform[] children = pathTransform.GetComponentsInChildren<Transform>();
    23         foreach (Transform child in children)
    24         {
    25             if (child != pathTransform)
    26             {
    27                 path.Add(child);
    28             }
    29         }
    30         PlayerTrasform = GameObject.Find("Player").transform;
    31     }
    32 
    33     /// <summary>
    34     /// 当前状态(巡逻状态)执行的逻辑
    35     /// </summary>
    36     /// <param name="TargetObj"></param>
    37     public override void CurrStateAction(GameObject TargetObj)
    38     {
    39         TargetObj.transform.LookAt(path[index].position);
    40         TargetObj.transform.Translate(Vector3.forward * Time.deltaTime * 3);
    41         if (Vector3.Distance(TargetObj.transform.position, path[index].position) < 1)
    42         {
    43             index++;
    44             index %= path.Count;
    45         }
    46     }
    47     /// <summary>
    48     /// 切换到追逐状态(下一状态)执行的的逻辑
    49     /// </summary>
    50     /// <param name="TargetObj"></param>
    51     public override void NextStateAction(GameObject TargetObj)
    52     {
    53         if (Vector3.Distance(PlayerTrasform.position, TargetObj.transform.position) < 3)
    54         {
    55             fsmSystem.PerformTranstion(Transition.SeePlayer);
    56         }
    57     }
    58 
    59 
    60 }

    3.2.增加一个ChaseState类,用怪物的追逐。

     1 using System;
     2 using System.Collections;
     3 using System.Collections.Generic;
     4 using UnityEngine;
     5 
     6 /// <summary>
     7 /// 追逐状态类
     8 /// </summary>
     9 public class ChaseState : FSMstate {
    10     
    11     private Transform PlayerTransForm;//玩家位置信息
    12 
    13     public ChaseState(FSMsystem fsm):base(fsm)
    14     {
    15         stateID = StateID.Chase;
    16         PlayerTransForm = GameObject.Find("Player").transform;
    17     }
    18     /// <summary>
    19     /// 追逐状态下执行的逻辑
    20     /// </summary>
    21     /// <param name="targrtObj"></param>
    22     public override void CurrStateAction(GameObject targrtObj)
    23     {
    24         targrtObj.transform.LookAt(PlayerTransForm.transform.position);
    25         targrtObj.transform.Translate(Vector3.forward*2*Time.deltaTime);
    26     }
    27     /// <summary>
    28     /// 切换到下一状态(巡逻)前要执行的逻辑
    29     /// </summary>
    30     /// <param name="targrtObj"></param>
    31     public override void NextStateAction(GameObject targrtObj)
    32     {
    33         if (Vector3.Distance(PlayerTransForm.position,targrtObj.transform.position)>6)
    34         {
    35             fsmSystem.PerformTranstion(Transition.LosePlayer);
    36         }
    37     }
    38 }

    3.3 创建一个控制怪物的脚本Enemy 

     1 using System.Collections;
     2 using System.Collections.Generic;
     3 using UnityEngine;
     4 /// <summary>
     5 /// 怪物类
     6 /// </summary>
     7 public class Enemy : MonoBehaviour {
     8 
     9     private FSMsystem fsmsystem; //在Enemy类中,实例化FSMSystem对象,添加巡逻和追逐状态,还有之间的转换条件
    10     void Start () {
    11         InitFsm();//创建状态机
    12     }
    13     
    14     void Update () {
    15         fsmsystem.UpdateState(this.gameObject);//检查更新状态
    16     }
    17     /// <summary>
    18     /// 创建状态机
    19     /// 怪物有两种状态分别是巡逻和追逐玩家
    20     /// 如果怪物初始状态(设置为Parol状态)一旦SeePlayer 切换状态被激活后,就切换到Chase状态
    21     /// 如果他在Chase状态一旦LosePlayer状态被激活了,它就转变到Parol状态
    22     /// </summary>
    23     void InitFsm()
    24     {
    25         fsmsystem = new FSMsystem();
    26         FSMstate PatrolState = new PatrolState(fsmsystem);
    27         PatrolState.AddTransition(Transition.SeePlayer, StateID.Chase);
    28         FSMstate ChaseState = new ChaseState(fsmsystem);
    29         ChaseState.AddTransition(Transition.LosePlayer, StateID.Parol);
    30         fsmsystem.AddState(PatrolState);//初始状态
    31         fsmsystem.AddState(ChaseState);
    32     }
    33 }

    以上就用FSM有限状态机实现了怪物AI,具体代码中都有注释,有理解不到位的地方,望请指正。

  • 相关阅读:
    【Shell】 计算文件 交集,并集和差集
    http协议--Apache-Httpd服务基本配置-rpm安装-编译安装(HTTP2.2,HTTP2.4)
    进程管理工具
    Linux系统原理(工作模式)
    网络协议和管理
    网络通信安全基础(加密方式,OpenSSL)
    BZOJ 2969 期望
    BZOJ 2118 Dijkstra
    BZOJ 1407 exgcd
    BZOJ 2406 二分+有上下界的网络流判定
  • 原文地址:https://www.cnblogs.com/answer-yj/p/10955933.html
Copyright © 2020-2023  润新知