Generics have been around awhile now. It’s not likely that Enums will be added. I’m sure there’s a reason they’re not now. In fact, you can’t restrict to any value type in particular. You’re limited to saying where T : struct (which will only allow value types) and IConvertible which Enum implements. I used this “workaround” in my example above by then checking to see if the type was an Enum in the constructor. It’s not really a constraint, but allows you to do the double check. It’s certainly nothing you should be worried about using in your own code as long as you document the intent.
@rozgo Thanks for posting. This is a great foundation on which to build!
Wow, this is an old thread! In the meantime, I converted my sample Unityscript coroutine state machine to C#
Here’s a multipurpose state machine class based on the original post. It’s intended to operate like a black box base class and run callback routines based on state/transition changes. Both states and transitions can be configured as needed. Oh, and it runs garbage free (tested with 2018.1/Win 7).
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
public class StateMachine : MonoBehaviour
{
public enum State
{
A,
B,
C,
}
public enum Transition
{
ENTER,
ARRIVE,
EXIT
}
public State state;
public bool debugStates;
protected delegate IEnumerator Routine();
public void SetState(int state) { this.state = (State)state; }// for easy connection to UI dropdown
Dictionary<int, Dictionary<Transition, Routine>> states = new Dictionary<int, Dictionary<Transition, Routine>>();
void Start()
{
InitStateMachine();
//// Test
AssignCoroutine(State.A, Transition.ENTER, TestRoutine);
//AssignCoroutine(State.B, Transition.ENTER, TestRoutine);
AssignCoroutine(State.C, Transition.ENTER, TestRoutine);
StartStateMachine();
}
protected void InitStateMachine()
{
foreach (int key in Enum.GetValues(typeof(State)))
{
var transitions = new Dictionary<Transition, Routine>();
foreach (Transition t in Enum.GetValues(typeof(Transition)))
{
Routine r = null;
transitions.Add(t, r);
}
states.Add(key, transitions);
}
}
protected void StartStateMachine()
{
StartCoroutine(Evaluate());
}
protected void AssignCoroutine(State state, Transition transition, Routine routine)
{
states[(int)state][transition] = routine;
}
protected void AddCoroutine(State state, Transition transition, Routine routine, bool clearExisting = false)
{
if (clearExisting) { states[(int)state][transition] = null; }
states[(int)state][transition] += routine;
}
protected IEnumerator Evaluate()
{
while (true)
{
for (int i = 0; i < states.Count; i++)
{
if ((int)state == i)
{
if (debugStates) { Debug.Log("ENTER: " + (State)i + "\n"); }
if (states[i][Transition.ENTER] != null) { yield return states[i][Transition.ENTER].Invoke(); }
if (debugStates) { Debug.Log("ARRIVE: " + (State)i + "\n"); }
if (states[i][Transition.ARRIVE] != null) { yield return states[i][Transition.ARRIVE].Invoke(); }
while ((int)state == i) { yield return null; } // yield until state change
if (debugStates) { Debug.Log("EXIT: " + (State)i + "\n"); }
if (states[i][Transition.EXIT] != null) { yield return states[i][Transition.EXIT].Invoke(); }
}
}
}
}
IEnumerator TestRoutine()
{
Debug.Log("Test Routine\n");
yield return new WaitForSeconds(1); // Simulate processing
yield break;
}
}
10 years later, and this still holds up, thanks a lot man!!
I mean, I’m doing simple ai though, nothing fancy, so this is all I need!
How to add a Animator.CrossFade inside this ?
IEnumerator WalkState () {
Debug.Log("Walk: Enter");
Animator.Crossfade("animName",1.0f); // <=== Make error
while (state == State.Walk) {
yield return 0;
}
Debug.Log("Walk: Exit");
NextState();
}
Please don’t necro-respond to 12-year-old completely unrelated posts to fix your simple typographic mistakes.
CrossFade() is a public method requiring an instance of an Animator.
It is NOT a static method on the Animator class, which is what it appears you are attempting to do above.