Very Simple StateMachine Pattern - C#

This is a stupid simple state machine pattern that works great with simple setups. Its no where near as featured as my other state machines, but still elegant. Keeps your state data scoped and your logic nicely inlined using coroutines. You can even change states from the inspector.

using UnityEngine;
using System.Collections;

public class Monster : MonoBehaviour {

    public enum State {
        Crawl,
        Walk,
        Die,
    }

    public State state;

    IEnumerator CrawlState () {
        Debug.Log("Crawl: Enter");
        while (state == State.Crawl) {
            yield return 0;
        }
        Debug.Log("Crawl: Exit");
        NextState();
    }

    IEnumerator WalkState () {
        Debug.Log("Walk: Enter");
        while (state == State.Walk) {
            yield return 0;
        }
        Debug.Log("Walk: Exit");
        NextState();
    }

    IEnumerator DieState () {
        Debug.Log("Die: Enter");
        while (state == State.Die) {
            yield return 0;
        }
        Debug.Log("Die: Exit");
    }

    void Start () {
        NextState();
    }

    void NextState () {
        string methodName = state.ToString() + "State";
        System.Reflection.MethodInfo info = 
            GetType().GetMethod(methodName, 
                                System.Reflection.BindingFlags.NonPublic |
                                System.Reflection.BindingFlags.Instance);
        StartCoroutine((IEnumerator)info.Invoke(this, null));
    }
    
}
3 Likes

Very handy! Is there a way of calling a state method when you need to other than automatically every frame using a co-routine?

happybomb, I think the idea is that you just change the public state variable.

Still, I would also like to know if this isn’t using up cpu’s? Maybe sleep in the while-loop would be good?

-Mike

I am trying to understand this code but ran into an error that I don’t quite understand. It keeps giving me an “NullReferenceException: Object reference not set to an instance of an object” error on this line.

         StartCoroutine((IEnumerator)info.Invoke(this, null));

…?

I use a similar pattern with Javascript (although a bit simpler and maybe less flexible) - http://drupal.technicat.com/games/unity/scripts/fsm.html

If you haven’t looked at it already, I recommend checking out the Stateless project. Works great with Unity.

The Stateless Project? Intrigued. This? Google Code Archive - Long-term storage for Google Code Project Hosting.

Yes, that’s the one. To convert to Unity you need to change the resource strings to hard-coded strings for the exceptions that are thrown. It takes about 10 minutes. That was the only change I had to make.

The documentation isn’t great unfortunately, giving just the most basic telephone call example, and the project, as you have no doubt experienced, can be difficult to track down due to its naming.

My experience with stateless is that it generates ~250 bytes of garbage per state transition, largely due to its use of LINQ. We’re also having trouble using it on iOS. (we’re running out of “trampolines” possibly due to the deep call stacks). I pulled LINQ out and got the garbage generation down to zero, but have only started looking at the second issue.

if anyone is interested, I’m willing to share my rough C# implementation of SCXML … drop me a pm…

Sorry, I haven’t. I have not yet had need to use stateless on iOS (stuck on mostly Windows projects this past year or so with Unity). For iOS, and have state machine usage, I am not sure Stateless is the best option. You may want to look in to AForge or snag one of the many other lighter-weight, less generic state machine libraries out there.

Did you ever resolve the issue with the trampolines? We use stateless for non-unity related .net here and would love to use it with unity as well…

Stateless looks great. Is it really not usable for Unity iOS?

Nice script. Here’s my simple change that will send a StateChanged event and has optional logging when a state enters.

Here it is in template format. You can make it the default new script. (C:\Program Files (x86)\Unity4.1.5f1\Editor\Data\Resources\ScriptTemplates\81-C# Script-NewBehaviourScript.cs.txt).

Dude this is what I was looking for,

This line is a little new to me, never used code like It before:

   System.Reflection.MethodInfo info = GetType().GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

I understand what its results to, but not sure about BindingFlags.NonPublic and BindingFlags.Instance?

And this runs the resulting “IEnumerator”:

StartCoroutine((IEnumerator)info.Invoke(this, null));

Sweet!!

I’m looking for a central controller for each game object as a module component and I think this is the way to do it. I did a few tests and then noticed State’s are the main structure to the logic.

I’m still a little new to C# and any light on how this works would be like very interesting.

For simple state machine people just use a switch in their update… For a more complex one I would use a baseclass called state which has 2 empty functions: void ChangeState(State newState, State prevState);, void Execute(Entity AIDude);. You then just create inside your mainclass a State and call its execute function while giving your own reference per this statement. ir works very well and there is no reflection magic required.

Edit: reflection shouldnt be done frequently.:roll:

This is where I learned the basic stuff: http://www.amazon.com/Programming-Game-Example-Mat-Buckland/dp/1556220782

Ah, I see… so with this approach only one state can be run at any one given time?

I originally had this idea of a switch board, where one class word be responsible for turning states on and off. And having a structure to utilise this approach.

The other thing I’d be careful with in the poster’s example is this guy:

string methodName = state.ToString() + "State";

You’re going to be generating garbage doing those string concatenations every time you change states. A better approach would probably be to do something like store your state method names globally in a Dictionary using the the State (or integer value of it since it’s an enum) as the key, then do:

string methodName = allMethods[state];

//OR if you're using Ints

string methodName = allMethods[(int)state];

You could pretty easily create your own generic StateMachine with a single code base that would allow your “States” to be any Enum type. Now, you can’t exactly restrict generic parameters to Enums but you can whittle it down to struct, IConvertable and then check the type of T on instantiation. If you want something a little more dynamic, you could create a base class. Here’s an example that just takes what you put into it, sets it as the current state, and returns it. Obviously this isn’t very useful, but it’s just a demonstration.

using System;

namespace StateMachine
{
    public class Machine<T> where T : struct, IConvertible
    {
	    protected T _currentState;

		public Machine()
		{
			if (!typeof(T).IsEnum)
			{
				throw new ArgumentException("T must be an enumeration");
			}
		}

	    public virtual T InOut(T input)
	    {
		    _currentState = input;
		    return input;
	    }
    }
}

Now, if you wanted, you could create a separate StateMachine for every enemy… so let’s say you have a Enum type called: “EnemyState” that looks like this:

public enum EnemyState
{
      Idle,
      Sleeping,
      Patrolling
}

You could actually create an implementation of the base:

public class EnemyMachine : Machine<EnemyState>
{
    //Your logic here
}

So you could easily encapsulate some base logic in the base class but use your derived class for any very specific functionality and all you have to do is call:

    EnemyMachine eMachine = new EnemyMachine;

And this will automatically be typed to “EnemyState” as that’s what you specified when inheriting the base class.

1 Like

there are some workarounds for this but none is really convenient. i don’t want to advertise them just for information of interested people. i think this should be added to c# quickly but then it still takes a while until mono catches up and then it takes ages until ut incorporates it in unity ;).