Finite State Machine
2014-12-16 23:23
363 查看
Contents[ |
Description
ThisisaDeterministicFiniteStateMachineframeworkbasedonchapter3.1ofGameProgrammingGems1byEricDybsend.Thereaaretwoclassesandtwoenums.IncludetheminyourprojectandfollowtheexplanationstogettheFSMworkingproperly.There'salsoacompleteexamplescriptattheendofthispage.Components
Transitionenum:Thisenumcontainsthelabelstothetransitionsthatcanbefiredbythesystem.Don'tchangethefirstlabel,NullTransition,astheFSMSystemclassusesit.StateIDenum:ThisistheIDofthestatesthegamemayhave.YoucouldusereferencestotherealStates'classesbutusingenumsmakesthesystemlesssusceptibletohavecodehavingaccesstoobjectsitisnotsupposedto.Allthestates'idsshouldbeplacedhere.Don'tchangethefirstlabel,NullStateID,astheFSMSystemclassusesit.
FSMStateclass:ThisclasshasaDictionarywithpairs(Transition-StateID)indicatingwhichnewstateS2theFSMshouldgotowhenatransitionTisfiredandthecurrentstateisS1.Ithasmethodstoaddanddeletepairs(Transition-StateID),amethodtocheckwhichstatetogotoifatransitionispassedtoit.Twomethodsareusedintheexamplegiventocheckwhichtransitionshouldbefired(Reason())andwhichaction(s)(Act())theGameObjectthathastheFSMStateattachedshoulddo.Youdon'thavetousethisschema,butsomekindoftransition-actioncodemustbeusedinyourgame.
FSMSystem:ThisistheFiniteStateMachineclassthateachNPCorGameObjectinyourgamemusthaveinordertousetheframework.ItstorestheNPC'sStatesinaList,hasmethodstoaddanddeleteastateandamethodtochangethecurrentstatebasedonatransitionpassedtoit(PerformTransition()).Youcancallthismethodanywherewithinyourcode,asinacollisiontest,orwithinUpdate()orFixedUpdate().
C#-FSMSystem.cs
usingSystem; usingSystem.Collections; usingSystem.Collections.Generic; usingUnityEngine; /** AFiniteStateMachineSystembasedonChapter3.1ofGameProgrammingGems1byEricDybsand WrittenbyRobertoCezarBianchini,July2010 Howtouse: 1.PlacethelabelsforthetransitionsandthestatesoftheFiniteStateSystem inthecorrespondingenums. 2.Writenewclass(es)inheritingfromFSMStateandfilleachonewithpairs(transition-state). ThesepairsrepresentthestateS2theFSMSystemshouldbeifwhilebeingonstateS1,a transitionTisfiredandstateS1hasatransitionfromittoS2.RememberthisisaDeterministicFSM. Youcan'thaveonetransitionleadingtotwodifferentstates. MethodReasonisusedtodeterminewhichtransitionshouldbefired. Youcanwritethecodetofiretransitionsinanotherplace,andleavethismethodemptyifyou feelit'smoreappropriatetoyourproject. MethodActhasthecodetoperformtheactionstheNPCissupposeddoifit'sonthisstate. Youcanwritethecodefortheactionsinanotherplace,andleavethismethodemptyifyou feelit'smoreappropriatetoyourproject. 3.CreateaninstanceofFSMSystemclassandaddthestatestoit. 4.CallReasonandAct(orwhichevermethodsyouhaveforfiringtransitionsandmakingtheNPCs behaveinyourgame)fromyourUpdateorFixedUpdatemethods. AsynchronoustransitionsfromUnityEngine,likeOnTriggerEnter,SendMessage,canalsobeused, justcalltheMethodPerformTransitionfromyourFSMSysteminstancewiththecorrectTransition whentheeventoccurs. THESOFTWAREISPROVIDED"ASIS",WITHOUTWARRANTYOFANYKIND,EXPRESSORIMPLIED, INCLUDINGBUTNOTLIMITEDTOTHEWARRANTIESOFMERCHANTABILITY,FITNESSFORAPARTICULARPURPOSE ANDNON-INFRINGEMENT.INNOEVENTSHALLTHEAUTHORSORCOPYRIGHTHOLDERSBELIABLEFORANYCLAIM, DAMAGESOROTHERLIABILITY,WHETHERINANACTIONOFCONTRACT,TORTOROTHERWISE,ARISINGFROM, OUTOFORINCONNECTIONWITHTHESOFTWAREORTHEUSEOROTHERDEALINGSINTHESOFTWARE. */ ///<summary> ///PlacethelabelsfortheTransitionsinthisenum. ///Don'tchangethefirstlabel,NullTransitionasFSMSystemclassusesit. ///</summary> publicenumTransition { NullTransition=0,//Usethistransitiontorepresentanon-existingtransitioninyoursystem } ///<summary> ///PlacethelabelsfortheStatesinthisenum. ///Don'tchangethefirstlabel,NullTransitionasFSMSystemclassusesit. ///</summary> publicenumStateID { NullStateID=0,//UsethisIDtorepresentanon-existingStateinyoursystem } ///<summary> ///ThisclassrepresentstheStatesintheFiniteStateSystem. ///EachstatehasaDictionarywithpairs(transition-state)showing ///whichstatetheFSMshouldbeifatransitionisfiredwhilethisstate ///isthecurrentstate. ///MethodReasonisusedtodeterminewhichtransitionshouldbefired. ///MethodActhasthecodetoperformtheactionstheNPCissupposeddoifit'sonthisstate. ///</summary> publicabstractclassFSMState { protectedDictionary<Transition,StateID>map=newDictionary<Transition,StateID>(); protectedStateIDstateID; publicStateIDID{get{returnstateID;}} publicvoidAddTransition(Transitiontrans,StateIDid) { //Checkifanyoneoftheargsisinvalid if(trans==Transition.NullTransition) { Debug.LogError("FSMStateERROR:NullTransitionisnotallowedforarealtransition"); return; } if(id==StateID.NullStateID) { Debug.LogError("FSMStateERROR:NullStateIDisnotallowedforarealID"); return; } //SincethisisaDeterministicFSM, //checkifthecurrenttransitionwasalreadyinsidethemap if(map.ContainsKey(trans)) { Debug.LogError("FSMStateERROR:State"+stateID.ToString()+"alreadyhastransition"+trans.ToString()+ "Impossibletoassigntoanotherstate"); return; } map.Add(trans,id); } ///<summary> ///Thismethoddeletesapairtransition-statefromthisstate'smap. ///Ifthetransitionwasnotinsidethestate'smap,anERRORmessageisprinted. ///</summary> publicvoidDeleteTransition(Transitiontrans) { //CheckforNullTransition if(trans==Transition.NullTransition) { Debug.LogError("FSMStateERROR:NullTransitionisnotallowed"); return; } //Checkifthepairisinsidethemapbeforedeleting if(map.ContainsKey(trans)) { map.Remove(trans); return; } Debug.LogError("FSMStateERROR:Transition"+trans.ToString()+"passedto"+stateID.ToString()+ "wasnotonthestate'stransitionlist"); } ///<summary> ///ThismethodreturnsthenewstatetheFSMshouldbeif ///thisstatereceivesatransitionand ///</summary> publicStateIDGetOutputState(Transitiontrans) { //Checkifthemaphasthistransition if(map.ContainsKey(trans)) { returnmap[trans]; } returnStateID.NullStateID; } ///<summary> ///ThismethodisusedtosetuptheStateconditionbeforeenteringit. ///ItiscalledautomaticallybytheFSMSystemclassbeforeassigningit ///tothecurrentstate. ///</summary> publicvirtualvoidDoBeforeEntering(){} ///<summary> ///Thismethodisusedtomakeanythingnecessary,asresetingvariables ///beforetheFSMSystemchangestoanotherone.Itiscalledautomatically ///bytheFSMSystembeforechangingtoanewstate. ///</summary> publicvirtualvoidDoBeforeLeaving(){} ///<summary> ///Thismethoddecidesifthestateshouldtransitiontoanotheronitslist ///NPCisareferencetotheobjectthatiscontrolledbythisclass ///</summary> publicabstractvoidReason(GameObjectplayer,GameObjectnpc); ///<summary> ///ThismethodcontrolsthebehavioroftheNPCinthegameWorld. ///Everyaction,movementorcommunicationtheNPCdoesshouldbeplacedhere ///NPCisareferencetotheobjectthatiscontrolledbythisclass ///</summary> publicabstractvoidAct(GameObjectplayer,GameObjectnpc); }//classFSMState ///<summary> ///FSMSystemclassrepresentstheFiniteStateMachineclass. ///IthasaListwiththeStatestheNPChasandmethodstoadd, ///deleteastate,andtochangethecurrentstatetheMachineison. ///</summary> publicclassFSMSystem { privateList<FSMState>states; //TheonlywayonecanchangethestateoftheFSMisbyperformingatransition //Don'tchangetheCurrentStatedirectly privateStateIDcurrentStateID; publicStateIDCurrentStateID{get{returncurrentStateID;}} privateFSMStatecurrentState; publicFSMStateCurrentState{get{returncurrentState;}} publicFSMSystem() { states=newList<FSMState>(); } ///<summary> ///ThismethodplacesnewstatesinsidetheFSM, ///orprintsanERRORmessageifthestatewasalreadyinsidetheList. ///Firststateaddedisalsotheinitialstate. ///</summary> publicvoidAddState(FSMStates) { //CheckforNullreferencebeforedeleting if(s==null) { Debug.LogError("FSMERROR:Nullreferenceisnotallowed"); } //FirstStateinsertedisalsotheInitialstate, //thestatethemachineisinwhenthesimulationbegins if(states.Count==0) { states.Add(s); currentState=s; currentStateID=s.ID; return; } //AddthestatetotheListifit'snotinsideit foreach(FSMStatestateinstates) { if(state.ID==s.ID) { Debug.LogError("FSMERROR:Impossibletoaddstate"+s.ID.ToString()+ "becausestatehasalreadybeenadded"); return; } } states.Add(s); } ///<summary> ///ThismethoddeleteastatefromtheFSMListifitexists, ///orprintsanERRORmessageifthestatewasnotontheList. ///</summary> publicvoidDeleteState(StateIDid) { //CheckforNullStatebeforedeleting if(id==StateID.NullStateID) { Debug.LogError("FSMERROR:NullStateIDisnotallowedforarealstate"); return; } //SearchtheListanddeletethestateifit'sinsideit foreach(FSMStatestateinstates) { if(state.ID==id) { states.Remove(state); return; } } Debug.LogError("FSMERROR:Impossibletodeletestate"+id.ToString()+ ".Itwasnotonthelistofstates"); } ///<summary> ///ThismethodtriestochangethestatetheFSMisinbasedon ///thecurrentstateandthetransitionpassed.Ifcurrentstate ///doesn'thaveatargetstateforthetransitionpassed, ///anERRORmessageisprinted. ///</summary> publicvoidPerformTransition(Transitiontrans) { //CheckforNullTransitionbeforechangingthecurrentstate if(trans==Transition.NullTransition) { Debug.LogError("FSMERROR:NullTransitionisnotallowedforarealtransition"); return; } //CheckifthecurrentStatehasthetransitionpassedasargument StateIDid=currentState.GetOutputState(trans); if(id==StateID.NullStateID) { Debug.LogError("FSMERROR:State"+currentStateID.ToString()+"doesnothaveatargetstate"+ "fortransition"+trans.ToString()); return; } //UpdatethecurrentStateIDandcurrentState currentStateID=id; foreach(FSMStatestateinstates) { if(state.ID==currentStateID) { //Dothepostprocessingofthestatebeforesettingthenewone currentState.DoBeforeLeaving(); currentState=state; //Resetthestatetoitsdesiredconditionbeforeitcanreasonoract currentState.DoBeforeEntering(); break; } } }//PerformTransition() }//classFSMSystem
Example
Here'sanexamplethatimplementstheaboveframework.TheGameObjectwiththisscriptfollowsapathofwaypointsandstartschasingatargetifitcomeswithinacertaindistancefromit.AttachthisclasstoyourNPC.Besidestheframeworktransitionandstateidenums,youalsohavetosetupareferencetothewaypointsandtothetarget(player).IusedFixedUpdate()intheMonoBehaviourbecausetheNPCreasoningschemadoesn'tneedtobecalledeveryframe,butyoucanchangethatanduseUpdate().usingSystem; usingSystem.Collections.Generic; usingSystem.Text; usingUnityEngine; [RequireComponent(typeof(Rigidbody))] publicclassNPCControl:MonoBehaviour { publicGameObjectplayer; publicTransform[]path; privateFSMSystemfsm; publicvoidSetTransition(Transitiont){fsm.PerformTransition(t);} publicvoidStart() { MakeFSM(); } publicvoidFixedUpdate() { fsm.CurrentState.Reason(player,gameObject); fsm.CurrentState.Act(player,gameObject); } //TheNPChastwostates:FollowPathandChasePlayer //Ifit'sonthefirststateandSawPlayertransitionisfired,itchangestoChasePlayer //Ifit'sonChasePlayerStateandLostPlayertransitionisfired,itreturnstoFollowPath privatevoidMakeFSM() { FollowPathStatefollow=newFollowPathState(path); follow.AddTransition(Transition.SawPlayer,StateID.ChasingPlayer); ChasePlayerStatechase=newChasePlayerState(); chase.AddTransition(Transition.LostPlayer,StateID.FollowingPath); fsm=newFSMSystem(); fsm.AddState(follow); fsm.AddState(chase); } } publicclassFollowPathState:FSMState { privateintcurrentWayPoint; privateTransform[]waypoints; publicFollowPathState(Transform[]wp) { waypoints=wp; currentWayPoint=0; stateID=StateID.FollowingPath; } publicoverridevoidReason(GameObjectplayer,GameObjectnpc) { //IfthePlayerpasseslessthan15metersawayinfrontoftheNPC RaycastHithit; if(Physics.Raycast(npc.transform.position,npc.transform.forward,outhit,15F)) { if(hit.transform.gameObject.tag=="Player") npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer); } } publicoverridevoidAct(GameObjectplayer,GameObjectnpc) { //Followthepathofwaypoints //Findthedirectionofthecurrentwaypoint Vector3vel=npc.rigidbody.velocity; Vector3moveDir=waypoints[currentWayPoint].position-npc.transform.position; if(moveDir.magnitude<1) { currentWayPoint++; if(currentWayPoint>=waypoints.Length) { currentWayPoint=0; } } else { vel=moveDir.normalized*10; //Rotatetowardsthewaypoint npc.transform.rotation=Quaternion.Slerp(npc.transform.rotation,Quaternion.LookRotation(moveDir),5*Time.deltaTime); npc.transform.eulerAngles=newVector3(0,npc.transform.eulerAngles.y,0); } //ApplytheVelocity npc.rigidbody.velocity=vel; } }//FollowPathState publicclassChasePlayerState:FSMState { publicChasePlayerState() { stateID=StateID.ChasingPlayer; } publicoverridevoidReason(GameObjectplayer,GameObjectnpc) { //Iftheplayerhasgone30metersawayfromtheNPC,fireLostPlayertransition if(Vector3.Distance(npc.transform.position,player.transform.position)>=30) npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer); } publicoverridevoidAct(GameObjectplayer,GameObjectnpc) { //Followthepathofwaypoints //Findthedirectionoftheplayer Vector3vel=npc.rigidbody.velocity; Vector3moveDir=player.transform.position-npc.transform.position; //Rotatetowardsthewaypoint npc.transform.rotation=Quaternion.Slerp(npc.transform.rotation, Quaternion.LookRotation(moveDir), 5*Time.deltaTime); npc.transform.eulerAngles=newVector3(0,npc.transform.eulerAngles.y,0); vel=moveDir.normalized*10; //ApplythenewVelocity npc.rigidbody.velocity=vel; } }//ChasePlayerState
相关文章推荐
- 译:Finite State Machine Datapath Design, Optimization, and Implementation(一)
- Recommended FSM (Finite State Machine) Library for Java [closed]
- 【Unity】Finite State Machine 有限状态机
- Unity 有限状态机(Finite State Machine)的理解 与 实现简单的可插拔(Pluggable)AI脚本对象。
- Finite State Machine
- The finite state machine
- FSM (Finite State Machine Framework)源码共享
- Finite State Machine 是什么?
- 有限状态机——The finite state machine
- Finite State Machine 有限状态机
- paper:synthesizable finite state machine design techniques using the new systemverilog 3.0 enhancements 之 FSM Coding Goals
- paper:synthesizable finite state machine design techniques using the new systemverilog 3.0 enhancements 之 standard verilog FSM conding styles(二段式)
- paper:synthesizable finite state machine design techniques using the new systemverilog 3.0 enhancements 之 standard verilog FSM conding styles(三段式)
- Finite State Machine library Introduction
- Are Osworkflow and Activiti based on Finite State Machine(FSM)?
- 有限状态机(Finite State Machine)在游戏中的应用和实现
- FSM(finite state machine)
- Finite State Machine library V1.0 Release
- state machine diagram
- HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\AllowRemoteConnection