基于Unity有限状态机框架

摘要:
有两个转换,实际上是两个边,A-˃B,B-˃A。在框架中,Transition表示一个边,StateID表示一个状态,FSMState是一个抽象的状态类。最重要的是Reason方法,用于确定是否需要转换。例如,在B州,有人写道,如果全国人大在其前面15米处起主导作用,它将被转换,在转换期间将进行一些操作。Act方法是当前状态需要做的事情。在这个项目中,我努力写作。我在网上搜索,发现有限状态机有很多缺点。我认为只有几个州是非常方便的。我以后可以试试。

这个框架是Unity wiki上的框架。网址:http://wiki.unity3d.com/index.php/Finite_State_Machine

这就相当于是“模板”吧,自己写的代码,写啥都是一个风格,还是得多读书,多看报啊...

有限状态机用来做状态转移,比如代码中的例子,NPC有两个状态跟着主角走(状态A),或者沿着自己的路线走(状态B)。有两个转换,其实就是两条边,A->B,B->A.

框架里面Transition表示边,StateID表示状态,FSMState是一个状态的抽象类,最主要的是Reason方法,就是判断是不是要进行转换,像例子里B状态里写的就是如果npc前面15m有主角,就进行转换,转换的时候又要进行一些个操作。Act方法就是当前状态要做的一些事情。

具体也没用过这个框架,也不知道理解的对不对。。项目里,我都是硬写乱搞的,网上搜了搜说,有限状态机有很多缺点,我觉得简单的只有几个状态的话,也挺方便的,以后可以尝试一下。

框架:

