protected override void OnUpdate(StateEventArgs eventArgs) { Update(eventArgs.DeltaTime); Draw(eventArgs.DeltaTime); base.OnUpdate(eventArgs); }
protected override void OnEnter(StateEventArgs eventArgs) { Initialize(); LoadContent(); base.OnEnter(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> /// 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> /// 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; }
/// <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); }
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; }
/// <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> /// <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> /// Called when "SubMenu" state is exited. /// </summary> private void OnExitSubMenuScreen(object sender, StateEventArgs eventArgs) { // Clean up. _subMenuExitAnimationController.Stop(); _subMenuExitAnimationController.Recycle(); _subMenuExitAnimationIsPlaying = false; _uiScreen.Children.Remove(_subMenuWindow); _subMenuWindow = null; }
/// <summary> /// Called every frame when "Game" state is active. /// </summary> private void OnUpdateGameScreen(object sender, StateEventArgs eventArgs) { // Exit the "Game" state if Back button or Escape key is pressed. if (InputService.IsPressed(Buttons.Back, false, LogicalPlayerIndex.One) || InputService.IsPressed(Keys.Escape, false)) { InputService.IsKeyboardHandled = true; InputService.SetGamePadHandled(LogicalPlayerIndex.One, true); _stateMachine.States.ActiveState.Transitions["GameToMenu"].Fire(); } }
/// <summary> /// Called when "Loading" state is exited. /// </summary> private void OnExitLoadingScreen(object sender, StateEventArgs eventArgs) { // Clean up. _uiScreen.Children.Remove(_loadingTextBlock); _loadingTextBlock = null; }
/// <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> protected internal virtual void OnAction(StateEventArgs eventArgs) { var handler = Action; if (handler != null) handler(this, eventArgs); }
/// <summary> /// Called when "Menu" state is entered. /// </summary> private void OnEnterMenuScreen(object sender, StateEventArgs eventArgs) { // Show a main menu consisting of several buttons. // The user should be able to select individual buttons by using the // D-pad on the gamepad or the arrow keys. Therefore we need to create // a Window. A Window manages the currently selected ("focused") control // and automatically handles focus movement. // In this example the Window is invisible (no chrome) and stretches across // the entire screen. _menuWindow = new Window { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch, }; _uiScreen.Children.Add(_menuWindow); // The content of the Window is a vertical StackPanel containing several buttons. var stackPanel = new StackPanel { Orientation = Orientation.Vertical, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Bottom, Margin = new Vector4F(150, 0, 0, 200) }; _menuWindow.Content = stackPanel; // The "Start" button starts the "Game" state. var startButton = new Button { Name = "StartButton", Content = new TextBlock { Text = "Start" }, FocusWhenMouseOver = true, }; startButton.Click += OnStartButtonClicked; // The buttons "Sub menu 1" and "Sub menu 2" show a dummy sub-menu. var subMenu1Button = new Button { Name = "SubMenu1Button", Content = new TextBlock { Text = "Sub-menu 1" }, FocusWhenMouseOver = true, }; subMenu1Button.Click += OnSubMenuButtonClicked; var subMenu2Button = new Button { Name = "SubMenu2Button", Content = new TextBlock { Text = "Sub-menu 2" }, FocusWhenMouseOver = true, }; subMenu2Button.Click += OnSubMenuButtonClicked; // The "Exit" button closes the application. var exitButton = new Button { Name = "ExitButton", Content = new TextBlock { Text = "Exit" }, FocusWhenMouseOver = true, }; exitButton.Click += OnExitButtonClicked; stackPanel.Children.Add(startButton); stackPanel.Children.Add(subMenu1Button); stackPanel.Children.Add(subMenu2Button); stackPanel.Children.Add(exitButton); // By default, the first button should be selected. startButton.Focus(); // Slide the buttons in from the left (off screen) to make things more dynamic. AnimateFrom(stackPanel.Children, 0, new Vector2F(-300, 0)); // The first time initialization of the GUI can take a short time. If we reset the elapsed // time of the XNA game timer, the animation will start a lot smoother. // (This works only if the XNA game uses a variable time step.) Game.ResetElapsedTime(); }
protected override void OnExit(StateEventArgs eventArgs) { UnloadContent(); base.OnExit(eventArgs); }
/// <summary> /// Called when "SubMenu" state is entered. /// </summary> private void OnEnterSubMenuScreen(object sender, StateEventArgs eventArgs) { // Similar to OnEnterMenuScreen. _subMenuWindow = new Window { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch, }; _uiScreen.Children.Add(_subMenuWindow); var stackPanel = new StackPanel { Orientation = Orientation.Vertical, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Bottom, Margin = new Vector4F(150, 0, 0, 200) }; _subMenuWindow.Content = stackPanel; var button1 = new Button { Name = "Item1Button", Content = new TextBlock { Text = "Item 1" }, FocusWhenMouseOver = true, }; var button2 = new Button { Name = "Item2Button", Content = new TextBlock { Text = "Item 2" }, FocusWhenMouseOver = true, }; var button3 = new Button { Name = "Item3Button", Content = new TextBlock { Text = "Item 3" }, FocusWhenMouseOver = true, }; var backButton = new Button { Name = "BackButton", Content = new TextBlock { Text = "Back" }, FocusWhenMouseOver = true, }; backButton.Click += OnBackButtonClicked; stackPanel.Children.Add(button1); stackPanel.Children.Add(button2); stackPanel.Children.Add(button3); stackPanel.Children.Add(backButton); button1.Focus(); // Fade-in the buttons from the right. AnimateFrom(stackPanel.Children, 0, new Vector2F(300, 0)); }
/// <summary> /// Called when "Game" state is exited. /// </summary> private void OnExitGameScreen(object sender, StateEventArgs eventArgs) { // Clean up. _uiScreen.Children.Remove(_gameTextBlock); _gameTextBlock = null; }
/// <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); }
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); }
private AnimationController _exitAnimationController; // Controls the fade-out animation. /// <summary> /// Called when "Start" state is entered. /// </summary> private void OnEnterStartScreen(object sender, StateEventArgs eventArgs) { // Show the "Press Start button" text centered on the screen. _startTextBlock = new TextBlock { Name = "StartTextBlock", Text = "Press Start button", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, }; _uiScreen.Children.Add(_startTextBlock); // The text should pulse to indicate that a user interaction is required. // To achieve this we can animate the opacity of the TextBlock. var opacityAnimation = new SingleFromToByAnimation { From = 1, // Animate from opaque (Opacity == 1) To = 0.25f, // to nearly transparent (Opacity == 0.25) Duration = TimeSpan.FromSeconds(0.5), // over a duration of 0.5 seconds. EasingFunction = new SineEase { Mode = EasingMode.EaseInOut } }; // A SingleFromToByAnimation plays only once, but the animation should be // played back-and-forth until the user presses a button. // We need wrap the SingleFromToByAnimation in an AnimationClip or TimelineClip. // Animation clips can be used to cut and loop other animations. var loopingOpacityAnimation = new AnimationClip<float>(opacityAnimation) { LoopBehavior = LoopBehavior.Oscillate, // Play back-and-forth. Duration = TimeSpan.MaxValue // Loop forever. }; // We want to apply the animation to the "Opacity" property of the TextBlock. // All "game object properties" of a UIControl can be made "animatable". // First, get a handle to the "Opacity" property. var opacityProperty = _startTextBlock.Properties.Get<float>(TextBlock.OpacityPropertyId); // Then cast the "Opacity" property to an IAnimatableProperty. var animatableOpacityProperty = opacityProperty.AsAnimatable(); // Start the pulse animation. var animationController = AnimationService.StartAnimation(loopingOpacityAnimation, animatableOpacityProperty); // Enable "automatic recycling". This step is optional. It ensures that the // associated resources are recycled when either the animation is stopped or // the target object (the TextBlock) is garbage collected. // (The associated resources will be reused by future animations, which will // reduce the number of required memory allocations at runtime.) animationController.AutoRecycle(); }
/// <summary> /// Called when "Start" state is exited. /// </summary> private void OnExitStartScreen(object sender, StateEventArgs eventArgs) { // Clean up. _exitAnimationController.Stop(); _exitAnimationController.Recycle(); _exitAnimationIsPlaying = false; _uiScreen.Children.Remove(_startTextBlock); _startTextBlock = null; }
/// <summary> /// Called when "Game" state is entered. /// </summary> private void OnEnterGameScreen(object sender, StateEventArgs eventArgs) { // Show a dummy text. _gameTextBlock = new TextBlock { Text = "Game is running. (Press Back button to return to menu.)", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, }; _uiScreen.Children.Add(_gameTextBlock); }
/// <summary> /// Called every frame when "Start" state is active. /// </summary> private void OnUpdateStartScreen(object sender, StateEventArgs eventArgs) { if (_exitAnimationIsPlaying) return; bool transitionToMenu = false; // Check if the user presses A or START on any connected gamepad. for (var controller = PlayerIndex.One; controller <= PlayerIndex.Four; controller++) { if (InputService.IsDown(Buttons.A, controller) || InputService.IsDown(Buttons.Start, controller)) { // A or START was pressed. Assign this controller to the first "logical player". InputService.SetLogicalPlayer(LogicalPlayerIndex.One, controller); transitionToMenu = true; } } if (InputService.IsDown(MouseButtons.Left) || InputService.IsDown(Keys.Enter) || InputService.IsDown(Keys.Escape) || InputService.IsDown(Keys.Space)) { // The users has pressed the left mouse button or a key on the keyboard. if (!InputService.GetLogicalPlayer(LogicalPlayerIndex.One).HasValue) { // No controller has been assigned to the first "logical player". Maybe // there is no gamepad connected. // --> Just guess which controller is the primary player and continue. InputService.SetLogicalPlayer(LogicalPlayerIndex.One, PlayerIndex.One); } transitionToMenu = true; } if (transitionToMenu) { // Play a fade-out animation which changes the opacity from its current // value to 0. var fadeOutAnimation = new SingleFromToByAnimation { To = 0, // Animate the opacity from the current value to 0 Duration = TimeSpan.FromSeconds(0.5), // over a duration of 0.5 seconds. }; var opacityProperty = _startTextBlock.Properties.Get<float>(TextBlock.OpacityPropertyId).AsAnimatable(); _exitAnimationController = AnimationService.StartAnimation(fadeOutAnimation, opacityProperty); // When the fade-out animation finished trigger the transition from the "Start" // screen to the "Menu" screen. _exitAnimationController.Completed += (s, e) => _stateMachine.States.ActiveState.Transitions["StartToMenu"].Fire(); _exitAnimationIsPlaying = true; } }
/// <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); }
private volatile bool _allAssetsLoaded; // Will be set to true, when the background thread is finished. /// <summary> /// Called when "Loading" state is entered. /// </summary> private void OnEnterLoadingScreen(object sender, StateEventArgs eventArgs) { // Show the text "Loading..." centered on the screen. _loadingTextBlock = new TextBlock { Name = "LoadingTextBlock", // Control names are optional - but very helpful for debugging! Text = "Loading...", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, }; _uiScreen.Children.Add(_loadingTextBlock); // Start loading assets in the background. Parallel.StartBackground(LoadAssets); }
/// <summary> /// Called every frame when "SubMenu" state is active. /// </summary> private void OnUpdateSubMenuScreen(object sender, StateEventArgs eventArgs) { if (_subMenuExitAnimationIsPlaying) return; // Exit sub menu if Back button, B button, or Escape key is pressed. if (InputService.IsPressed(Buttons.Back, false, LogicalPlayerIndex.One) || InputService.IsPressed(Buttons.B, false, LogicalPlayerIndex.One) || InputService.IsPressed(Keys.Escape, false)) { InputService.IsKeyboardHandled = true; InputService.SetGamePadHandled(LogicalPlayerIndex.One, true); ExitSubMenuScreen(); } }