吃饭,睡觉,打豆豆

现在要实现一个游戏中的一个NPC的AI,他只做三件事,吃饭,睡觉,打豆豆,最直接,最简答想到的代码应该是这样。

[csharp] 查看纯文本 
  1. void  Update()  
  2. {  
  3.     如果(饿)  
  4.     {  
  5.         吃();  
  6.         返回;  
  7.     }  
  8.   
  9.     如果(困)  
  10.     {  
  11.         睡觉();  
  12.         返回;  
  13.     }  
  14.   
  15.     如果(无聊)  
  16.     {  
  17.         KickDD();  
  18.         返回;  
  19.     }  
  20.       
  21. }  



这样的代码是工作,但是,当NPC的状态和行为不断复杂化的时候,慢慢的,你就会添加各种条件变量,慢慢的,你就会用上了各种switch case,慢慢的,判断层级越来越多,慢慢的....


这个时候你就需要一个状态机了。


FSM简介

FSM定义:

一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生。一个有限状态机在任何瞬间只能处在一种状态。

它的优点:

1.编程快速简单,2易于。调试,3。很少的计算开销,4。直觉性,5。灵活性。



简单的框架

通用unity状态机


主要的两个类,FSMState表示状态,FSMSystem里面维护了一个状态的列表,最后需要一个StateController作为状态的控制器。

代码清单

FSMState.cs

[csharp] 查看纯文本 
  1. 使用 UnityEngine;  
  2. 使用 系统;  
  3. 使用 System.Collections.Generic;  
  4. 使用 System.Collections;  
  5.   
  6. 公共枚举 过渡   
  7. {  
  8.     NullTransition = 0,  //使用此转换来表示系统中不存在的转换  
  9.     SawPlayer,  
  10.     LostPlayer,  
  11.     NoHealth,  
  12.     ReadytoAim,  
  13.     ReadytoShot,  
  14.     ReadytoIdle,  
  15.     ReadytoAttack,  
  16.     ReadytoChasing  
  17. }  
  18.   
  19. public enum  StateID   
  20. {  
  21.     NullStateID = 0,  //使用此ID表示系统中不存在的状态  
  22.     闲,  
  23.     追逐  //跳  
  24.     攻击,  
  25.     射击,  
  26.     瞄准,  
  27.     BacktoIdle,//跳  
  28.     死,  
  29. }  
  30.   
  31.   
  32. 公共抽象 FSMState {    
  33.     protected  Dictionary <Transition,StateID> map =  new  Dictionary <Transition,StateID>();  
  34.     受保护的状态 ID stateID;  
  35.     public  StateID ID {  get  {  return  stateID; }}  
  36.   
  37.     public void  AddTransition(Transition trans,StateID id)   
  38.     {  
  39.         //检查任何args是否无效  
  40.         if  (trans == Transition.NullTransition)  
  41.         {  
  42.             Debug.LogError( FSMState ERROR:NullTransition不允许实际转换” );  
  43.             返回;  
  44.         }  
  45.   
  46.         if  (id == StateID.NullStateID)  
  47.         {  
  48.             Debug.LogError( FSMState ERROR:NullStateID不允许为真实的ID” );  
  49.             返回;  
  50.         }  
  51.   
  52.         //因为这是一个确定性的FSM,  
  53.         //检查当前的转换是否已经在地图内  
  54.         if  (map.ContainsKey(trans))  
  55.         {  
  56.             Debug.LogError( FSMState ERROR:State”  + stateID.ToString()+  “has has transition”  + trans.ToString()+  
  57.                            “不可能分配到另一个状态” );  
  58.             返回;  
  59.         }  
  60.   
  61.         map.Add(trans,id);  
  62.     }  
  63.   
  64.     /// <summary>  
  65.     ///这个方法从这个状态的地图中删除一个转换状态。  
  66.     ///如果转换不在状态图中,则会打印出一条错误消息。  
  67.     /// </ summary>  
  68.     public void  DeleteTransition(Transition trans)   
  69.     {  
  70.         //检查NullTransition  
  71.         if  (trans == Transition.NullTransition)  
  72.         {  
  73.             Debug.LogError( FSMState ERROR:NullTransition不允许” );  
  74.             返回;  
  75.         }  
  76.   
  77.         //在删除之前检查该对是否在映射内  
  78.         if  (map.ContainsKey(trans))  
  79.         {  
  80.             map.Remove(反式);  
  81.             返回;  
  82.         }  
  83.         Debug.LogError( FSMState ERROR:Transition”  + trans.ToString()+  “传递给”  + stateID.ToString()+  
  84.                        “不在国家的过渡名单” );  
  85.     }  
  86.   
  87.     /// <summary>  
  88.     ///这个方法返回FSM应该是if的新状态  
  89.     ///这个状态接收到转换   
  90.     /// </ summary>  
  91.     public  StateID GetOutputState(Transition trans)  
  92.     {  
  93.         //检查地图是否具有此转换  
  94.         if  (map.ContainsKey(trans))  
  95.         {  
  96.             返回 地图[trans];  
  97.         }  
  98.         return  StateID.NullStateID;  
  99.     }  
  100.   
  101.     /// <summary>  
  102.     ///此方法用于在进入状态之前设置状态。  
  103.     ///在分配前由FSMSystem类自动调用它  
  104.     ///到当前状态。  
  105.     /// </ summary>  
  106.     public virtual void  DoBeforeEntering(){}    
  107.   
  108.     /// <summary>  
  109.     ///这个方法用来做任何必要的改变变量  
  110.     ///在FSMSystem变为另一个之前。它被自动调用  
  111.     ///在FSMS系统变为新状态之前。  
  112.     /// </ summary>  
  113.     public virtual void  DoBeforeLeaving(){}    
  114.   
  115.     /// <summary>  
  116.     ///此方法决定状态是否应该在其列表上转换到另一个  
  117.     /// NPC是对由该类控制的对象的引用  
  118.     /// </ summary>  
  119.     public abstract void  Reason(GameObject player,GameObject npc);    
  120.   
  121.     /// <summary>  
  122.     ///这个方法控制游戏世界中NPC的行为。  
  123.     /// NPC所做的每一个动作,动作或沟通应放在这里  
  124.     /// NPC是对由该类控制的对象的引用  
  125.     /// </ summary>  
  126.     public abstract void  Act(GameObject player,GameObject npc);    
  127. }  