基于Unity有限状态机框架第1张基于Unity有限状态机框架第2张
1 usingSystem;
2 usingSystem.Collections;
3 usingSystem.Collections.Generic;
4 usingUnityEngine;
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 /// </summary>
52 public enumTransition
53 {
54     NullTransition = 0, //Use this transition to represent a non-existing transition in your system
55 }
56  
57 /// <summary>
58 ///Place the labels for the States in this enum.
59 ///Don't change the first label, NullTransition as FSMSystem class uses it.
60 /// </summary>
61 public enumStateID
62 {
63     NullStateID = 0, //Use this ID to represent a non-existing State in your system    
64 }
65  
66 /// <summary>
67 ///This class represents the States in the Finite State System.
68 ///Each state has a Dictionary with pairs (transition-state) showing
69 ///which state the FSM should be if a transition is fired while this state
70 ///is the current state.
71 ///Method Reason is used to determine which transition should be fired .
72 ///Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
73 /// </summary>
74 public abstract classFSMState
75 {
76     protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
77     protectedStateID stateID;
78     public StateID ID { get { returnstateID; } }
79  
80     public voidAddTransition(Transition trans, StateID id)
81 {
82         //Check if anyone of the args is invalid
83         if (trans ==Transition.NullTransition)
84 {
85             Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
86             return;
87 }
88  
89         if (id ==StateID.NullStateID)
90 {
91             Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
92             return;
93 }
94  
95         //Since this is a Deterministic FSM,
96         //check if the current transition was already inside the map
97         if(map.ContainsKey(trans))
98 {
99             Debug.LogError("FSMState ERROR: State " + stateID.ToString() + "already has transition " + trans.ToString() + 
100                            "Impossible to assign to another state");
101             return;
102 }
103  
104 map.Add(trans, id);
105 }
106  
107     /// <summary>
108     ///This method deletes a pair transition-state from this state's map.
109     ///If the transition was not inside the state's map, an ERROR message is printed.
110     /// </summary>
111     public voidDeleteTransition(Transition trans)
112 {
113         //Check for NullTransition
114         if (trans ==Transition.NullTransition)
115 {
116             Debug.LogError("FSMState ERROR: NullTransition is not allowed");
117             return;
118 }
119  
120         //Check if the pair is inside the map before deleting
121         if(map.ContainsKey(trans))
122 {
123 map.Remove(trans);
124             return;
125 }
126         Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + "passed to " + stateID.ToString() + 
127                        "was not on the state's transition list");
128 }
129  
130     /// <summary>
131     ///This method returns the new state the FSM should be if
132     ///this state receives a transition and 
133     /// </summary>
134     publicStateID GetOutputState(Transition trans)
135 {
136         //Check if the map has this transition
137         if(map.ContainsKey(trans))
138 {
139             returnmap[trans];
140 }
141         returnStateID.NullStateID;
142 }
143  
144     /// <summary>
145     ///This method is used to set up the State condition before entering it.
146     ///It is called automatically by the FSMSystem class before assigning it
147     ///to the current state.
148     /// </summary>
149     public virtual voidDoBeforeEntering() { }
150  
151     /// <summary>
152     ///This method is used to make anything necessary, as reseting variables
153     ///before the FSMSystem changes to another one. It is called automatically
154     ///by the FSMSystem before changing to a new state.
155     /// </summary>
156     public virtual voidDoBeforeLeaving() { } 
157  
158     /// <summary>
159     ///This method decides if the state should transition to another on its list
160     ///NPC is a reference to the object that is controlled by this class
161     /// </summary>
162     public abstract voidReason(GameObject player, GameObject npc);
163  
164     /// <summary>
165     ///This method controls the behavior of the NPC in the game World.
166     ///Every action, movement or communication the NPC does should be placed here
167     ///NPC is a reference to the object that is controlled by this class
168     /// </summary>
169     public abstract voidAct(GameObject player, GameObject npc);
170  
171 } //class FSMState
172  
173  
174 /// <summary>
175 ///FSMSystem class represents the Finite State Machine class.
176 ///It has a List with the States the NPC has and methods to add,
177 ///delete a state, and to change the current state the Machine is on.
178 /// </summary>
179 public classFSMSystem
180 {
181     private List<FSMState>states;
182  
183     //The only way one can change the state of the FSM is by performing a transition
184     //Don't change the CurrentState directly
185     privateStateID currentStateID;
186     public StateID CurrentStateID { get { returncurrentStateID; } }
187     privateFSMState currentState;
188     public FSMState CurrentState { get { returncurrentState; } }
189  
190     publicFSMSystem()
191 {
192         states = new List<FSMState>();
193 }
194  
195     /// <summary>
196     ///This method places new states inside the FSM,
197     ///or prints an ERROR message if the state was already inside the List.
198     ///First state added is also the initial state.
199     /// </summary>
200     public voidAddState(FSMState s)
201 {
202         //Check for Null reference before deleting
203         if (s == null)
204 {
205             Debug.LogError("FSM ERROR: Null reference is not allowed");
206 }
207  
208         //First State inserted is also the Initial state,
209         //the state the machine is in when the simulation begins
210         if (states.Count == 0)
211 {
212 states.Add(s);
213             currentState =s;
214             currentStateID =s.ID;
215             return;
216 }
217  
218         //Add the state to the List if it's not inside it
219         foreach (FSMState state instates)
220 {
221             if (state.ID ==s.ID)
222 {
223                 Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() + 
224                                "because state has already been added");
225                 return;
226 }
227 }
228 states.Add(s);
229 }
230  
231     /// <summary>
232     ///This method delete a state from the FSM List if it exists, 
233     ///or prints an ERROR message if the state was not on the List.
234     /// </summary>
235     public voidDeleteState(StateID id)
236 {
237         //Check for NullState before deleting
238         if (id ==StateID.NullStateID)
239 {
240             Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
241             return;
242 }
243  
244         //Search the List and delete the state if it's inside it
245         foreach (FSMState state instates)
246 {
247             if (state.ID ==id)
248 {
249 states.Remove(state);
250                 return;
251 }
252 }
253         Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() + 
254                        ". It was not on the list of states");
255 }
256  
257     /// <summary>
258     ///This method tries to change the state the FSM is in based on
259     ///the current state and the transition passed. If current state
260     ///doesn't have a target state for the transition passed, 
261     ///an ERROR message is printed.
262     /// </summary>
263     public voidPerformTransition(Transition trans)
264 {
265         //Check for NullTransition before changing the current state
266         if (trans ==Transition.NullTransition)
267 {
268             Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
269             return;
270 }
271  
272         //Check if the currentState has the transition passed as argument
273         StateID id =currentState.GetOutputState(trans);
274         if (id ==StateID.NullStateID)
275 {
276             Debug.LogError("FSM ERROR: State " + currentStateID.ToString() +  "does not have a target state " + 
277                            "for transition " +trans.ToString());
278             return;
279 }
280  
281         //Update the currentStateID and currentState        
282         currentStateID =id;
283         foreach (FSMState state instates)
284 {
285             if (state.ID ==currentStateID)
286 {
287                 //Do the post processing of the state before setting the new one
288 currentState.DoBeforeLeaving();
289  
290                 currentState =state;
291  
292                 //Reset the state to its desired condition before it can reason or act
293 currentState.DoBeforeEntering();
294                 break;
295 }
296 }
297  
298     } //PerformTransition()
299  
300 } //class FSMSystem
View Code

例子:

基于Unity有限状态机框架第3张基于Unity有限状态机框架第4张
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingUnityEngine;
 
[RequireComponent(typeof(Rigidbody))]
public classNPCControl : MonoBehaviour
{
    publicGameObject player;
    publicTransform[] path;
    privateFSMSystem fsm;
 
    public voidSetTransition(Transition t) { fsm.PerformTransition(t); }
 
    public voidStart()
    {
        MakeFSM();
    }
 
    public voidFixedUpdate()
    {
        fsm.CurrentState.Reason(player, gameObject);
        fsm.CurrentState.Act(player, gameObject);
    }
 
    //The NPC has two states: FollowPath and ChasePlayer
    //If it's on the first state and SawPlayer transition is fired, it changes to ChasePlayer
    //If it's on ChasePlayerState and LostPlayer transition is fired, it returns to FollowPath
    private voidMakeFSM()
    {
        FollowPathState follow = newFollowPathState(path);
        follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);
 
        ChasePlayerState chase = newChasePlayerState();
        chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);
 
        fsm = newFSMSystem();
        fsm.AddState(follow);
        fsm.AddState(chase);
    }
}
 
