/// <summary> /// Raises the <see cref="Exit"/> event. /// </summary> /// <param name="eventArgs"> /// <see cref="StateEventArgs"/> object that provides the arguments for the event. /// </param> /// <remarks> /// <strong>Notes to Inheritors:</strong> When overriding <see cref="OnExit"/> in a derived /// class, be sure to call the base class's <see cref="OnExit"/> method so that registered /// delegates receive the event. /// </remarks> protected virtual void OnExit(StateEventArgs eventArgs) { var handler = Exit; if (handler != null) { handler(this, eventArgs); } }
/// <summary> /// Raises the <see cref="Action"/> event. /// </summary> /// <param name="eventArgs"> /// <see cref="StateEventArgs"/> object that provides the arguments for the event. /// </param> /// <remarks> /// <strong>Notes to Inheritors:</strong> When overriding <see cref="OnAction"/> in a derived /// class, be sure to call the base class's <see cref="OnAction"/> method so that registered /// delegates receive the event. /// </remarks> internal protected virtual void OnAction(StateEventArgs eventArgs) { var handler = Action; if (handler != null) { handler(this, eventArgs); } }
/// <summary> /// Raises the <see cref="Update"/> event. /// </summary> /// <param name="eventArgs"> /// <see cref="StateEventArgs"/> object that provides the arguments for the event. /// </param> /// <remarks> /// <strong>Notes to Inheritors:</strong> When overriding <see cref="OnUpdate"/> in a derived /// class, be sure to call the base class's <see cref="OnUpdate"/> method so that registered /// delegates receive the event. /// </remarks> protected virtual void OnUpdate(StateEventArgs eventArgs) { var handler = Update; if (handler != null) { handler(this, eventArgs); } }
/// <summary> /// Updates the active state. /// </summary> /// <param name="eventArgs"> /// The <see cref="StateEventArgs"/> instance containing the required data. /// </param> internal void UpdateState(StateEventArgs eventArgs) { if (Count == 0) { return; // Nothing to do. } Debug.Assert(ActiveState != null, "Cannot update state. Active state is not set."); Debug.Assert(LastActiveState != null, "Last active state is not set."); ActiveState.UpdateState(eventArgs); }
/// <summary> /// Performs state transitions. /// </summary> /// <param name="eventArgs"> /// The <see cref="StateEventArgs"/> instance containing the required data. /// </param> /// <returns> /// The firing transition; or <see langword="null"/> if no transition is firing. /// </returns> internal Transition UpdateTransitions(StateEventArgs eventArgs) { if (ActiveState == null) { // No state active. --> Transition into a state. // This happens at the first step of a state machine. EnterState(null, eventArgs); return(null); } // Check transitions of active state. return(ActiveState.UpdateTransitions(eventArgs)); }
/// <summary> /// Updates the state. /// </summary> /// <param name="eventArgs"> /// The <see cref="StateEventArgs"/> instance containing the required data. /// </param> internal void UpdateState(StateEventArgs eventArgs) { // Sub-states are updated first. if (_parallelSubStates != null) { foreach (var stateCollection in _parallelSubStates) { stateCollection.UpdateState(eventArgs); } } // Raise Update event. OnUpdate(eventArgs); }
/// <summary> /// Exits to the specified target state. /// </summary> /// <param name="transition">The firing transition.</param> /// <param name="eventArgs"> /// The <see cref="StateEventArgs"/> instance containing the required data. /// </param> internal void ExitState(Transition transition, StateEventArgs eventArgs) { if (Count == 0 || ActiveState == null) { return; // Nothing to do. } // Call exit on current state. The method will call true if the // state was exited. If the ActiveState contains the target state, // the method will return false and the ActiveState will still be active. bool exited = ActiveState.ExitState(transition, eventArgs); if (exited) { ActiveState = null; } }
/// <summary> /// Enters the state. /// </summary> /// <param name="transition">The firing transition.</param> /// <param name="eventArgs"> /// The <see cref="StateEventArgs"/> instance containing the required data. /// </param> internal void EnterState(Transition transition, StateEventArgs eventArgs) { if (!IsActive) { // State is not active. Raise Enter event. OnEnter(eventArgs); } // Call Enter for sub-states. if (_parallelSubStates != null) { foreach (var stateCollection in _parallelSubStates) { stateCollection.EnterState(transition, eventArgs); } } }
/// <summary> /// Exits to the specified target state. /// </summary> /// <param name="transition">The firing transition.</param> /// <param name="eventArgs"> /// The <see cref="StateEventArgs"/> instance containing the required data. /// </param> /// <returns> /// <see langword="true"/> if this state was exited. <see langword="false"/> if this state is /// still active because the target state is a sub-state of this state. /// </returns> internal bool ExitState(Transition transition, StateEventArgs eventArgs) { Debug.Assert(IsActive, "Exit should only be called for active states."); if (transition != null && !Transitions.Contains(transition) && Contains(transition.TargetState)) { // The transition is not from this state and the target is a sub-state // --> We do not leave the current state! // We have to find the sub-state collection that contains the target. In this sub-state // collection the active state will exit (and the target state will be entered later). if (_parallelSubStates != null) { foreach (var stateCollection in _parallelSubStates) { if (stateCollection.ContainsRecursive(transition.TargetState)) { stateCollection.ExitState(transition, eventArgs); } } } return(false); // This approach assumes that there are no transitions from a state to a state in a // parallel set. This is not explicitly forbidden (it is never verified in the state // machine). // Programmers must not create such transitions and graphical state machine editors // should prohibit such constructs. } // The target state is no sub-state. --> Exit all sub-state collections. if (_parallelSubStates != null) { foreach (var stateCollection in _parallelSubStates) { stateCollection.ExitState(null, eventArgs); } } // Raise Exit event. OnExit(eventArgs); return(true); }
/// <summary> /// Enters a new state. /// </summary> /// <param name="transition">The transition. (Can be <see langword="null"/>.)</param> /// <param name="eventArgs"> /// The <see cref="StateEventArgs"/> instance containing the required data. /// </param> internal void EnterState(Transition transition, StateEventArgs eventArgs) { if (Count == 0) { // Abort, nothing to do. return; } // Current active state (can be null). var newActiveState = ActiveState; if (transition != null && ContainsRecursive(transition.TargetState)) { // The target state is a state or sub-state. // The new active state must be the state that contains the target state. newActiveState = GetChild(transition.TargetState); Debug.Assert(newActiveState != null, "New active state not found."); } if (newActiveState == null) { // We don't know which state to enter - enter history/initial state. if (SaveHistory && LastActiveState != null) { newActiveState = LastActiveState; } else { newActiveState = InitialState; } } // Enter the new state. newActiveState.EnterState(transition, eventArgs); // Now that state was entered we remember it as the active state. ActiveState = newActiveState; LastActiveState = newActiveState; }
internal Transition UpdateTransitions(StateEventArgs eventArgs) { Transition firingTransition = null; // Update sub-state transitions first. if (_parallelSubStates != null) { foreach (var stateCollection in _parallelSubStates) { firingTransition = stateCollection.UpdateTransitions(eventArgs); if (firingTransition != null && !stateCollection.ContainsRecursive(firingTransition.TargetState)) { // The transition target is outside the sub-state collection. // More parallel transition updates are only allowed for internal transitions. break; } // There was no transition or the transition was an internal transition. // We can check the next parallel state set. } } // Abort if a transition was performed. if (firingTransition != null) { return(firingTransition); } // Check transitions of this state. foreach (var transition in Transitions) { if (transition.TargetState == null) { throw new InvalidOperationException("TargetState of transition must not be null."); } //if (transition.TargetState.StateMachine != StateMachine) // throw new InvalidOperationException("TargetState of transition must not belong to different state machine."); // Check transition. bool fired = transition.Update(eventArgs.DeltaTime); if (fired) { // Get state collection that contains this state and the target state. var stateCollection = StateCollection.GetCollection(this, transition.TargetState); // Exit states. stateCollection.ExitState(transition, eventArgs); // Execute transition action. transition.OnAction(eventArgs); // Enter states. stateCollection.EnterState(transition, eventArgs); // Do not update other transitions after first transition has fired. return(transition); } } return(null); }