FSMSystem.cs

[csharp] 查看纯文本 
  1. 使用 UnityEngine;  
  2. 使用 System.Collections;  
  3. 使用 System.Collections.Generic;  
  4.   
  5.    
  6. 公共 FSMSystem {   
  7.     私人 列表<FSMState>状态;  
  8.     //可以改变FSM状态的唯一方法是执行转换  
  9.     //不要直接更改CurrentState  
  10.     private  StateID currentStateID;  
  11.     public  StateID CurrentStateID {  get  {  return  currentStateID; }}  
  12.     私人 FSMState currentState;  
  13.     public  FSMState CurrentState {  get  {  return  currentState; }}  
  14.     public  StateID defaultState { set {defaultState = value;}  get  { return  defaultState;}}  
  15.   
  16.     public void  resetToDefaultState()   
  17.     {  
  18.         currentState = states [0];  
  19.         currentStateID = states [0] .ID;  
  20.         / * for(int i = 0; i <states.Count; i ++) 
  21.         { 
  22.             if(states [i] .ID == defaultState) 
  23.             { 
  24.                 currentState = states [i]; 
  25.                 currentStateID = states [i] .ID; 
  26.             } 
  27.         } * /  
  28.     }  
  29.   
  30.     公共 FSMSystem()  
  31.     {  
  32.         states =  new  List <FSMState>();  
  33.     }  
  34.   
  35.     /// <summary>  
  36.     ///此方法将新状态置于FSM中,  
  37.     ///如果状态已经在列表中,则打印出ERROR消息。  
  38.     ///添加的第一个状态也是初始状态。  
  39.     /// </ summary>  
  40.     public void  AddState(FSMState s)   
  41.     {  
  42.         //删除前检查Null引用  
  43.         if  (s ==  null )  
  44.         {  
  45.             Debug.LogError(“FSM ERROR:Null reference is not allowed” );  
  46.         }  
  47.   
  48.         //插入的第一个状态也是初始状态,  
  49.         //机器在模拟开始时所处的状态  
  50.         if  (states.Count == 0)  
  51.         {  
  52.             states.Add(一个或多个);  
  53.             currentState = s;  
  54.             currentStateID = s.ID;  
  55.             返回;  
  56.         }  
  57.   
  58.         //如果状态不在列表中,则将状态添加到列表中  
  59.         的foreach  (FSMState状态   状态)  
  60.         {  
  61.             if  (state.ID == s.ID)  
  62.             {  
  63.                 Debug.LogError(“FSM ERROR:不可能添加状态”  + s.ID.ToString()+  
  64.                                “因为状态已经添加” );  
  65.                 返回;  
  66.             }  
  67.         }  
  68.         states.Add(一个或多个);  
  69.     }  
  70.   
  71.     /// <summary>  
  72.     ///此方法从FSM列表中删除状态(如果存在)   
  73.     ///如果状态不在列表中,则打印错误消息。  
  74.     /// </ summary>  
  75.     public void  DeleteState(StateID id)   
  76.     {  
  77.         //在删除之前检查NullState  
  78.         if  (id == StateID.NullStateID)  
  79.         {  
  80.             Debug.LogError(“FSM ERROR:NullStateID不允许为真实状态” );  
  81.             返回;  
  82.         }  
  83.   
  84.         //搜索列表并删除状态,如果它在里面  
  85.         的foreach  (FSMState状态   状态)  
  86.         {  
  87.             if  (state.ID == id)  
  88.             {  
  89.                 states.Remove(状态);  
  90.                 返回;  
  91.             }  
  92.         }  
  93.         Debug.LogError(“FSM ERROR:不可能删除状态”  + id.ToString()+  
  94.                        “不在州名单上” );  
  95.     }  
  96.   
  97.     /// <summary>  
  98.     ///这个方法试图改变FSM所处的状态  
  99.     ///当前状态和转换已过。如果当前状态  
  100.     ///没有转换的目标状态通过,   
  101.     ///打印出错误信息。  
  102.     /// </ summary>  
  103.     public void  PerformTransition(Transition trans)   
  104.     {  
  105.         //在更改当前状态之前检查NullTransition  
  106.         if  (trans == Transition.NullTransition)  
  107.         {  
  108.             Debug.LogError(“FSM错误:NullTransition不允许实际转换” );  
  109.             返回;  
  110.         }  
  111.   
  112.         //检查currentState是否具有作为参数传递的转换  
  113.         StateID id = currentState.GetOutputState(trans);  
  114.         if  (id == StateID.NullStateID)  
  115.         {  
  116.             Debug.LogError(“FSM ERROR:State”  + currentStateID.ToString()+  “没有目标状态”  +  
  117.                            “for transition”  + trans.ToString());  
  118.             返回;  
  119.         }  
  120.   
  121.         //更新currentStateID和currentState         
  122.         currentStateID = id;  
  123.         的foreach  (FSMState状态   状态)  
  124.         {  
  125.             if  (state.ID == currentStateID)  
  126.             {  
  127.                 //在设置新的状态之前进行后处理  
  128.                 currentState.DoBeforeLeaving();  
  129.   
  130.                 currentState = state;  
  131.   
  132.                 //将状态重置为所需状态,然后才能推理或行动  
  133.                 currentState.DoBeforeEntering();  
  134.                 打破;  
  135.             }  
  136.         }  
  137.   
  138.     }  // PerformTransition()  
  139. }  


