/// <summary> /// Inspects a MonoBehaviour for state methods as definied by the supplied Enum, and returns a stateMachine instance used to trasition states. /// </summary> /// <param name="component">The component with defined state methods</param> /// <param name="startState">The default starting state</param> /// <returns>A valid stateMachine instance to manage MonoBehaviour state transitions</returns> public static FiniteStateMachine <T> Initialize(MonoBehaviour component, T startState) { var engine = component.GetComponent <FiniteStateMachineRunner>(); if (engine == null) { engine = component.gameObject.AddComponent <FiniteStateMachineRunner>(); } return(engine.Initialize <T>(component, startState)); }
public FiniteStateMachine(FiniteStateMachineRunner engine, MonoBehaviour component) { this.engine = engine; this.component = component; //Define States var values = Enum.GetValues(typeof(T)); if (values.Length < 1) { throw new ArgumentException("Enum provided to Initialize must have at least 1 visible definition"); } stateLookup = new Dictionary <object, FiniteStateMapping>(); for (int i = 0; i < values.Length; i++) { var mapping = new FiniteStateMapping((Enum)values.GetValue(i)); stateLookup.Add(mapping.state, mapping); } //Reflect methods var methodBindingFlags = BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic; // To refactor var baseMethods = component.GetType().BaseType.GetMethods(methodBindingFlags); var methods = baseMethods.Union(component.GetType().GetMethods(methodBindingFlags)).ToArray(); //Bind methods to states var separator = "_".ToCharArray(); for (int i = 0; i < methods.Length; i++) { if (methods[i].GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Length != 0) { continue; } var names = methods[i].Name.Split(separator); //Ignore functions without an underscore if (names.Length <= 1) { continue; } Enum key; try { key = (Enum)Enum.Parse(typeof(T), names[0]); } catch (ArgumentException) { //Not an method as listed in the state enum continue; } var targetState = stateLookup[key]; switch (names[1]) { case "Enter": if (methods[i].ReturnType == typeof(IEnumerator)) { targetState.hasEnterRoutine = true; targetState.EnterRoutine = CreateDelegate <Func <IEnumerator> >(methods[i], component); } else { targetState.hasEnterRoutine = false; targetState.EnterCall = CreateDelegate <Action>(methods[i], component); } break; case "Exit": if (methods[i].ReturnType == typeof(IEnumerator)) { targetState.hasExitRoutine = true; targetState.ExitRoutine = CreateDelegate <Func <IEnumerator> >(methods[i], component); } else { targetState.hasExitRoutine = false; targetState.ExitCall = CreateDelegate <Action>(methods[i], component); } break; case "Finally": targetState.Finally = CreateDelegate <Action>(methods[i], component); break; case "Update": targetState.Update = CreateDelegate <Action>(methods[i], component); break; case "LateUpdate": targetState.LateUpdate = CreateDelegate <Action>(methods[i], component); break; case "FixedUpdate": targetState.FixedUpdate = CreateDelegate <Action>(methods[i], component); break; case "OnCollisionEnter": targetState.OnCollisionEnter = CreateDelegate <Action <Collision> >(methods[i], component); break; } } //Create nil state mapping currentState = new FiniteStateMapping(null); }