A Finite State Machine (FSM) for Unity
Today’s article introduces a small but capable and extensible finite state machine (FSM) that you can use to organize your Unity applications’ code. FSMs are commonly used to represent the different modes the app can be in—main menu, world map, gameplay, game over—and how the app transitions from one state to another. The FSM in today’s article follows the pure code methodology by not tying the code to game objects, MonoBehaviour
s, or scenes. So read on to learn about and make use of this “pure code” FSM for Unity!
There are three main concepts in this FSM:
- State: a mode of the app that executes over time
- Transition: the process of moving from one state to another
- StateMachine: runs the current state and transitions to the next state
Let’s see how this manifests in code by first looking at the state:
/// <summary> /// A mode of the app that executes over time. This begins the transition process and receives /// notifications about its progress. /// </summary> public interface IState { /// <summary> /// Notify the state that the transition process has begun the entering phase /// </summary> void BeginEnter(); /// <summary> /// Notify the state that the transition process has finished the entering phase /// </summary> void EndEnter(); /// <summary> /// Execute the state's logic over time. This function should 'yield return' until it has /// nothing left to do. It may also dispatch OnBeginExit when it is ready to begin transitioning /// to the next state. If it does this, this funtion will no longer be resumed. /// </summary> IEnumerable Execute(); /// <summary> /// Dispatched when the state is ready to begin transitioning to the next state. This implies /// that the transition process will immediately begin the exiting phase. /// </summary> event EventHandler<StateBeginExitEventArgs> OnBeginExit; /// <summary> /// Notify the state that the transition process has finished exiting phase. /// </summary> void EndExit(); }
The most important function of IState
is Execute
. It’s similar to an Update
or Tick
function that does—or initiates—all of the logic the state performs over time. The other functions relate to starting transitions to other states and being notified about the transition’s progress.
The next concept is that of a transition. Here’s how that looks in code:
/// <summary> /// A transition from one state to another. This is broken into two parts. The first is the 'exit' /// process where the current state is left. The second is the 'enter' process where the next state /// becomes the new current state. Both of these processes are executed over time. /// </summary> public interface IStateTransition { /// <summary> /// Exit the current state over time. This function should 'yield return' until the exit process /// is finished. /// </summary> IEnumerable Exit(); /// <summary> /// Enter the next state over time. This function should 'yield return' until the enter process /// is finished. /// </summary> IEnumerable Enter(); }
The transition is really just made up of two iterator functions—Exit
and Enter
—to represent the two phases of the transition: exiting the old state and entering the new state.
The final concept is exceedingly simple: the state machine itself.
/// <summary> /// A finite state machine that runs a single state at a time and handles the transition process /// from one state to another /// </summary> public interface IStateMachine { /// <summary> /// Execute the state machine by executing states and transitions. This function should /// 'yield return' until there are no more states to execute. /// </summary> IEnumerable Execute(); }
The state machine simply has a function to execute it over time. This function in turn executes all the states and transitions.
With the three main concepts covered, let’s take a look at the final part consisting of two concrete classes. We’ll start with the EventArgs
class that is dispatched when a state wants to transition to another state:
/// <summary> /// Event args that are dispatched when a state wants to transition to another state /// </summary> public class StateBeginExitEventArgs : EventArgs { /// <summary> /// The state to transition to /// </summary> public IState NextState { get; private set; } /// <summary> /// The transition to use to get to the next state /// </summary> public IStateTransition Transition { get; private set; } /// <summary> /// Create the event args /// </summary> /// <param name="nextState">The state to transition to</param> /// <param name="transition">The transition to use to get to the next state</param> public StateBeginExitEventArgs( IState nextState, IStateTransition transition ) { NextState = nextState; Transition = transition; } }
StateBeginExitEventArgs
is just a wrapper for the two pieces of information that a state machine would need to execute the transition to the next state. That is, which state are we going to and how do we get there?
Finally, the last class is the actual StateMachine
that implements IStateMachine
to actually execute the states and transitions.
/// <summary> /// A state machine implementation that executes states and transitions /// </summary> public class StateMachine : IStateMachine { /// <summary> /// The current state /// </summary> private IState state; /// <summary> /// The next state (to transition to) /// </summary> private IState nextState; /// <summary> /// Transition to use to get to the next state /// </summary> private IStateTransition transition; /// <summary> /// Create the state machine with an initial state /// </summary> /// <param name="initialState">Initial state. BeginEnter() and EndEnter() will be called /// on it immediately</param> public StateMachine(IState initialState) { State = initialState; state.EndEnter(); } /// <summary> /// Execute the initial state and any subsequent states and transitions until there are no more /// states to execute. /// </summary> public IEnumerable Execute() { while (true) { // Execute the current state until it transitions or stops executing for ( var e = state.Execute().GetEnumerator(); transition == null && e.MoveNext(); ) { yield return e.Current; } // Wait until the current state transitions while (transition == null) { yield return null; } // Stop listening for the current state to transition // This prevents accidentally transitioning twice state.OnBeginExit -= HandleStateBeginExit; // There is no next state to transition to // This means the state machine is finished executing if (nextState == null) { break; } // Run the transition's exit process foreach (var e in transition.Exit()) { yield return e; } state.EndExit(); // Switch state State = nextState; nextState = null; // Run the transition's enter process foreach (var e in transition.Enter()) { yield return e; } transition = null; state.EndEnter(); } } /// <summary> /// Set the current state, call its BeginEnter(), and listen for it to transition /// </summary> /// <value>The state to be the new current state</value> private IState State { set { state = value; state.OnBeginExit += HandleStateBeginExit; state.BeginEnter(); } } /// <summary> /// Handles the current state wanting to transition /// </summary> /// <param name="sender">The state that wants to transition</param> /// <param name="e">Information about the desired transition</param> private void HandleStateBeginExit(object sender, StateBeginExitEventArgs e) { nextState = e.NextState; transition = e.Transition; } }
The StateMachine
is a pretty straightforward implementation of IStateMachine
where the Execute
iterator just executes the current state and the phases of transition over and over until there is a transition to a null
state.
That’s it for the state machine system. Here it is all together so you can put it in a single .cs
file to include into your projects. You could also break it into multiple .cs
files if you’d prefer. Notice that it doesn’t have any dependencies on Unity, so you’re free to use it in other .NET environments such as command-line apps or Xamarin.
//////////////////////////////////////////////////////////////////////////////////////////////////// // About: Finite State Machine by Jackson Dunstan // Article: http://JacksonDunstan.com/articles/3137 // License: MIT // Copyright © 2015 Jackson Dunstan // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and // associated documentation files (the “Softwareâ€), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, publish, distribute, // sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // THE SOFTWARE IS PROVIDED “AS ISâ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //////////////////////////////////////////////////////////////////////////////////////////////////// using System; using System.Collections; using System.Collections.Generic; /// <summary> /// Event args that are dispatched when a state wants to transition to another state /// </summary> public class StateBeginExitEventArgs : EventArgs { /// <summary> /// The state to transition to /// </summary> public IState NextState { get; private set; } /// <summary> /// The transition to use to get to the next state /// </summary> public IStateTransition Transition { get; private set; } /// <summary> /// Create the event args /// </summary> /// <param name="nextState">The state to transition to</param> /// <param name="transition">The transition to use to get to the next state</param> public StateBeginExitEventArgs( IState nextState, IStateTransition transition ) { NextState = nextState; Transition = transition; } } /// <summary> /// A mode of the app that executes over time. This begins the transition process and receives /// notifications about its progress. /// </summary> public interface IState { /// <summary> /// Notify the state that the transition process has begun the entering phase /// </summary> void BeginEnter(); /// <summary> /// Notify the state that the transition process has finished the entering phase /// </summary> void EndEnter(); /// <summary> /// Execute the state's logic over time. This function should 'yield return' until it has /// nothing left to do. It may also dispatch OnBeginExit when it is ready to begin transitioning /// to the next state. If it does this, this funtion will no longer be resumed. /// </summary> IEnumerable Execute(); /// <summary> /// Dispatched when the state is ready to begin transitioning to the next state. This implies /// that the transition process will immediately begin the exiting phase. /// </summary> event EventHandler<StateBeginExitEventArgs> OnBeginExit; /// <summary> /// Notify the state that the transition process has finished exiting phase. /// </summary> void EndExit(); } /// <summary> /// A transition from one state to another. This is broken into two parts. The first is the 'exit' /// process where the current state is left. The second is the 'enter' process where the next state /// becomes the new current state. Both of these processes are executed over time. /// </summary> public interface IStateTransition { /// <summary> /// Exit the current state over time. This function should 'yield return' until the exit process /// is finished. /// </summary> IEnumerable Exit(); /// <summary> /// Enter the next state over time. This function should 'yield return' until the enter process /// is finished. /// </summary> IEnumerable Enter(); } /// <summary> /// A finite state machine that runs a single state at a time and handles the transition process /// from one state to another /// </summary> public interface IStateMachine { /// <summary> /// Execute the state machine by executing states and transitions. This function should /// 'yield return' until there are no more states to execute. /// </summary> IEnumerable Execute(); } /// <summary> /// A state machine implementation that executes states and transitions /// </summary> public class StateMachine : IStateMachine { /// <summary> /// The current state /// </summary> private IState state; /// <summary> /// The next state (to transition to) /// </summary> private IState nextState; /// <summary> /// Transition to use to get to the next state /// </summary> private IStateTransition transition; /// <summary> /// Create the state machine with an initial state /// </summary> /// <param name="initialState">Initial state. BeginEnter() and EndEnter() will be called /// on it immediately</param> public StateMachine(IState initialState) { State = initialState; state.EndEnter(); } /// <summary> /// Execute the initial state and any subsequent states and transitions until there are no more /// states to execute. /// </summary> public IEnumerable Execute() { while (true) { // Execute the current state until it transitions or stops executing for ( var e = state.Execute().GetEnumerator(); transition == null && e.MoveNext(); ) { yield return e.Current; } // Wait until the current state transitions while (transition == null) { yield return null; } // Stop listening for the current state to transition // This prevents accidentally transitioning twice state.OnBeginExit -= HandleStateBeginExit; // There is no next state to transition to // This means the state machine is finished executing if (nextState == null) { break; } // Run the transition's exit process foreach (var e in transition.Exit()) { yield return e; } state.EndExit(); // Switch state State = nextState; nextState = null; // Run the transition's enter process foreach (var e in transition.Enter()) { yield return e; } transition = null; state.EndEnter(); } } /// <summary> /// Set the current state, call its BeginEnter(), and listen for it to transition /// </summary> /// <value>The state to be the new current state</value> private IState State { set { state = value; state.OnBeginExit += HandleStateBeginExit; state.BeginEnter(); } } /// <summary> /// Handles the current state wanting to transition /// </summary> /// <param name="sender">The state that wants to transition</param> /// <param name="e">Information about the desired transition</param> private void HandleStateBeginExit(object sender, StateBeginExitEventArgs e) { nextState = e.NextState; transition = e.Transition; } }
Now let’s take a look at how to use the system starting with a transition that fades out during the exit phase and in during the enter phase. It uses a full-screen Image
UI to accomplish this.
/// <summary> /// A transition that fades the screen out during the exit phase and in during the enter phase /// </summary> public class ScreenFadeTransition : IStateTransition { private Canvas canvas; private Image cover; private float fadeTime; /// <summary> /// Create the transition /// </summary> /// <param name="fadeTime">Time in seconds to complete both parts of the phase</param> public ScreenFadeTransition(float fadeTime) { // Set up the fade cover var screenFadePrefab = Resources.Load<Canvas>("ScreenFade"); canvas = UnityEngine.Object.Instantiate(screenFadePrefab); var coverGO = canvas.transform.Find("Cover"); cover = coverGO.GetComponent<Image>(); this.fadeTime = fadeTime; } public IEnumerable Exit() { // Fade out foreach (var e in TweenAlpha(0, 1, fadeTime / 2)) { yield return e; } } public IEnumerable Enter() { // Fade in foreach (var e in TweenAlpha(1, 0, fadeTime / 2)) { yield return e; } // Clean up the fade cover UnityEngine.Object.Destroy(canvas.gameObject); } // Tween the alpha of the fade cover private IEnumerable TweenAlpha( float fromAlpha, float toAlpha, float duration ) { var startTime = Time.time; var endTime = startTime + duration; while (Time.time < endTime) { var sinceStart = Time.time - startTime; var percent = sinceStart / duration; var color = cover.color; color.a = Mathf.Lerp(fromAlpha, toAlpha, percent); cover.color = color; yield return null; } } }
Now let’s look at how we’d implement a state starting with a simple main menu:
/// <summary> /// A state for the main menu /// </summary> public class MainMenuState : IState { private Canvas canvas; private Text frameCount; private int initialFrame; public void BeginEnter() { // Create the main menu UI var canvasPrefab = Resources.Load<Canvas>("MainMenu"); canvas = UnityEngine.Object.Instantiate(canvasPrefab); var playButtonGO = canvas.transform.Find("PlayButton"); var playButton = playButtonGO.GetComponent<Button>(); playButton.onClick.AddListener(HandlePlayButton); var frameCountGO = canvas.transform.Find("FrameCount"); frameCount = frameCountGO.GetComponent<Text>(); initialFrame = Time.frameCount; } public void EndEnter() { } public IEnumerable Execute() { // Update the frame count until we transition while (true) { var numFrames = Time.frameCount - initialFrame; frameCount.text = "Frames spent on menu: " + numFrames; yield return null; } } public event EventHandler<StateBeginExitEventArgs> OnBeginExit; public void EndExit() { // Clean up the UI UnityEngine.Object.Destroy(canvas.gameObject); } private void HandlePlayButton() { // Transition to the play state using a screen fade var nextState = new PlayState(); var transition = new ScreenFadeTransition(2); var eventArgs = new StateBeginExitEventArgs(nextState, transition); OnBeginExit(this, eventArgs); } }
This main menu is really simple and hardly does anything. It shows a frame count on a Text
UI purely as an example of some logic performed over time. When the play Button
UI is clicked, it transitions to the PlayState
using the screen fade transition above.
Let’s take a look at that play state to see the “game” portion of this example:
/// <summary> /// A state for the game play portion of the example /// </summary> public class PlayState : IState { private GameObject targetsContainer; private List<GameObject> targets; public void BeginEnter() { // Set up the targets targetsContainer = new GameObject("TargetsContainer"); var targetPrefab = Resources.Load<GameObject>("Target"); targets = new List<GameObject>(3); for (var i = 0; i < targets.Capacity; ++i) { var target = UnityEngine.Object.Instantiate(targetPrefab); target.transform.parent = targetsContainer.transform; target.transform.position += new Vector3(i*2, 0, 0); targets.Add(target); } } public void EndEnter() { // Turn the targets green to indicate that they're ready to be clicked foreach (var target in targets) { SetTargetColor(target, Color.green); } } public IEnumerable Execute() { // Handle clicks while (true) { if (Input.GetMouseButtonDown(0)) { HandleClick(); } yield return null; } } public event EventHandler<StateBeginExitEventArgs> OnBeginExit; public void EndExit() { // Clean up the targets UnityEngine.Object.Destroy(targetsContainer); } private void HandleClick() { // Cast a ray where the user clicked var ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; if (Physics.Raycast(ray, out hitInfo) == false) { return; } // Check if the user clicked any targets foreach (var target in targets) { if (target != null && hitInfo.transform == target.transform) { HitTarget(target); break; } } } private void HitTarget(GameObject target) { // Turn the target red so the user knows they hit it SetTargetColor(target, Color.red); // If the user hit all the targets targets.Remove(target); if (targets.Count == 0) { // Transition to the main menu using the screen fade transition var nextState = new MainMenuState(); var transition = new ScreenFadeTransition(2); var eventArgs = new StateBeginExitEventArgs(nextState, transition); OnBeginExit(this, eventArgs); } } private void SetTargetColor(GameObject target, Color color) { var renderer = target.GetComponent<Renderer>(); renderer.material.color = color; } }
The “game” is trivial: click three objects. When you do, the play state transitions to the main menu state using the screen fade transition.
Finally, here’s the code to start and run the example:
public class TestScript : MonoBehaviour { void Start() { // Create the initial state var mainMenuState = new MainMenuState(); // Create the state machine var stateMachine = new StateMachine(mainMenuState); // Run the state machine StartCoroutine(stateMachine.Execute().GetEnumerator()); } }
Here are a couple of screenshots of the example app:
That wraps things up for today. If you don’t have a state machine already, perhaps you’ll find this one useful. It’s tiny—under 100 lines without comments—but quite capable and extensible. If you do already have a state machine that you use, let me know how it compares to this one in the comments. How did you tackle problems like transitions? Do you use game objects and scenes or is your state machine “pure code”?
#1 by Tim Keating on July 23rd, 2015 ·
I have done state machines in Unity a couple of times now. Interesting to see another take on it… there are a few bits here (like componentizing the transition) I very well may steal next time.
Just curious — If you are on Unity 5, have you considered using Mecanim for this?
#2 by jackson on July 23rd, 2015 ·
I hadn’t considered directly incorporating Macanim into the FSM, but it’s certainly possible to use it from your states or your transitions. In fact, that’s probably a good idea in most cases as it’s a much easier, designer-friendly way do animation. I used code in the article not to be more “pure code” but rather to make the article easier to understand without opening up a Unity project. I suppose if Mecanim got to be so standard in your project that you might actually directly replace the transition’s iterator functions with calls to Mecanim.
#3 by Tim Keating on July 27th, 2015 ·
Actually, I was thinking of that the other way around — in light of the fact that under Unity 5, Mecanim can drive any script (not just an animation controller), you could use it for general-purpose state machines. Ergo, it becomes a visual design tool for your FSMs. Something I’ve been thinking about trying, just haven’t yet.
#4 by jackson on July 27th, 2015 ·
Sounds like an interesting idea to play around with. You could really make good use of the “animation” state UI as a design tool. Let me know if you try it out and how it goes. I’d be interested to see how it compares with the FSM from this article.
#5 by Valerie on August 5th, 2016 ·
Thanks for this excellent article on FSM Jackson. I was able to follow along and create your example unity application, and learned a ton along the way. Just subscribed to your Tips-of-the-Week as well. Just wanted to say thanks for your contribution to the development community!
#6 by Valerie on August 21st, 2016 ·
Hey Jackson,
Curious how you would go about implementing an EnemyState (patrol, attack, …) if you were implementing with your MVC. That is, where would you execute the StartCoroutine since this requires MonoBehaviour? The view class (i.e. EnemyView) derives from monoBehaviour, but the view class is only supposed to provide input and output. The only solution I could figure out is adding the TestScript (above in your article) to enemy game objects.
Basically the enemy game object would have the EnemyView (for MVC) component and the TestScript component (for the FSM) Is this what you would do?
#7 by jackson on August 21st, 2016 ·
StartCoroutine
isn’t strictly necessary. All that’s needed is to to callMoveNext
on theIEnumerator
you get back fromExecute
. So you could have the view dispatch anOnFrame
event every frame and the controller listen to that event. Each time it would callMoveNext
. Something like this:#8 by K on October 11th, 2016 ·
Hi Jackson, Thanks you amazing FSM, but I want know how to implement sub fsm?
#9 by K on October 11th, 2016 ·
I write this code,
but, if I call iter.MoveNext() in Update, yield return new WaitForSeconds(10.0f) etc will invalid, you could see below, can you give me a right code?
#10 by jackson on October 11th, 2016 ·
The core idea is to make a single
IState
have its ownIStateMachine
. ItsExecute
essentially just callsExecute
on itsIStateMachine
. The outer state machine has no idea that this state is using a state machine of its own to execute.Hopefully that clears things up a bit.
#11 by Valerie on April 11th, 2017 ·
Hey JD, quick question about the transitions between states. You provided us with a fade transition with this FSM approach, but I’m wondering if it’s possible to do a slide transition between states. For example if I have a list/table view, and I want to drill into a detail view, where the canvas would slide-in from the side (typical iOS navigation) could this be accomplished using this FSM? I’m not sure how I would accomplish this since we’re in transition.
My thought was the transition is a dummy (that is, doesn’t do anything visually), and the next (incoming) state’s canvas would be created where the pivot point would be animated over time which would cause the canvas to slide into view (hopefully while the previous state’s canvas is still in view. Seems doable, so that’s my approach. I’ll let you know how I fair. (fare?)
#12 by jackson on April 11th, 2017 ·
Sure, that sounds pretty doable with this FSM approach. Here’s the code from the article to start a transition:
Instead, imagine you have a `ScreenSlideTransition`. You pass references to the two things to slide to the transition class and it slides them during its `Exit` and `Enter` phases. Here’s some pseudo-code lightly modifying the third line of the above example:
Basically you just have to give the transition class the two things to slide and it needs to know how to slide them. That could vary depending on the UI system you’re using, but it should be pretty easy to change the `TweenAlpha` function to adjust the X coordinate instead of alpha.
#13 by EMebane on April 14th, 2017 ·
I realized as I was thinking about this and writing my own and thinking about Cross Fading from one state to another that the _Finite_ part of the name is suggestive of the idea that you are only in one state at a time. I think the idea is to prevent possible problems that might crop up if you try to manipulate the same data in the exit of one state and the enter of another state.
#14 by jackson on April 14th, 2017 ·
Yes, the “finite” part does have that meaning: it can only be in a finite number of states. One “state” it can be be in is two of what are called “states” in the code. So a combination of state A and B is one overall state that the whole machine is in. That definitely ramps up the number of possible machine states as now you have N2 instead of just N (where N is the number of state types you define), but the number is still finite. If you want to rein this in then you could make an `InstantTransition` that immediately exits one state and moves to the next.
#15 by Valerie on April 15th, 2017 ·
Great discussion guys. I was about to implement this slide transition and so came back to re-read what JD had written. It always takes a few reads before I understand his reply fully. Anyway I’m glad I did because I always learn a thing or two each time I show up!
#16 by EMebane on April 14th, 2017 ·
Hi. I combined this FSM, the newer *Simpler* FSM and Prime31’s with some of my own features. Find it here: https://github.com/ElliotMebane/UnityFiniteStateMachine. I used it in a sample poker game I wrote that’s also linked from the GitHub page. Thanks for the great work, Jackson!
#17 by jackson on April 14th, 2017 ·
Cool! Thanks for the link!
#18 by Valerie on September 18th, 2017 ·
Hi Jackson,
I’m trying to organize my state machine with the use of “managers”. For example, UI Manager, Camera Manager, Map Manager, and Marker Manager.
I want one manager to handle the events which occur from another manager. I guess I should add that I’m using your FSM with your MV-C pattern. Currently I have the state machine: MainViewState listening to the events from the model-views.
CURRENTLY:
MainViewState
_mapManagerMV
_markerManagerMV
_markerManagerMV.OnMarkersChangedEvent += HandleMarkers;
HandleMarkers() { _mapManagerMV.PlaceMarkers(_markerManagerMV.Markers); }
I don’t really want to tightly couple the two classes together. So I don’t want to pass the model-view of the marker manager to that of the map manager so that it can listen to events, but I also don’t want the MainViewState to have a bunch of event handlers.
How can I have the map manager aware of the event from the marker manager? I read some of your articles on the use of Delegates and see that I can pass an instance delegate (or lambda) and have that solve my problem.
Does this seem like the best approach:
mapFuncDelegate = _mapManagerMV.GetMarkerHandlerFunction();
_markerManagerMV.OnMarkersChanged(mapFuncdelegate);
Thanks,
Valerie
#19 by jackson on September 18th, 2017 ·
If you don’t want one class to directly reference another class, then you’ll need some kind of middle-man. That can come in various forms with C#. You mentioned one: delegates. For example:
This way
A
andB
only know aboutAction
, not each other, which helps to decouple them. Another way is to use pseudo-events as a replacement for delegates:One last example: interfaces. This is more of a drop-in replacement for events that doesn’t support multiple listeners.
In this last example,
B
has a direct reference toA
but doesn’t know it because it only knows it has anIEventHandler
. Sure, it could cast this reference to anA
but you probably wouldn’t do that. The middle-man in this case is the fact that interface functions are virtual, so you have a layer of indirection by runtime binding.Regardless of which way you go, some kind of middle-man will help you decouple the two classes.
#20 by Valerie on September 18th, 2017 ·
OMG Jackson: Pseudo-Events …wow
#21 by Renan Valentin Ferreira on January 22nd, 2018 ·
Awesome article Jackson!!!!
I’m wondering how to use state machine in conjunction with inputs, e.g:
If I start a caroutine inside of the fixed update it will spawn a new process for each frame, right?
Thanks!!
#22 by jackson on January 23rd, 2018 ·
Hi Renan,
As with the example in the article, you only want to start the coroutine once. You might start it in
Start
, for example. Then the machine runs until you decide to stop it.Glad you enjoyed the article!
#23 by Gourav on June 12th, 2018 ·
Hello Jackson,
how can i use your FSM with monobehaviour. instead of creating ui in OnBeginEnter i want it to just active gameobject. But as soon as it enters into OnBeginEnter throw null reference as it fails to get gameobject reference.
#24 by jackson on June 12th, 2018 ·
Hi Gourav,
The examples in the article are just that- examples. They only create the UI in
BeginEnter
for demonstration purposes and there’s absolutely nothing that would prohibit you from swapping that code out for your ownMonoBehaviour
-focused UI. For example, in your case you could do something like this:When you create the
MainMenuState
, just pass it the UI to activate:If you want to use the FSM pattern described in the article, I imagine you’ll swap out almost all of the specifics for code that more closely matches your game and technical design.