Skip to content

Commit

Permalink
Concept articles (#57)
Browse files Browse the repository at this point in the history
* Concept articles
  • Loading branch information
SimonDarksideJ authored Sep 8, 2024
1 parent c86e7ee commit f81ec9d
Show file tree
Hide file tree
Showing 8 changed files with 460 additions and 2 deletions.
48 changes: 48 additions & 0 deletions articles/getting_to_know/howto/HowTo_AsyncProgramming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: How to work with Asynchronous Methods in MonoGame
description: This topic describes how you can work with asynchronous methods in MonoGame.
requireMSLicense: true
---

## Overview

This topic describes how you can work with asynchronous methods in MonoGame.

MonoGame provides many methods that operate `asynchronously` for operations that may take longer than the desired render-cycle length.

Asynchronous methods consist of four elements:

- A **Begin** call that begins the asynchronous process. **Begin** methods return an [IASyncResult](http://msdn.microsoft.com/en-us/library/system.iasyncresult.aspx) object that can be used to poll for completion if a callback function is not used to detect the completion of the operation.
- An **End** call that ends the asynchronous process and returns objects or data requested by the **Begin** call. Calling the corresponding **End** method for each **Begin** method is important to prevent deadlocks and other undesirable behavior.
- An optional _callback_ method that is called by the system when the asynchronous operation completes. This is passed to the **Begin** call.
- An optional, arbitrary _tracking object_ that can be supplied to **Begin** to uniquely identify a particular asynchronous request. This object is part of the _IASyncResult_ returned by **Begin**, and is also present in the callback method's _**IASyncResult**_ parameter. Because of this, it also can be used to pass arbitrary data to the callback method when the asynchronous process completes.

The two most common methods of working with asynchronous methods are to check for completion by `polling` or by `callback`. This topic describes both methods.

> [!NOTE]
> For more exhaustive information about Event Based asynchronous methods, see [Asynchronous Programming Design Patterns](http://msdn.microsoft.com/library/ms228969.aspx) on MSDN.
>
> Alternatively, review the alternative [Task-Based Asynchronous patter](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap) approach.
## To poll for asynchronous method completion

1. Call the asynchronous **Begin** method, and save the returned _**IASyncResult**_ object to a variable that will be checked for completion.

2. In your update code, check [IsCompleted](http://msdn.microsoft.com/en-us/library/system.iasyncresult.iscompleted.aspx).

3. When [IsCompleted](http://msdn.microsoft.com/en-us/library/system.iasyncresult.iscompleted.aspx) is **true**, call the **End** method that corresponds to the **Begin** method called in step 1.

### To use a callback to check for asynchronous method completion

1. Call the asynchronous **Begin** method, passing it an [AsyncCallback](http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx) method that will be called when the asynchronous process is completed.

> [AsyncCallback](http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx) methods must return void, and take a single parameter: [IASyncResult](http://msdn.microsoft.com/en-us/library/system.iasyncresult.aspx).
2. In the callback, call the **End** method that corresponds to the **Begin** method called in step 1.

> The **End** method typically returns any data or objects requested by the **Begin** call.
## See Also

- [Asynchronous Programming Design Patterns](http://msdn.microsoft.com/library/ms228969.aspx)
- [Task-based asynchronous pattern (TAP) in .NET](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap)
34 changes: 34 additions & 0 deletions articles/getting_to_know/howto/HowTo_AutomaticRotation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: How to manage automatic rotation and scaling
description: A walkthrough what is involved in figuring out if two objects collide for MonoGame!
requireMSLicense: true
---

## Overview

This topic describes automatic rotation and scaling in the MonoGame Framework. Rotation and scaling are done in hardware at no performance cost to the game.

### Setting the Preferred Device Orientations

If your game supports more than one display orientation, as specified by [SupportedOrientations](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.SupportedOrientations) and described with [DisplayOrientation](xref:Microsoft.Xna.Framework.DisplayOrientation), the MonoGame Framework automatically rotates and scales the game when the `OrientationChanged` event is raised.

### When the device's orientation is changed

The current back buffer resolution is scaled, and can be queried by using [PreferredBackBufferWidth](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferWidth) and [PreferredBackBufferHeight](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferHeight). These values will not be the same as the non-scaled screen resolution, which can be queried by using [DisplayMode](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice.DisplayMode) or [ClientBounds](xref:Microsoft.Xna.Framework.GameWindow.ClientBounds).

## Orientation defaults

If you leave [SupportedOrientations](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.SupportedOrientations) set to **DisplayOrientation.Default**, orientation is automatically determined from your [PreferredBackBufferWidth](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferWidth) and [PreferredBackBufferHeight](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferHeight). If the [PreferredBackBufferWidth](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferWidth) is greater than the [PreferredBackBufferHeight](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferHeight), the game will run in the landscape orientation and automatically switch between **LandscapeLeft** and **LandscapeRight** depending on the position which the user holds the phone. To run a game in the portrait orientation, set the [PreferredBackBufferWidth](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferWidth) to a value smaller than the [PreferredBackBufferHeight](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferHeight).

## See Also

- [XNA Orientation Sample](https://github.com/simondarksidej/XNAGameStudio/wiki/Orientation)

### Concepts

- [What Is a Back Buffer?](../whatis/graphics/WhatIs_BackBuffer.md)

### Reference

- [SupportedOrientations](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.SupportedOrientations)
- [DisplayOrientation](xref:Microsoft.Xna.Framework.DisplayOrientation)
76 changes: 76 additions & 0 deletions articles/getting_to_know/howto/HowTo_ExitNow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: How to exit a Game Immediately
description: Demonstrates how to exit a game in response to user input.
requireMSLicense: true
---

## Overview

Demonstrates how to exit a game in response to user input.

## Exiting a Game Without Finishing the Current Update

> [!NOTE]
> Some platforms react differently to `Game.Exit`, so be sure to test on a device!
1. Derive a class from [Game](xref:Microsoft.Xna.Framework.Game).

2. Create a method that checks [KeyboardState.IsKeyDown](xref:Microsoft.Xna.Framework.Input.KeyboardState) for the state of the **ESC** key.

3. If the ESC key has been pressed, call [Game.Exit](xref:Microsoft.Xna.Framework.Game.Exit) and return **true**.

```csharp
bool checkExitKey(KeyboardState keyboardState, GamePadState gamePadState)
{
// Check to see whether ESC was pressed on the keyboard
// or BACK was pressed on the controller.
if (keyboardState.IsKeyDown(Keys.Escape) ||
gamePadState.Buttons.Back == ButtonState.Pressed)
{
Exit();
return true;
}
return false;
}
```

4. Call the method in **Game.Update**, and return from **Update** if the method returned **true**.

```csharp
GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
KeyboardState keyboardState = Keyboard.GetState();

// Check to see if the user has exited
if (checkExitKey(keyboardState, gamePadState))
{
base.Update(gameTime);
return;
}
```

5. Create a method to handle the **Game.Exiting** event.

The **Exiting** event is issued at the end of the tick in which [Game.Exit](xref:Microsoft.Xna.Framework.Game.Exit) is called.

```csharp
void Game1_Exiting(object sender, EventArgs e)
{
// Add any code that must execute before the game ends.
}
```

> [!NOTE]
> The default MonoGame project template already includes code to exit your game (as shown below), however depending on your game, you may wish to change this default logic.
>
> ```csharp
> if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
> Exit();
> ```

## See Also

- [Input Overview](./input/index.md)

### Reference

- [Game.Exit](xref:Microsoft.Xna.Framework.Game.Exit)
82 changes: 82 additions & 0 deletions articles/getting_to_know/howto/HowTo_MobileBestPractices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: How to apply Best Practices for MonoGame Games
description: The practices discussed here will help you have the most success possible with your MonoGame game.
requireMSLicense: true
---

## Overview

The practices discussed here will help you have the most success possible with your MonoGame game.

This overview covers the following topics.

- [Polish Your Game](#polish-your-game)
- [Build Intuitive and Fun Controls](#build-intuitive-and-fun-controls)
- [Support Changing Screen Orientation](#support-changing-screen-orientation)
- [Audio Tips](#audio-tips)
- [Respond Correctly to Back Button Use](#respond-correctly-to-back-button-use)
- [Diligently Save Game State](#diligently-save-game-state)

## Polish Your Game

It is difficult to over emphasize how important polish is for a successful game. The best game ideas and the most stable code do not compare to a game with an extra level of polish. Polish can be defined as putting in the extra effort to make your game look and feel its best. It also is:

- The difference between a basic menu with buttons that just work and the same menu with polish. It may take time to add a small flourish of animation for each button press, sound effect, and styled buttons to a menu, but doing so makes a big difference.
- Smooth menu operation and intuitive controls.
- Smooth transitions among screens, modes, and levels.

## Build Intuitive and Fun Controls

Avoid simulating traditional controls such as thumbsticks in MonoGame games. That type of control takes away useful space from the gameplay area and is not platform friendly. Use gestures for user input. Games that are engaging and naturally fun have controls that are natural to the platform.

|Control|Description|
|-|-|
|Touch|Touch control systems will feel natural to MonoGame users. Design games from the beginning to take full advantage of the touch screen. The touch screen is the primary way users interact with their phone, and users expect to interact with games in the same way.|
|Back Button|Although there are other buttons on the device, only the **Back** button is available to the game. Use the **Back** button for pausing and exiting the game.|
|Gestures|Design your gameplay to use touch gestures in natural ways. For example: allowing players to draw paths on the screen to direct gameplay, or allowing for group selection by stretching an on-screen rectangle around play pieces. Consider allowing navigation by dragging the landscape, and allowing users to rotate by touching and rotating two fingers.|

## Support Changing Screen Orientation

MonoGame supports three screen orientation views: portrait, landscape left, and landscape right. Portrait view is the default view for applications. The **Start** button is always presented in portrait view. In portrait view, the page is oriented vertically with the steering buttons displayed at the bottom of the phone.

In both landscape views, the **Status Bar** and **Application Bar** remain on the side of the screen that has the **Power** and **Start** buttons. Landscape left has the **Status Bar** on the left, and landscape right has the **Status Bar** on the right.

Routinely check current screen orientation and enable gameplay regardless of how the phone is held.

> [!NOTE]
> For more detailed information on screen rotation see [Automatic Rotation and Scaling](HowTo_AutomaticRotation.md).
## Audio Tips

Audio can enrich an application and add needed polish. Playing audio should also dependent on user preference. Consider these tips when building audio into your game:

- Play sound effects at an average volume to avoid forcing the player to adjust the devices volume during the game.
- Allow sound effects and background music to be turned on and off by users.
- Play directional sounds that reflect a location of the originating element on the screen.

> [!NOTE]
> For more detailed information on audio see [Creating and Playing Sounds](../whatis/audio/index.md).
## Respond Correctly to Back Button Use

Games must respond to use of the **Back** button, or "esc" on Windows. MonoGame consistently uses **Back** to move backward through the UI. Games must implement this behavior as follows:

- During gameplay, the game can do one of the following:
- Present a contextual pause menu (dialog). Pressing the **Back** button again while in the pause menu closes the menu and resumes the game.
- Navigate the user to the prior menu screen. Pressing the **Back** button again should continue to return the user to the previous menu or page.

- Outside of gameplay, such as within the game's menu system, pressing **Back** must return to the previous menu or page.
- At the game’s initial (start) screen, pressing **Back** must exit the game.

It is a good practice in case the player exits the game to automatically save the game state while the pause menu is shown.

## Diligently Save Game State

On MonoGame, a game might be exited at any time. An incoming call may interrupt gameplay, or the user might quit the game by using the **Home** or **Search** buttons to use other applications. We recommend saving game state whenever possible to protect the user's time investment in the game. We also recommend that you make a distinction between the automatically saved game state and the user's explicitly saved games. Automatically saved games should be viewed as a backup in case the game ends unexpectedly, but should not replace the user's ability to save the game at a chosen time or place.

If you implement automatic game saving, check for an automatically saved state when the game launches. If found, let the user choose to resume the game from the automatically saved state or from a specific manually saved game, if present. During the save process, we also recommend that you display a visual cue warning users not to press the **Search** or **Home** button because the action could cause the game to exit before the save is complete.

## See Also

- [Creating a your first MonoGame Game](https://monogame.net/articles/getting_started/index.html)
- [Setting Aspect ratios for your game](graphics/HowTo_AspectRatio.md)
49 changes: 49 additions & 0 deletions articles/getting_to_know/howto/HowTo_PlayerResize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: How to resize a Game
description: Demonstrates how to handle the resizing of the active game window.
requireMSLicense: true
---

## Overview

On Desktop, it is normally possible for the user to change how the game window is sized, either by moving from full-screen to windowed, or by altering the resolution of their screen. In these cases an event is fired to enable you to handle these changes.

> [!NOTE]
> Ideally, your games drawing should always take account of the Aspect Ratio and dimensions of the displayed screen, regardless of device, else content may not be always drawn where you expect it to.
## To handle player window resizing to a game

1. Derive a class from [Game](xref:Microsoft.Xna.Framework.Game).

2. Set [Game.GameWindow.AllowUserResizing](xref:Microsoft.Xna.Framework.GameWindow#Microsoft_Xna_Framework_GameWindow_AllowUserResizing) to **true**.

3. Add an event handler for the **ClientSizeChanged** event of [Game.Window](xref:Microsoft.Xna.Framework.Game#Microsoft_Xna_Framework_Game_Window).

```csharp
this.Window.AllowUserResizing = true;
this.Window.ClientSizeChanged += new EventHandler<EventArgs>(Window_ClientSizeChanged);
```

4. Implement a method to handle the **ClientSizeChanged** event of [Game.Window](xref:Microsoft.Xna.Framework.GameWindow).

```csharp
void Window_ClientSizeChanged(object sender, EventArgs e)
{
// Make changes to handle the new window size.
}
```

## See Also

- [How to articles for the Graphics Pipeline](index.md)

### Concepts

- [What Is 3D Rendering?](../whatis/graphics/WhatIs_3DRendering.md)
- [What Is a Back Buffer?](../whatis/graphics/WhatIs_BackBuffer.md)
- [What Is a Viewport?](../whatis/graphics/WhatIs_Viewport.md)

### Reference

- [GameWindow](xref:Microsoft.Xna.Framework.GameWindow)
- [GraphicsDeviceManager](xref:Microsoft.Xna.Framework.GraphicsDeviceManager)
71 changes: 71 additions & 0 deletions articles/getting_to_know/howto/HowTo_TimingOut.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: How to exit a Game After a Time Out
description: Demonstrates how to exit a game after a period of time (such as inactivity) has passed.
requireMSLicense: true
---

## Overview

Games are meant to be played, but some users had a tendency to walk away from their controllers or go and do something IRL, when this happens, especially if your game is very dynamic, it is good practice to monitor for a lack of interactivity and pause the game (unless you want the player to die horribly for not paying attention).

## To make a game time out

1. Create a class that derives from [Game](xref:Microsoft.Xna.Framework.Game).

2. Determine the desired time-out limit in milliseconds.

```csharp
// Time out limit in ms.
static private int TimeOutLimit = 4000; // 4 seconds
```

3. Add a variable for tracking the elapsed time since the most recent user activity.

```csharp
// Amount of time that has passed.
private double timeoutCount = 0;
```

4. When user input is checked, set a flag indicating whether any user activity has taken place.

```csharp
GamePadState blankGamePadState = new GamePadState(
new GamePadThumbSticks(), new GamePadTriggers(), new GamePadButtons(),
new GamePadDPad());
```

5. In **Update**, if there has not been any user activity, increment the tracking variable by the elapsed time since the last call to **Update**.

6. If there has been some user activity, set the tracking variable to zero.

```csharp
// Check to see if there has been any activity
if (checkActivity(keyboardState, gamePadState) == false)
{
timeoutCount += gameTime.ElapsedGameTime.Milliseconds;
}
else
timeoutCount = 0;
```

7. Check whether the value of the tracking variable is greater than the time-out limit.

8. If the variable is greater than the limit, perform some time-out logic such as playing an idle animation or, in this case, exit the game.

```csharp
// Timeout if idle long enough
if (timeoutCount > TimeOutLimit)
{
Exit();
base.Update(gameTime);
return;
}
```

## See Also

- [Input Overview](./input/index.md)

### Reference

- [Game.Exit](xref:Microsoft.Xna.Framework.Game.Exit)
Loading

0 comments on commit f81ec9d

Please sign in to comment.