public classFollowPathState : FSMState
{
    private intcurrentWayPoint;
    privateTransform[] waypoints;
 
    publicFollowPathState(Transform[] wp) 
    { 
        waypoints =wp;
        currentWayPoint = 0;
        stateID =StateID.FollowingPath;
    }
 
    public override voidReason(GameObject player, GameObject npc)
    {
        //If the Player passes less than 15 meters away in front of the NPC
RaycastHit hit;
        if (Physics.Raycast(npc.transform.position, npc.transform.forward, outhit, 15F))
        {
            if (hit.transform.gameObject.tag == "Player")
                npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);
        }
    }
 
    public override voidAct(GameObject player, GameObject npc)
    {
        //Follow the path of waypoints
        //Find the direction of the current way point 
        Vector3 vel =npc.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 * 10;
 
            //Rotate towards the waypoint
            npc.transform.rotation =Quaternion.Slerp(npc.transform.rotation,
                                                      Quaternion.LookRotation(moveDir),
                                                      5 *Time.deltaTime);
            npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);
 
        }
 
        //Apply the Velocity
        npc.rigidbody.velocity =vel;
    }
 
} //FollowPathState
 
public classChasePlayerState : FSMState
{
    publicChasePlayerState()
    {
        stateID =StateID.ChasingPlayer;
    }
 
    public override voidReason(GameObject player, GameObject npc)
    {
        //If the player has gone 30 meters away from the NPC, fire LostPlayer transition
        if (Vector3.Distance(npc.transform.position, player.transform.position) >= 30)
            npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
    }
 
    public override voidAct(GameObject player, GameObject npc)
    {
        //Follow the path of waypoints
        //Find the direction of the player         
        Vector3 vel =npc.rigidbody.velocity;
        Vector3 moveDir = player.transform.position -npc.transform.position;
 
        //Rotate towards the waypoint
        npc.transform.rotation =Quaternion.Slerp(npc.transform.rotation,
                                                  Quaternion.LookRotation(moveDir),
                                                  5 *Time.deltaTime);
        npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);
 
        vel = moveDir.normalized * 10;
 
        //Apply the new Velocity
        npc.rigidbody.velocity =vel;
    }
 
} //ChasePlayerState
View Code

免责声明:文章转载自《基于Unity有限状态机框架》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇获取谷歌浏览器缓存视频方法使用 async-await 简化代码的检讨下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

unity shader序列帧动画代码,顺便吐槽一下unity shader系统

一、看到UNITY论坛里有些人求unity shader序列帧动画,写shader我擅长啊,就顺势写了个CG的shader。代码很简单,就是变换UV采样序列帧贴图,美术配置行数列数以及变换速度。Shader "HELLOHUAN/Hello_Sequence" { Properties { _Color ("Main Color", Colo...

CSS动画:animation、transition、transform、translate

https://blog.csdn.net/px01ih8/article/details/80780470 一、区分容易混淆的几个属性和值 先区分一下css中的几个属性:animation(动画)、transition(过渡)、transform(变形)、translate(移动)。 CSS3中的transform(变形)属性用于内联元素和块级元素,可...

Unity琐碎(3) UGUI 图文混排解决方案和优化

感觉使用Unity之后总能看到各种各样解决混排的方案,只能说明Unity不够体恤下情啊。这篇文章主要讲一下个人在使用过程中方案选择和优化过程,已做记录。顺便提下,开源很多意味着坑,还是要开实际需求。 1. 方案选择 1 TextMeshPro Unity 最近公布收购了TextMeshPro并且免费开源给大家使用,估计还需要几个小版本才会完全融合到Unit...

【Unity3D】实现太阳系

    实践要求:写一个程序,实现一个完整的太阳系,其他星球围绕太阳的转速必须不一样,并且不再一个法平面内。     法平面是指过空间曲线的切点,且与切线垂直的平面。要求不在一个法平面内,则在保证所有行星以及太阳在一条轴上时,另外两条轴的比例不相同即可。公转速度在RotateAround参数里面设置。如:     这个程序在课堂程序的基础上完成,使用了预...

Unity基本操作

主要内容: C#学习 Unity项目 打砖块:BreakBricks Unity操作 Unity操作: 调试 碰撞体 触发器 视角键盘视角平移 光照贴图 游戏对象Gameobject 访问对象 实体化对象 Instantiate 得到组件 对象的移动 交互类 鼠标输入 键盘输入 调试 在某函数内进行`Debug.Log(...)...

Unity3d之音效播放和调用手机震动

http://blog.csdn.net/sunshine_1984/article/details/12943979 今天研究了下Unity3d音效播放相关内容,整理下实现细节。 1,添加音效文件到Assets/Resources目录,我这里添加到Assets/Resources/audio目录了。 2,新建C# Script命名为audio 3,编写a...