/// <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; }
private void CreateStateMachine() { // Let's create a state machine that manages all states and transitions. _stateMachine = new StateMachine(); // ----- First we need to define all possible states of the game component: // The initial state is the "Loading" state. In this state a "Loading..." // text is rendered and all required assets are loaded in the background. var loadingState = new State { Name = "Loading" }; loadingState.Enter += OnEnterLoadingScreen; loadingState.Exit += OnExitLoadingScreen; // The second state is the "Start" state. Once loading is finished the text // "Press Start button" is shown until the users presses the Start button // on the gamepad. var startState = new State { Name = "Start" }; startState.Enter += OnEnterStartScreen; startState.Update += OnUpdateStartScreen; startState.Exit += OnExitStartScreen; // The "Menu" state represents the main menu. It provides buttons to start // the game, show sub menus, and exit the game. var menuState = new State { Name = "Menu" }; menuState.Enter += OnEnterMenuScreen; menuState.Exit += OnExitMenuScreen; // The "SubMenu" is just a dummy menu containing a few buttons. It is shown // whenever a sub menu is selected in the main menu. var subMenuState = new State { Name = "SubMenu" }; subMenuState.Enter += OnEnterSubMenuScreen; subMenuState.Update += OnUpdateSubMenuScreen; subMenuState.Exit += OnExitSubMenuScreen; // The "Game" state is a placeholder for the actual game content. var gameState = new State { Name = "Game" }; gameState.Enter += OnEnterGameScreen; gameState.Update += OnUpdateGameScreen; gameState.Exit += OnExitGameScreen; // Register the states in the state machine. _stateMachine.States.Add(loadingState); _stateMachine.States.Add(startState); _stateMachine.States.Add(menuState); _stateMachine.States.Add(subMenuState); _stateMachine.States.Add(gameState); // Optional: Define the initially selected state. // (If not set explicitly then the first state in the list is used as the // initial state.) _stateMachine.States.InitialState = loadingState; // ----- Next we can define the allowed transitions between states. // The "Loading" screen will transition to the "Start" screen once all assets // are loaded. The assets are loaded in the background. The background worker // sets the flag _allAssetsLoaded when it has finished. // The transition should fire automatically. To achieve this we can set FireAlways // to true and define a Guard. A Guard is a condition that needs to be fulfilled // to enable the transition. This way the game component automatically switches // from the "Loading" state to the "Start" state once the loading is complete. var loadToStartTransition = new Transition { Name = "LoadingToStart", TargetState = startState, FireAlways = true, // Always trigger the transition, if the guard allows it. Guard = () => _allAssetsLoaded, // Enable the transition when _allAssetsLoaded is true. }; loadingState.Transitions.Add(loadToStartTransition); // The remaining transition need to be triggered manually. // The "Start" screen will transition to the "Menu" screen. var startToMenuTransition = new Transition { Name = "StartToMenu", TargetState = menuState, }; startState.Transitions.Add(startToMenuTransition); // The "Menu" screen can transition to the "Game" screen or to the "SubMenu". var menuToGameTransition = new Transition { Name = "MenuToGame", TargetState = gameState, }; var menuToSubMenuTransition = new Transition { Name = "MenuToSubMenu", TargetState = subMenuState, }; menuState.Transitions.Add(menuToGameTransition); menuState.Transitions.Add(menuToSubMenuTransition); // The "Game" screen will transition back to the "Menu" screen. var gameToMenuTransition = new Transition { Name = "GameToMenu", TargetState = menuState, }; gameState.Transitions.Add(gameToMenuTransition); // The "SubMenu" can transition to back to the "Menu" screen. var subMenuToMenuTransition = new Transition { Name = "SubMenuToMenu", TargetState = menuState, }; subMenuState.Transitions.Add(subMenuToMenuTransition); }