private static void GetStateDelegate(StateInfo state, Type type) { var method = type.GetMethod(state.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(float) }, null); if (method != null) { state.InvokeWithTimeDelegate = (OpenDelegate <float>)OpenDelegate.Create(method as MethodInfo); return; } method = type.GetMethod(state.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null); if (method != null) { state.InvokeDelegate = (OpenDelegate)OpenDelegate.Create(method as MethodInfo); return; } if (type.BaseType != null) { GetStateDelegate(state, type.BaseType); } }
/// <summary> /// Attempt to add a transition to the given transitions list /// </summary> private static StateTransitionInfo CreateTransition(StateMachineInfo info, StateInfo from, StateInfo to) { if (from == to) { return(null); } // Check for the transition method var methodInfo = info.TargetType.GetMethod( $"On{from?.Name??"Any"}To{to?.Name??"Any"}", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (methodInfo == null) { return(null); } return(new StateTransitionInfo { From = from, To = to, InvokeDelegate = OpenDelegate.Create(methodInfo) }); }
/// <summary> /// Create state machine info for a given type /// </summary> /// <param name="type"></param> /// <returns></returns> public static StateMachineInfo Create(Type type, Type statesType) { var typeName = type.FullName; if (_cache.TryGetValue(typeName, out var info)) { return(info); } var values = Enum.GetValues(statesType); var states = new StateInfo[values.Length]; foreach (var value in values) { var index = (int)value; var name = value.ToString(); if (index < 0 || index >= states.Length) { throw new UnityException("State enum must be sequential and start from zero"); } states[index] = new StateInfo { Name = value.ToString(), Mask = (1UL << index), Index = index }; } // Create a new state machine info info = new StateMachineInfo(); info.TargetType = type; info.States = states; info._transitionStride = states.Length + 1; _cache.Add(typeName, info); // Get state method and transitions info._transitions = new StateTransitionInfo[states.Length * info._transitionStride]; foreach (var stateToInfo in states) { // State method? var stateMethod = type.GetMethod($"On{stateToInfo.Name}", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (null != stateMethod) { var parameters = stateMethod.GetParameters(); if (parameters.Length == 0) { stateToInfo.InvokeDelegate = OpenDelegate.Create(stateMethod); } else if (parameters.Length == 1 && parameters[0].ParameterType == typeof(float)) { stateToInfo.InvokeWithTimeDelegate = OpenDelegate <float> .Create(stateMethod); } else { throw new UnityException($"method for state '{stateToInfo.Name}' does not match a valid state signature"); } } // Add the "Any" to state transition info._transitions[info.GetTransitionIndex(-1, stateToInfo.Index)] = CreateTransition(info, null, stateToInfo); // Add transition from all other states foreach (var stateFromInfo in states) { info._transitions[info.GetTransitionIndex(stateFromInfo.Index, stateToInfo.Index)] = CreateTransition(info, stateFromInfo, stateToInfo); } } return(info); }
private static void GetInfo(Type type, List <StateInfo> states, List <StateTriggerInfo> triggers, List <StateTransitionInfo> transitions) { // Recurse through all parent types as well if (type.BaseType != null) { GetInfo(type.BaseType, states, triggers, transitions); } // Get all public and private methods var members = type.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var member in members) { switch (member.MemberType) { case MemberTypes.Field: case MemberTypes.Property: { foreach (var attr in member.GetCustomAttributes <StateTriggerAttribute>()) { var trigger = new StateTriggerInfo(); trigger.From = GetState(states, attr.From); trigger.To = GetState(states, attr.To); trigger.TargetValue = attr.Value; trigger.Getter = (FastGetter <bool>)FastGetter.Create(member); if (trigger.To == null) { throw new InvalidOperationException("trigger to state must not be null"); } triggers.Add(trigger); } break; } case MemberTypes.Method: { // Get transitions foreach (var attr in member.GetCustomAttributes <StateTransitionAttribute>()) { var trans = new StateTransitionInfo(); trans.From = GetState(states, attr.From); trans.To = GetState(states, attr.To); if (((member as MethodInfo).GetParameters()).Length == 0) { trans.InvokeDelegate = (OpenDelegate)OpenDelegate.Create(member as MethodInfo); } else { throw new InvalidOperationException("state transitions must return void and take no parameter or one parameter as a float"); } if (trans.To == null) { throw new InvalidOperationException("transition to state must not be null"); } transitions.Add(trans); } #if false // Get states foreach (var attr in member.GetCustomAttributes <StateAttribute>()) { var state = GetState(states, attr.Name ?? member.Name); var parameters = (member as MethodInfo).GetParameters(); if (parameters.Length == 0) { state.InvokeDelegate = (OpenDelegate)OpenDelegate.Create(member as MethodInfo); } else if (parameters.Length == 1 && parameters[0].ParameterType == typeof(float)) { state.InvokeWithTimeDelegate = (OpenDelegate <float>)OpenDelegate.Create(member as MethodInfo); } else { throw new InvalidOperationException("states must return void and take no parameter or one parameter as a float"); } } #endif break; } } } }