角色控制状态机

角色的控制器通常也是通过状态机来实现。

首先要定义出角色的各种状态已经状态间的转换条件,就像这样:


通用unity状态机


接下来就是用代码定义各种状态的执行逻辑,跳转条件等。有些复杂的游戏还有通过分层的概念来处理角色的。

下面是最简单的两个状态,空闲和移动。

IdleState.cs

[csharp] 查看纯文本 
  1. 使用 UnityEngine;  
  2. 使用 System.Collections;  
  3.   
  4. 命名空间 CharacterFSM  
  5. {  
  6.     公共 IdleState:CharacterState   
  7.     {  
  8.         浮动 水平移动;  
  9.         浮动 垂直移动;  
  10.   
  11.         public  IdleState(Character _host)  
  12.         {  
  13.             host = _host;  
  14.             stateID = CharacterStateID.Idle;  
  15.         }  
  16.   
  17.         public override void  HandleInput(MovementInput movementInput)    
  18.         {  
  19.             horizo​​ntalMove = movingInput.moveStrafe;  
  20.             verticalMove = movingInput.moveForward;  
  21.         }  
  22.   
  23.         public override void  Act()    
  24.         {  
  25.   
  26.         }  
  27.         public override void  Reason()    
  28.         {  
  29.             if  (horizo​​ntalMove * horizo​​ntalMove + verticalMove * verticalMove <0.1f)  
  30.             {  
  31.                 返回;  
  32.             }  
  33.             其他  
  34.             {  
  35.                 host.stateController.SetTransition(CharacterStateTransition.ToMove);  
  36.             }  
  37.         }  
  38.   
  39.         public override void  DoBeforeEntering()    
  40.         {  
  41.             host.animator.SetBool(“Static_b” ,  true );  
  42.             host.animator.SetFloat(“Speed_f” ,0);  
  43.         }  
  44.     }  
  45.   
  46. }  


MoveState.cs

