开始的时候想用lua写的 比较简便 代码也有现成的 


但是考虑到还是以学习为主 就把思想转到cs实现一遍 


这篇文章可以认为是对前几天的解决Unity多线程问题中的状态机的一个简单实现




先来看看将要实现的状态机(本人不想画图 喜欢用文字描述 后期修改再说吧。。。)


共 5 种状态

wait
move
attack_a
attack_b
power

将要实现的过程是:

在wait状态下:

可以转到 move 移动(这里有 3 秒的判断时间) 可以转到 attack_a 攻击 或者 attack_b 攻击

move:

可回到wait 可转到attack_a攻击

attack_a状态:

可转到wait

attack_b状态:

可转到power 可转到wait (这里有 3 秒的判断时间)

power状态:

可转到wait


通过以上的信息 应该能够自行画出状态图 至于其中每个状态的动作 在下面的代码中就可以看到


主要代码:



FSMBase.cs 

 using UnityEngine; 

 using System.Collections; 





 public abstract class FSMBase 

 { 



      public abstract void HandleActor(Actor actor); 

 

 } 

 FSMMain.cs 

 using UnityEngine; 

 using System.Collections; 

 using System.Collections.Generic; 

 //状态 

 public enum State 

 { 

     WAIT, 

     MOVE, 

     ATTACK_A, 

     ATTACK_B, 

     POWER, 

 } 

 //动作 

 public enum Actor 

 { 

     KEY_W, 

     MOUSE_LEFT, 

     MOUSE_RIGHT, 

     WAIT_TIME, 

     IMMEDIATELY, 

     KEY_SPACE 

 } 





 public class FSMMain : MonoBehaviour 

 { 



     public static FSMMain _instance; 



     public State currState; 

     List<Actor> actorList = new List<Actor>();                              //储存执行的动作 

     Dictionary<State, FSMBase> stateDic = new Dictionary<State, FSMBase>(); //储存状态和状态类的映射 

     public void StartActor(Actor actor) 

     { 

         actorList.Add(actor); 

     } 

     public void ChangeState(State state) 

     { 

         this.currState = state; 

         Debug.Log("currState changed : " + this.currState); 





     } 

     private void RegistState() 

     { 

         stateDic.Add(State.WAIT, new FSMWait()); 

         stateDic.Add(State.MOVE, new FSMMove()); 

         stateDic.Add(State.ATTACK_A, new FSMAttackA()); 

         stateDic.Add(State.ATTACK_B, new FSMAttackB()); 

         stateDic.Add(State.POWER, new FSMPower()); 


     } 



     // Use this for initialization 

     void Start() 

     { 

          

         RegistState(); 

         _instance = this; 

     } 


     // Update is called once per frame 

     void Update() 

     { 

         

         if (actorList.Count > 0) 

         { 

             Actor actor = actorList[0]; 

             FSMBase fsmBase; 

             if (stateDic.TryGetValue(currState, out fsmBase)) 

             { 

                 fsmBase.HandleActor(actor); 

                 actorList.RemoveAt(0); 

                  

             } 

         } 


     } 

 } 



 TestMain.cs 

 using UnityEngine; 

 using System.Collections; 



 public class TestMain : MonoBehaviour 

 { 

     // Use this for initialization 

     void Start() 

     { 


     } 



     // Update is called once per frame 

     void Update() 

     { 


         if (Input.GetKeyDown(KeyCode.W)) 

         { 

             FSMMain._instance.StartActor(Actor.KEY_W); 

             Invoke("MoveWait", 3); 


         } 

         if (Input.GetKeyDown(KeyCode.Space)) 

         { 

             FSMMain._instance.StartActor(Actor.KEY_SPACE); 

            

         } 

         if (Input.GetMouseButtonDown(0)) 

         { 

             FSMMain._instance.StartActor(Actor.MOUSE_LEFT); 

         } 

         if (Input.GetMouseButtonDown(1)) 

         { 

             FSMMain._instance.StartActor(Actor.MOUSE_RIGHT); 

             Invoke("PowerWait",3); 


         } 


     } 

     //这里的代码可以 FSMPower 中实现 个人感觉可以不用判断状态 因为判断状态对状态机有了一定的偏离 所以可以用控制时间的方法比较好 

     //虽然代码多了一点 不过对这个功能而言 可以设置个判断是否处于 “大招等待” 的变量isPower 

     //如果 3s 中没有任何动作 直接回到 wait 状态 一旦在 attack_b 中有了其他动作 那么变量


设为 false 在 update 就不会执行计算时间 那么就会到其他动作的状态 而不会进入 wait 状态


 


void PowerWait() 

     { 

         if (FSMMain._instance.currState == State.ATTACK_B) 

         { 

             FSMMain._instance.StartActor(Actor.IMMEDIATELY); 

         } 

         else 

         { 

             print("currState is not attack_b So do nothing"); 

         } 

     } 




     void MoveWait() 

     { 

         if (FSMMain._instance.currState == State.MOVE) 

         { 

             FSMMain._instance.StartActor(Actor.IMMEDIATELY); 

         } 

         else 

         { 

             print("currState is not move So do nothing"); 

         } 

     } 

 } 



 其他代码:(其他代码略 格式与下相同) 

 FSMWait.cs 

 using UnityEngine; 

 using System.Collections; 



 public class FSMWait : FSMBase { 



     public override void HandleActor(Actor actor) 

     { 

         if (actor == Actor.KEY_W) 

         { 


             FSMMain._instance.ChangeState(State.MOVE); 

            

         } 

         if (actor == Actor.MOUSE_LEFT) 

         { 





             FSMMain._instance.ChangeState(State.ATTACK_A); 

             //这里处理其他的的动作 

             //然后可以在这里等待 1 s 来结束该动作 这里不做演示 直接进入wait状态 





             //这里由于是完成后立即回到wait状态 所以用ChangeState也可以 

             FSMMain._instance.StartActor(Actor.IMMEDIATELY); 

             

            

         } 

         if (actor == Actor.MOUSE_RIGHT) 

         { 

               

             FSMMain._instance.ChangeState(State.ATTACK_B); 

             Debug.Log("currState changed : " + FSMMain._instance.currState); 

           

         } 

         

     } 

 }



开始的时候我认为很容易实现 但是真正做的时候还是不算太容易


在这里说明一下 这是Unity c#工程中的使用 在lua中的实现比这个要容易一些 


毕竟Unity中热更新的逻辑部分大部分都在 lua 脚本中 所以可以试一试用lua脚本实现状态机


其他的cs参考代码我还不知道怎么传呢。。。。。。毕竟刚用没几天。。。(FSMMain.cs 和 TestMain.cs 都随便挂在物体上即可)