[csharp] 查看纯文本 
  1. 使用 UnityEngine;  
  2. 使用 System.Collections;  
  3.   
  4. 命名空间 CharacterFSM  
  5. {  
  6.     公共 MoveState:CharacterState   
  7.     {  
  8.         漂浮 stepDelta  
  9.         浮动 stepMark  
  10.   
  11.         public  MoveState(Character _host)  
  12.         {  
  13.             stepMark = -1f;  
  14.             stepDelta = 0.3f;  
  15.             host = _host;  
  16.             stateID = CharacterStateID.Move;  
  17.         }  
  18.   
  19.         public override void  HandleInput(MovementInput movementInput)    
  20.         {  
  21.    
  22.   
  23.             float  maxSpeed = host.MaxSpeed * Mathf.Sqrt(movingInput.moveStrafe * movingInput.moveStrafe + movingInput.moveForward * movingInput.moveForward);  
  24.   
  25.             host.CurrentSpeed - = 2 * host.Acceleration * Time.deltaTime;  
  26.             host.CurrentSpeed = Mathf.Max(maxSpeed,host.CurrentSpeed);  
  27.   
  28.             Vector2 tmp =  new  Vector2(movingInput.moveStrafe,movingInput.moveForward).normalized * host.CurrentSpeed;  
  29.             host.CurrentVelocity =  new  Vector3(tmp.x,0,tmp.y);  
  30.             host.animationController.SetSpeed(host.CurrentSpeed);  
  31.   
  32.         }  
  33.   
  34.         public override void  Act()    
  35.         {  
  36.         }  
  37.   
  38.         public override void  Reason()    
  39.         {  
  40.             if (host.CurrentSpeed <0.01f)  
  41.             {  
  42.                 host.stateController.SetTransition(CharacterStateTransition.ToIdle);  
  43.             }  
  44.         }  
  45.   
  46.         public override void  DoBeforeLeaving()    
  47.         {  
  48.           
  49.         }  
  50.   
  51.         public override void  DoBeforeEntering()    
  52.         {  
  53.             host.animationController.PerformMove();  
  54.         }  
  55.     }  
  56. }  


还有一个比较重要的类,CharacterStateController

[csharp] 查看纯文本 
  1. 使用 UnityEngine;  
  2. 使用 System.Collections;  
  3. 使用 CharacterFSM;  
  4.   
  5. public class  CharacterStateController {   
  6.   
  7.     CharacterFSMSystem characterFsm;  
  8.     性格特征  
  9.   
  10.     public  CharacterStateController(Character _character)  
  11.     {  
  12.         character = _character;  
  13.     }  
  14.   
  15.     public  CharacterStateID GetCurrentStateID()  
  16.     {  
  17.         返回 characterFsm.CurrentStateID;  
  18.     }  
  19.   
  20.     public void  Init()   
  21.     {  
  22.         ConstructFSM();  
  23.     }  
  24.       
  25.     //每帧调用一次更新  
  26.     public void  Update(){   
  27.         的debug.log(GetCurrentStateID());  
  28.         //Debug.Log(character.movementInput.moveForward +“”+ character.movementInput.moveStrafe);  
  29.         characterFsm.CurrentState.HandleInput(character.movementInput);  
  30.         characterFsm.CurrentState.Reason();  
  31.         characterFsm.CurrentState.Act();  
  32.     }  
  33.   
  34.   
  35.     public void  SetTransition(CharacterStateTransition t)   
  36.     {  
  37.         if  (characterFsm!=  null )  
  38.         {  
  39.             characterFsm.PerformTransition(T);  
  40.         }  
  41.     }  
  42.   
  43.     void  ConstructFSM()  
  44.     {  
  45.         IdleState idleState =  new  IdleState(character);  
  46.         idleState.AddTransition(CharacterStateTransition.ToMove,CharacterStateID.Move);  
  47.   
  48.         MoveState moveState =  new  MoveState(character);  
  49.         moveState.AddTransition(CharacterStateTransition.ToIdle,CharacterStateID.Idle);  
  50.   
  51.         characterFsm =  new  CharacterFSMSystem();  
  52.         characterFsm.AddState(idleState);  
  53.         characterFsm.AddState(moveState);  
  54.   
  55.     }  
  56. }  

这个类没必要声明称Monobehavior,只需要作为汉字的一个成员来处理就可以了。运行的效果是这样的

相关文章:

  • 2021-06-09
  • 2022-12-23
  • 2022-12-23
  • 2021-09-13
  • 2022-01-12
  • 2019-12-11
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-11-29
  • 2021-08-17
  • 2021-08-30
  • 2021-07-22
相关资源
相似解决方案