From 18831845b28ed78d934576f1f1403a00e60ec340 Mon Sep 17 00:00:00 2001 From: QuestSWE Date: Thu, 12 Jun 2025 21:11:36 -0400 Subject: [PATCH 1/6] Clarify distance labels in circle collision explanation - Added missing word "circles" in description of 'a' - Reworded all three distance descriptions for clarity and consistency --- .../building_2d_games/12_collision_detection/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/tutorials/building_2d_games/12_collision_detection/index.md b/articles/tutorials/building_2d_games/12_collision_detection/index.md index 1468400c..8e728469 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/index.md +++ b/articles/tutorials/building_2d_games/12_collision_detection/index.md @@ -44,9 +44,9 @@ To find the distance between two circles, imagine drawing a line from the center In *Figure 12-1* above -- $a$ is the distance between the center of the two on the x-axis (horizontal). -- $b$ is the distance between the center of the two circles on the y-axis (vertical). -- $c$ is the total distance between the center of the two circles. +- $a$ is the horizontal distance between the centers of the two circles (x-axis). +- $b$ is the vertical distance between the centers of the two circles (y-axis). +- $c$ is the total distance between the centers of the two circles. Since this forms a right triangle, we can use Pythagorean's Theorem to calculate $c^2$ given $a^2$ and $b^2$: From 7f87c52ea3ca98427bc5947e809d35fae28b1e7a Mon Sep 17 00:00:00 2001 From: QuestSWE Date: Sat, 14 Jun 2025 14:31:56 -0400 Subject: [PATCH 2/6] fix(title scene draw): remove unused color assignment to dropShadowColor --- .../building_2d_games/17_scenes/snippets/titlescene.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs b/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs index 68baea08..a07bf886 100644 --- a/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs +++ b/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs @@ -100,8 +100,7 @@ public override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. Core.SpriteBatch.Begin(samplerState: SamplerState.PointClamp); - Color dropShadowColor = new Color(19, 23, 46, 175); - dropShadowColor = Color.Black * 0.5f; + Color dropShadowColor = Color.Black * 0.5f; // Draw the Dungeon text slightly offset from it is original position and // with a transparent color to give it a drop shadow From 19a35e713c1f268ea29a6358a49bad020fd6150b Mon Sep 17 00:00:00 2001 From: QuestSWE Date: Sat, 14 Jun 2025 14:49:25 -0400 Subject: [PATCH 3/6] fix(title scene draw): add missing comment above dropShadowColor assignment --- .../tutorials/building_2d_games/17_scenes/snippets/titlescene.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs b/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs index a07bf886..17501401 100644 --- a/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs +++ b/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs @@ -100,6 +100,7 @@ public override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. Core.SpriteBatch.Begin(samplerState: SamplerState.PointClamp); + // The color to use for the drop shadow text. Color dropShadowColor = Color.Black * 0.5f; // Draw the Dungeon text slightly offset from it is original position and From 797342477bc1ad57679489c8734d2811ff91a663 Mon Sep 17 00:00:00 2001 From: QuestSWE Date: Mon, 16 Jun 2025 17:15:11 -0400 Subject: [PATCH 4/6] refactor: fix typo in textureatlas.cs filename --- .../snippets/{tetureatlas.cs => textureatlas.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/{tetureatlas.cs => textureatlas.cs} (100%) diff --git a/articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/tetureatlas.cs b/articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/textureatlas.cs similarity index 100% rename from articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/tetureatlas.cs rename to articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/textureatlas.cs From a8398de0ab113a6e441f7317b8dd0e28386a7f41 Mon Sep 17 00:00:00 2001 From: QuestSWE Date: Mon, 16 Jun 2025 17:26:08 -0400 Subject: [PATCH 5/6] fix(docs): correct typos and improve wording across documentation --- articles/console_access.md | 4 +- articles/contributing.md | 8 +- articles/help_and_support.md | 6 +- articles/index.md | 4 +- articles/samples.md | 2 +- .../01_what_is_monogame/index.md | 4 +- .../02_getting_started/index.md | 12 +- .../03_the_game1_file/index.md | 4 +- .../04_creating_a_class_library/index.md | 6 +- .../snippets/core.cs | 10 +- .../05_content_pipeline/index.md | 4 +- .../06_working_with_textures/index.md | 2 +- .../snippets/color.cs | 2 +- .../06_working_with_textures/snippets/draw.cs | 2 +- .../snippets/draw_all_params.cs | 2 +- .../snippets/draw_center.cs | 2 +- .../snippets/draw_center_wrong.cs | 2 +- .../snippets/opacity.cs | 2 +- .../snippets/origin.cs | 2 +- .../snippets/rotation.cs | 2 +- .../snippets/scale.cs | 2 +- .../snippets/scale_no_origin.cs | 2 +- .../snippets/scale_vector2.cs | 2 +- .../snippets/spriteeffects.cs | 2 +- .../snippets/spriteeffects_flags.cs | 2 +- .../07_optimizing_texture_rendering/index.md | 6 +- .../snippets/textureregion.cs | 2 +- .../08_the_sprite_class/index.md | 6 +- .../08_the_sprite_class/snippets/game1.cs | 2 +- .../08_the_sprite_class/snippets/sprite.cs | 2 +- .../09_the_animatedsprite_class/index.md | 6 +- .../snippets/game1.cs | 2 +- .../10_handling_input/index.md | 16 +- .../10_handling_input/snippets/buttonstate.cs | 2 +- .../snippets/enablegestures.cs | 2 +- .../10_handling_input/snippets/game1.cs | 2 +- .../snippets/gamepadstate.cs | 2 +- .../10_handling_input/snippets/inputbuffer.cs | 4 +- .../snippets/isbuttondown.cs | 2 +- .../10_handling_input/snippets/thumbstick.cs | 4 +- .../10_handling_input/snippets/triggers.cs | 2 +- .../10_handling_input/snippets/vibration.cs | 6 +- .../11_input_management/index.md | 12 +- .../11_input_management/snippets/core.cs | 14 +- .../11_input_management/snippets/game1.cs | 2 +- .../snippets/gamepadinfo.cs | 4 +- .../snippets/inputmanager.cs | 1 - .../snippets/keyboardinfo.cs | 2 +- .../11_input_management/snippets/mouseinfo.cs | 2 +- .../12_collision_detection/index.md | 153 +++++++++--------- .../snippets/blocking_example.cs | 8 +- .../snippets/bounce_example.cs | 24 +-- .../snippets/contains_example.cs | 2 +- .../12_collision_detection/snippets/game1.cs | 16 +- .../snippets/trigger_example.cs | 6 +- .../snippets/vector2_distance.cs | 2 +- .../13_working_with_tilemaps/index.md | 80 ++++----- .../snippets/game1.cs | 2 +- .../15_audio_controller/index.md | 32 ++-- .../snippets/audiocontroller.cs | 12 +- .../15_audio_controller/snippets/core.cs | 12 +- .../15_audio_controller/snippets/game1.cs | 30 ++-- .../16_working_with_spritefonts/index.md | 4 +- .../snippets/center_example.cs | 4 +- .../snippets/game1.cs | 26 +-- .../snippets/measurestring.cs | 2 +- .../building_2d_games/17_scenes/index.md | 18 +-- .../17_scenes/snippets/core.cs | 18 +-- .../17_scenes/snippets/game1.cs | 6 +- .../17_scenes/snippets/gamescene.cs | 32 ++-- .../17_scenes/snippets/titlescene.cs | 12 +- .../18_texture_sampling/index.md | 2 +- .../snippets/titlescene.cs | 18 +-- .../19_user_interface_fundamentals/index.md | 6 +- .../20_implementing_ui_with_gum/index.md | 12 +- .../22_snake_game_mechanics/index.md | 20 +-- .../23_completing_the_game/index.md | 10 +- .../snippets/gamescene/collisionchecks.cs | 4 +- .../snippets/gamescene/draw.cs | 2 +- .../snippets/gamescene/eventhandlers.cs | 6 +- .../snippets/gamescene/initialize.cs | 4 +- .../snippets/gamescene/initializenewgame.cs | 8 +- .../snippets/gamescene/loadcontent.cs | 10 +- .../gamescene/positionbatawayfromslime.cs | 10 +- .../snippets/gamescene/statechanges.cs | 12 +- .../snippets/gamescene/update.cs | 14 +- .../building_2d_games/24_shaders/index.md | 10 +- .../24_shaders/snippets/gamescene/draw.cs | 2 +- .../24_shaders/snippets/gamescene/fields.cs | 2 +- .../24_shaders/snippets/gamescene/gameover.cs | 6 +- .../snippets/gamescene/loadcontent.cs | 12 +- .../snippets/gamescene/togglepause.cs | 10 +- .../24_shaders/snippets/gamescene/update.cs | 14 +- .../24_shaders/snippets/grayscaleeffect.fx | 4 +- .../25_packaging_game/index.md | 22 +-- .../26_publish_to_itch/index.md | 6 +- .../building_2d_games/27_conclusion/index.md | 12 +- articles/whats_new.md | 10 +- 98 files changed, 472 insertions(+), 474 deletions(-) diff --git a/articles/console_access.md b/articles/console_access.md index a3b58069..d02bd8c6 100644 --- a/articles/console_access.md +++ b/articles/console_access.md @@ -5,7 +5,7 @@ description: How do I get access to console versions of MonoGame? MonoGame is free to use on all platforms from the [public repository](https://github.dev/MonoGame/monogame), but the code for supporting console platforms is only accessible to authorized console developers. -These platforms are provided as private code repositories that add integrations with the console vendor's APIs and platform specific documentation. +These platforms are provided as private code repositories that add integrations with the console vendor's APIs and platform-specific documentation. ## Application process @@ -23,7 +23,7 @@ The instructions below will help you gain access to each platform. ![Nintendo Switch](images/nintendo_switch.png) -Your first step is to register as [Nintendo Developer](https://developer.nintendo.com/register) for Nintendo Switch. +Your first step is to register as a [Nintendo Developer](https://developer.nintendo.com/register) for Nintendo Switch. Once you are in the program, you can go to the middleware page and fill out the [MonoGame - Nintendo Developer Authorization](https://developer.nintendo.com/group/development/getting-started/g1kr9vj6/middleware/monogame) form. diff --git a/articles/contributing.md b/articles/contributing.md index 7fae7248..20d65497 100644 --- a/articles/contributing.md +++ b/articles/contributing.md @@ -19,7 +19,7 @@ Thank you for choosing to contribute to the MonoGame project! This page provides The MonoGame documentation contains two types of documents: articles and API references. -Articles include manuals, guides and tutorials on how to use the MonoGame Framework to create games. +Articles include manuals, guides, and tutorials on how to use the MonoGame Framework to create games. API references provide detailed explanation of each class and method found in the MonoGame Framework. The documentation is written in the [C# XML format](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/xml-documentation-comments) and is inline to the MonoGame source code. @@ -68,7 +68,7 @@ Being inline with the code allows you to easily look for critical information wi ### Focus on What Adds Value to the Consumer -Limit documentation to public methods and functions unless there is a specific reason to include internal methods, while documenting internals helps with readability of the code, it provides limited use to consumers of the MonoGame Framework. +Limit documentation to public methods and functions unless there is a specific reason to include internal methods, while documenting internals helps with the readability of the code, it provides limited use to consumers of the MonoGame Framework. ### Documentation Is Referenced Not Read @@ -94,7 +94,7 @@ With a few points to call out: #### `` and `` should be used whenever an API reference is used in the documentation -To ensure that API documentation is linked to whichever reference is used, `` and `` references should be used, this helps users navigate the methods, especially when looking up initializers or use of a property or method. +To ensure that API documentation is linked to whichever reference is used, `` and `` references should be used. This helps users navigate the methods, especially when looking up initializers or use of a property or method. #### Avoid self referencing `` unless it provides value @@ -121,7 +121,7 @@ and the y component uses 6 bits. > [!NOTE] > If the `cref` description would cause the line to exceed the 120 recommendation, this is generally ok, so long as the rendered line does not exceed the limit. -> THe limit however, is more of a guideline than a hard rule, so common sense should be applied to keep the limit near 120 characters. +> The limit however, is more of a guideline than a hard rule, so common sense should be applied to keep the limit near 120 characters. #### Use the packed multi-line style with surrounding tags diff --git a/articles/help_and_support.md b/articles/help_and_support.md index 8815c31b..18b2d75f 100644 --- a/articles/help_and_support.md +++ b/articles/help_and_support.md @@ -3,14 +3,14 @@ title: Help and Support description: Where to get help and support when using MonoGame. --- -There is a wealth of [community created content, blogs and tutorials](tutorials/index.md) available. +There is a wealth of [community-created content, blogs and tutorials](tutorials/index.md) available. -You can also chat to other MonoGame users via [Discord](https://discord.com/invite/monogame). +You can also chat with other MonoGame users via [Discord](https://discord.com/invite/monogame). If you want to find an answer to a more specific problem, you can ask it on our [GitHub Discussions](https://github.com/MonoGame/MonoGame/discussions) page. ## Bugs and feature requests -If you find a bug or have a feature request, [please open a new issue](https://github.com/mono/monogame/issues). Before opening any issue, please search for existing issues. +If you find a bug or have a feature request, [please open a new issue](https://github.com/mono/monogame/issues). Before opening an issue, please check for existing ones. Please note that the issue tracker is not for requesting help. diff --git a/articles/index.md b/articles/index.md index a2acd0b7..3b2ca64f 100644 --- a/articles/index.md +++ b/articles/index.md @@ -20,7 +20,7 @@ This documentation [helps you to get started](getting_started/index.md) by provi Please use the links at the top and left to navigate the documentation sections. -> This documentation assume that the reader has a basic knowledge of the C# programming language. +> This documentation assumes that the reader has a basic knowledge of the C# programming language. ## What is MonoGame exactly? @@ -36,4 +36,4 @@ If you love coding and understanding how things work under the hood, MonoGame mi MonoGame is an open-source project maintained by its community. Great open source projects require high-quality documentation. This is a call for volunteers to continue to help us make the MonoGame documentation truly great. If you can create tutorials, feature guides, code snippets, reference docs, video walkthroughs, or make any improvement to the current documentation, we could use your help! -Check out the [README on GitHub](https://github.com/MonoGame/MonoGame/blob/develop/README.md) or [talk with us on discord](https://discord.gg/monogame) to learn how to help! +Check out the [README on GitHub](https://github.com/MonoGame/MonoGame/blob/develop/README.md) or [talk with us on Discord](https://discord.gg/monogame) to learn how to help! diff --git a/articles/samples.md b/articles/samples.md index 64259b21..f572223e 100644 --- a/articles/samples.md +++ b/articles/samples.md @@ -21,7 +21,7 @@ These are [the official samples](https://github.com/MonoGame/MonoGame.Samples) t ### 3D Samples -- [FuelCell](https://github.com/MonoGame/MonoGame.Samples/tree/3.8.4/NeonShooter) +- [FuelCell](https://github.com/MonoGame/MonoGame.Samples/tree/3.8.4/FuelCell) - [ShipGame](https://github.com/MonoGame/MonoGame.Samples/tree/3.8.4/ShipGame) ## XNA Game Studio Archive diff --git a/articles/tutorials/building_2d_games/01_what_is_monogame/index.md b/articles/tutorials/building_2d_games/01_what_is_monogame/index.md index 087b63ac..137fce42 100644 --- a/articles/tutorials/building_2d_games/01_what_is_monogame/index.md +++ b/articles/tutorials/building_2d_games/01_what_is_monogame/index.md @@ -15,7 +15,7 @@ As XNA became more popular, the need for cross-platform development started to g The official first release of MonoGame occurred in 2011, as an open-source version of XNA. While it still had the same familiar API as XNA, the cross-platform support was expanded to include Windows, macOS, Linux, iOS, Android, Xbox, and PlayStation. Despite Microsoft discontinuing XNA in 2013, MonoGame continued to grow and develop. Maintenance of the project was given to [Steve Williams](https://github.com/KonajuGames) and [Tom Spilman](https://github.com/tomspilman) in 2014. In order to direct its future development and undertaking, the [MonoGame Foundation](https://monogame.net/about/) was formed on September 29th, 2023. -Today, MonoGame is a mature cross-platform framework, that is built with the spirit of preserving XNA while adopting modern game development practices. Some popular titles created using MonoGame includes [Celeste](https://store.steampowered.com/app/504230/Celeste/), [Stardew Valley](https://store.steampowered.com/app/413150/Stardew\_Valley/), and [Streets of Rage 4](https://store.steampowered.com/app/985890/Streets\_of\_Rage\_4/). +Today, MonoGame is a mature cross-platform framework, that is built with the spirit of preserving XNA while adopting modern game development practices. Some popular titles created using MonoGame include [Celeste](https://store.steampowered.com/app/504230/Celeste/), [Stardew Valley](https://store.steampowered.com/app/413150/Stardew\_Valley/), and [Streets of Rage 4](https://store.steampowered.com/app/985890/Streets\_of\_Rage\_4/). | ![Figure 1-1: Celeste](./images/celeste.png) | ![Figure 1-2: Stardew Valley](./images/stardew-valley.png) | | :-------------------------------------------------: | :--------------------------------------------------------: | @@ -36,7 +36,7 @@ MonoGame, following in the footsteps of XNA, is a "bring your own tools" framewo At its core, MonoGame offers a set of libraries and APIs to handle common game development tasks. These include: 1. **Graphics Rendering**: 2D and 3D rendering are supported through the graphics API offered by MonoGame. This API provides sprite batching for 2D graphics, a flexible 3D pipeline, and shaders for custom visuals and effects. -2. **Input Handling**: Input from keyboard, mouse, gamepads, and touchscreens are supported, allowing for development of games for any platform and different styles of play. +2. **Input Handling**: Input from keyboard, mouse, gamepads, and touchscreens is supported, allowing for development of games for any platform and different styles of play. 3. **Audio**: A comprehensive audio system that can be used to create sound effects as well as play music with included support for many audio formats. 4. **Content Pipeline**: An out-of-the-box workflow for importing and processing game assets such as textures, models, and audio, and compiling them to a format that is optimal for the game's target platform. 5. **Math Library**: A math library specifically optimized for game development, providing essential mathematical functions and operations. diff --git a/articles/tutorials/building_2d_games/02_getting_started/index.md b/articles/tutorials/building_2d_games/02_getting_started/index.md index e2387fd2..6a64536d 100644 --- a/articles/tutorials/building_2d_games/02_getting_started/index.md +++ b/articles/tutorials/building_2d_games/02_getting_started/index.md @@ -27,12 +27,12 @@ The first thing we need to do is install the .NET *Software Development Kit* (SD > [!NOTE] > The minimum supported version is .NET 8 -3. Once the download finishes, run the installer +3. Once the download finishes, run the installer. ### [macOS](#tab/macos) 1. Open a web browser and navigate to [https://dotnet.microsoft.com/en-us/download](https://dotnet.microsoft.com/en-us/download). -2. Choose the version of the .NET SDK to install and click lick the *Download .NET SDK x64 (Intel)* button start the download of the .NET SDK Installer. +2. Choose the version of the .NET SDK to install and click the *Download .NET SDK x64 (Intel)* button start the download of the .NET SDK Installer. > [!NOTE] > The minimum supported version is .NET 8 @@ -58,7 +58,7 @@ sudo apt-get update && sudo apt-get install -y dotnet-sdk-8.0 ## Install Additional Workloads (Optional) -After installing the .NET SDK, if you intend to target mobile devices such as Android or iOS, you will also need to install the corresponding mobile workloads. To do this, open a *Command Prompt* or *Terminal* window and enter the following commands +After installing the .NET SDK, if you intend to target mobile devices such as Android or iOS, you will also need to install the corresponding mobile workloads. To do this, open a *Command Prompt* or *Terminal* window and enter the following commands. ```sh dotnet workload install ios @@ -75,7 +75,7 @@ dotnet new install MonoGame.Templates.CSharp ## Installing Visual Studio Code -*Visual Studio Code* (VSCode) is a free, light weight editor. Depending on the programming language you are using, it is just a matter of installing the appropriate extension to support that particular language. VSCode is also cross-platform, meaning you can use it for development on Windows, macOS, and Linux. To ensure that all readers can follow this tutorial regardless of the operating system used, we will be using VSCode as our IDE. +*Visual Studio Code* (VSCode) is a free, lightweight editor. Depending on the programming language you are using, it is just a matter of installing the appropriate extension to support that particular language. VSCode is also cross-platform, meaning you can use it for development on Windows, macOS, and Linux. To ensure that all readers can follow this tutorial regardless of the operating system used, we will be using VSCode as our IDE. To install VSCode, follow the instructions for your operating system below: @@ -193,7 +193,7 @@ With your development environment setup, it is time to create your first MonoGam > dotnet new install MonoGame.Templates.CSharp > ``` -5. After choosing the template, a dialog window will appear asking you to choose a location to save the project, this is a folder where your projects will will be created by default. +5. After choosing the template, a dialog window will appear asking you to choose a location to save the project, this is a folder where your projects will be created by default. 6. Next, you will be prompted to enter a name for the project. Enter the name `DungeonSlime`, which will create your project in a new folder with the same name. 7. If this is your first time creating your project, you will be asked to choose a solution format, simply select the default `.sln` option and click `Next` to continue. (This does not occur with subsequent projects) 8. Finally, select the *Create Project* prompt. @@ -255,7 +255,7 @@ Now that your development environment is setup and ready to go, you can dive in 2. What is the primary reason that game applications implement a *game loop* structure instead of using an event-based approach like traditional desktop applications? :::question-answer - Game application implement a *game loop* structure because games need to continuously update and render, event when there is no user input. In games, objects might be moving, animations playing, and physics calculating regardless of user interaction, requiring constant updating and rendering until the game is told to exit. + Game applications implement a *game loop* structure because games need to continuously update and render, event when there is no user input. In games, objects might be moving, animations playing, and physics calculating regardless of user interaction, requiring constant updating and rendering until the game is told to exit. ::: 3. What is the color of the game window when you run a MonoGame project for the first time? diff --git a/articles/tutorials/building_2d_games/03_the_game1_file/index.md b/articles/tutorials/building_2d_games/03_the_game1_file/index.md index c00ca414..92c6d550 100644 --- a/articles/tutorials/building_2d_games/03_the_game1_file/index.md +++ b/articles/tutorials/building_2d_games/03_the_game1_file/index.md @@ -31,7 +31,7 @@ This class provides the following structure: ## Graphics and Rendering -The graphics pipeline in monogame starts with two components: the [**GraphicsDeviceManager**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager) and [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch). +The graphics pipeline in MonoGame starts with two components: the [**GraphicsDeviceManager**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager) and [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch). [!code-csharp[](./snippets/game1.cs?start=9&end=10)] @@ -50,7 +50,7 @@ After that, the [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) m This separation allows you to perform setup tasks in a logical order; core systems in the constructor and game-specific initializations in the [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) method. The call to `base.Initialize()` should never be removed, as this is where the graphics device is initialized for the target platform. > [!TIP] -> You may be wondering why there is an [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) method instead of performing all initializations in the constructor. The [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) method is a `virtual` method that is overridden, and [it is advised to not call overridable methods from within a constructor](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2214), as this can lead to unexpected states in object constructor when called. Additionally, when the constructor is called, the base constructor will instantiate properties and services based on the target platform that may be needed first before performing initializations for the game itself. +> You may be wondering why there is an [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) method instead of performing all initializations in the constructor. The [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) method is a `virtual` method that is overridden, and [it is advised to not call overridable methods from within a constructor](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2214), as this can lead to unexpected states in the object constructor when called. Additionally, when the constructor is called, the base constructor will instantiate properties and services based on the target platform that may be needed first before performing initializations for the game itself. ## Content Loading diff --git a/articles/tutorials/building_2d_games/04_creating_a_class_library/index.md b/articles/tutorials/building_2d_games/04_creating_a_class_library/index.md index 323bb731..b26fb564 100644 --- a/articles/tutorials/building_2d_games/04_creating_a_class_library/index.md +++ b/articles/tutorials/building_2d_games/04_creating_a_class_library/index.md @@ -52,7 +52,7 @@ To use the template to add the class library, perform the following based on whi To add the class library using the MonoGame Game Library project template in Visual Studio Code, perform the following: 1. In the [*Solution Explorer*](../02_getting_started/index.md#install-the-c-dev-kit-extension) panel, right-click the *DungeonSlime* solution. -2. Chose *New Project* from the context menu. +2. Choose *New Project* from the context menu. 3. Enter "MonoGame Game Library" and select it as the template to use. 4. Name the project "MonoGameLibrary". 5. When prompted for a location, use the default option, which will put the new project in a folder next to your game project. @@ -128,7 +128,7 @@ When using the *MonoGame Game Library* project template, the generated project c > > If you would like more information on this, Simon Jackson has written the article [Going cross-platform with MonoGame](https://darkgenesis.zenithmoon.com/going-cross-platform-with-monogame.html) which covers this in more detail. > -> Also the `2D Start Kit` and `2D Blank Start Kit` templates provide you with a richer startup project targetting all platforms, using a common Class Library to reuse code across them all. Although we recommend completing this tutorial first before tackling that beast. +> Also the `2D StartKit` and `2D Blank StartKit` templates provide you with a richer startup project targeting all platforms, using a common Class Library to reuse code across them all. Although we recommend completing this tutorial first before tackling that beast. ## Creating Our First Library Module @@ -153,7 +153,7 @@ The `Core` class provides the following features: This approach provides a consistent foundation for all our games, handling common setup tasks and providing convenient access to core functionality. > [!NOTE] -> As this tutorial progress, we will be coming back to this `Core` class to add more to it. +> As this tutorial progresses, we will be coming back to this `Core` class to add more to it. ## Updating Our Game to Use the Core Class diff --git a/articles/tutorials/building_2d_games/04_creating_a_class_library/snippets/core.cs b/articles/tutorials/building_2d_games/04_creating_a_class_library/snippets/core.cs index 581bd849..3fd31b79 100644 --- a/articles/tutorials/building_2d_games/04_creating_a_class_library/snippets/core.cs +++ b/articles/tutorials/building_2d_games/04_creating_a_class_library/snippets/core.cs @@ -55,25 +55,25 @@ public Core(string title, int width, int height, bool fullScreen) // Create a new graphics device manager. Graphics = new GraphicsDeviceManager(this); - // Set the graphics defaults + // Set the graphics defaults. Graphics.PreferredBackBufferWidth = width; Graphics.PreferredBackBufferHeight = height; Graphics.IsFullScreen = fullScreen; - // Apply the graphic presentation changes + // Apply the graphic presentation changes. Graphics.ApplyChanges(); - // Set the window title + // Set the window title. Window.Title = title; // Set the core's content manager to a reference of hte base Game's // content manager. Content = base.Content; - // Set the root directory for content + // Set the root directory for content. Content.RootDirectory = "Content"; - // Mouse is visible by default + // Mouse is visible by default. IsMouseVisible = true; } diff --git a/articles/tutorials/building_2d_games/05_content_pipeline/index.md b/articles/tutorials/building_2d_games/05_content_pipeline/index.md index c2071229..6577b806 100644 --- a/articles/tutorials/building_2d_games/05_content_pipeline/index.md +++ b/articles/tutorials/building_2d_games/05_content_pipeline/index.md @@ -59,7 +59,7 @@ To open the *Content.mgcb* content project file in the MGCB Editor using the dot 2. Enter the following dotnet CLI command ```sh - dotnet mgcb-editor ./Content/Content.mgcb` + dotnet mgcb-editor ./Content/Content.mgcb ``` --- @@ -236,7 +236,7 @@ Running the game now will show the MonoGame logo displayed in the upper-left cor | :--------------------------------------------------------------------------------: | | **Figure 5-8: The MonoGame logo drawn to the game window** | -## Adding Build-In Asset Types +## Adding Built-In Asset Types The MGCB Editor can also create certain built-in asset types. In this section we will explore these types and this functionality. If not already open, [open the MGCB Editor](#the-mgcb-editor) and perform the following: diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/index.md b/articles/tutorials/building_2d_games/06_working_with_textures/index.md index 37136c24..2698441a 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/index.md +++ b/articles/tutorials/building_2d_games/06_working_with_textures/index.md @@ -49,7 +49,7 @@ The [**SpriteBatch.Draw**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch.Dra > [!TIP] > Try adjusting the position and color parameters and see how they affect the image being drawn. -MonoGame uses a coordinate system where (0, 0) is at the screen's upper-left corner. X values increase moving right, and Y values increase moving down. Understanding this, we wil try to center the logo on the game window. +MonoGame uses a coordinate system where (0, 0) is at the screen's upper-left corner. X values increase moving right, and Y values increase moving down. Understanding this, we will try to center the logo on the game window. To center content on the screen, we need to find the window's center point. We can access this using the [**Window.ClientBounds**](xref:Microsoft.Xna.Framework.GameWindow.ClientBounds) property from the [**Game**](xref:Microsoft.Xna.Framework.Game) class, which represents the rectangular bounds of the game window. [**Window.ClientBounds**](xref:Microsoft.Xna.Framework.GameWindow.ClientBounds) exposes both [**Width**](xref:Microsoft.Xna.Framework.Rectangle.Width) and [**Height**](xref:Microsoft.Xna.Framework.Rectangle.Height) properties for the window's dimensions in pixels. By dividing these dimensions in half, we can calculate the window's center coordinates. We can update our [**Draw**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch.Draw(Microsoft.Xna.Framework.Graphics.Texture2D,Microsoft.Xna.Framework.Rectangle,Microsoft.Xna.Framework.Color)) method to use this: diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/color.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/color.cs index 7ba80984..dcf8fe5b 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/color.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/color.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw.cs index 1cb3b5c8..c6846d49 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw(_logo, Vector2.Zero, Color.White); // Always end the sprite batch when finished. diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_all_params.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_all_params.cs index 5bb7d74b..bafabd70 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_all_params.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_all_params.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center.cs index 447d7795..8eccebca 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the logo texture + // Draw the logo texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center_wrong.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center_wrong.cs index 553702f6..3355f6e2 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center_wrong.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/draw_center_wrong.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the logo texture + // Draw the logo texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/opacity.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/opacity.cs index f204d296..dd68870f 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/opacity.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/opacity.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/origin.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/origin.cs index 646e2039..24c6f403 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/origin.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/origin.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/rotation.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/rotation.cs index af26ea6f..d2aafa83 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/rotation.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/rotation.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale.cs index 790cc1ca..a3463aa8 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_no_origin.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_no_origin.cs index 3f0b7c9e..6172be0c 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_no_origin.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_no_origin.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_vector2.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_vector2.cs index d20c0d7c..dcaa1867 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_vector2.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/scale_vector2.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects.cs index 234cd235..6c5baee1 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects_flags.cs b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects_flags.cs index 8f2d4f43..d6925f87 100644 --- a/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects_flags.cs +++ b/articles/tutorials/building_2d_games/06_working_with_textures/snippets/spriteeffects_flags.cs @@ -6,7 +6,7 @@ protected override void Draw(GameTime gameTime) // Begin the sprite batch to prepare for rendering. SpriteBatch.Begin(); - // Draw the texture + // Draw the texture. SpriteBatch.Draw( _logo, // texture new Vector2( // position diff --git a/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/index.md b/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/index.md index 6d575134..25558dcf 100644 --- a/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/index.md +++ b/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/index.md @@ -34,7 +34,7 @@ These texture swaps, while negligible in this example, can become a performance ### Attempting to Optimize Draw Order -One approach to get around this could be to optimize the order of the draw calls to minimize texture swaps For example, if we reorder the draw calls from the previous example so that both paddles are drawn first and then the ball, the number of texture swaps is reduced from two to one: +One approach to get around this could be to optimize the order of the draw calls to minimize texture swaps. For example, if we reorder the draw calls from the previous example so that both paddles are drawn first and then the ball, the number of texture swaps is reduced from two to one: [!code-csharp[](./snippets/draw_order.cs)] @@ -162,7 +162,7 @@ These methods serve different purposes in managing the texture atlas: ## Using the TextureAtlas Class -No we can put our new `TextureAtlas` class to use by exploring two approaches; creating an atlas manually and using XML configuration. So far, we have been practicing using textures with the MonoGame logo. Now we will use a new texture atlas that contains various sprites we will need for our game. +Now we can put our new `TextureAtlas` class to use by exploring two approaches; creating an atlas manually and using XML configuration. So far, we have been practicing using textures with the MonoGame logo. Now we will use a new texture atlas that contains various sprites we will need for our game. Download the texture atlas by right-clicking the following image and saving it as atlas.png: @@ -204,7 +204,7 @@ The key changes in this implementation are: - Retrieved the regions using their names. 4. Updated [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) to: - Draw the slime at a scale factor of 4. - - Draw the bat 10 pixels to the right of the bat based on the slime's `Width` property, at a scale of 4 + - Draw the bat 10 pixels to the right of the slime based on the slime's `Width` property, at a scale of 4. Running the game now shows both sprites in the upper-left corner: diff --git a/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/snippets/textureregion.cs b/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/snippets/textureregion.cs index 407bb946..87a9c44a 100644 --- a/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/snippets/textureregion.cs +++ b/articles/tutorials/building_2d_games/07_optimizing_texture_rendering/snippets/textureregion.cs @@ -46,7 +46,7 @@ public TextureRegion() { } /// /// The texture to use as the source texture for this texture region. /// The x-coordinate position of the upper-left corner of this texture region relative to the upper-left corner of the source texture. - /// + /// The y-coordinate position of the upper-left corner of this texture region relative to the upper-left corner of the source texture. /// The width, in pixels, of this texture region. /// The height, in pixels, of this texture region. public TextureRegion(Texture2D texture, int x, int y, int width, int height) diff --git a/articles/tutorials/building_2d_games/08_the_sprite_class/index.md b/articles/tutorials/building_2d_games/08_the_sprite_class/index.md index 272ed59e..65452abe 100644 --- a/articles/tutorials/building_2d_games/08_the_sprite_class/index.md +++ b/articles/tutorials/building_2d_games/08_the_sprite_class/index.md @@ -1,6 +1,6 @@ --- title: "Chapter 08: The Sprite Class" -description: "Explore creating a reusable Sprite class to efficiently sprites and their rendering properties, including position, rotation, scale, and more." +description: "Explore creating a reusable Sprite class to efficiently manage sprites and their rendering properties, including position, rotation, scale, and more." --- In [Chapter 07](../07_optimizing_texture_rendering/index.md), you learned how to use texture atlases to optimize rendering performance. While this solved the issue of texture swapping, managing individual sprites and their properties becomes increasingly complex as your game grows. Even in our simple example with just a slime and a bat, we would eventually need to track various properties for each sprite: @@ -73,7 +73,7 @@ We can simplify this process by adding a sprite creation method to the `TextureA ## Using the Sprite Class -Now we can adjust our game now to use the `Sprite` class instead of just the texture regions. Update the contents of `Game1.cs` with the following: +Now we can adjust our game to use the `Sprite` class instead of just the texture regions. Update the contents of `Game1.cs` with the following: [!code-csharp[](./snippets/game1.cs?highlight=11-15,34-40,61-65)] @@ -92,7 +92,7 @@ Running the game now will produce the same result as in the previous chapter. | **Figure 8-1: The slime and bat sprites being rendered in the upper-left corner of the game window** | > [!NOTE] -> Notice how even though we increased the scale of both sprites, the bat sprite is still only 10px to the right of the bat. This is because the `Width` property we created for the `Sprite` class takes into account the scale factor of the sprite as well. +> Notice how even though we increased the scale of both sprites, the bat sprite is still only 10px to the right of the slime. This is because the `Width` property we created for the `Sprite` class takes into account the scale factor of the sprite as well. Try adjusting the various properties available for the slime and the bat sprites to see how they affect the rendering. diff --git a/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/game1.cs b/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/game1.cs index b2dff791..2e919611 100644 --- a/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/game1.cs @@ -28,7 +28,7 @@ protected override void Initialize() protected override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml"); // Create the slime sprite from the atlas. diff --git a/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/sprite.cs b/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/sprite.cs index 13e8bd04..5dd1ab59 100644 --- a/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/sprite.cs +++ b/articles/tutorials/building_2d_games/08_the_sprite_class/snippets/sprite.cs @@ -99,7 +99,7 @@ public Sprite(TextureRegion region) #region methods /// - /// Sets the origin of this sprite to the center + /// Sets the origin of this sprite to the center. /// public void CenterOrigin() { diff --git a/articles/tutorials/building_2d_games/09_the_animatedsprite_class/index.md b/articles/tutorials/building_2d_games/09_the_animatedsprite_class/index.md index caa011ce..3d951f39 100644 --- a/articles/tutorials/building_2d_games/09_the_animatedsprite_class/index.md +++ b/articles/tutorials/building_2d_games/09_the_animatedsprite_class/index.md @@ -53,7 +53,7 @@ Add the following constructors: ## Creating Animations With The TextureAtlas Class -The `TextureAtlas` class we created in [Chapter 07](../07_optimizing_texture_rendering/index.md#the-textureatlas-class) can do more than just manage texture regions and create sprites; it can also store and manage animation data to create animated sprites with. The `atlas.png` image we are currently using contains the frames of animation for both a slime and a bat, as well as sprites for other things. We will first update our `atlas-definition.xml` file to include all regions in the atlas, as well as add new `` elements to define the animations. +The `TextureAtlas` class we created in [Chapter 07](../07_optimizing_texture_rendering/index.md#the-textureatlas-class) can do more than just manage texture regions and create sprites; it can also store and manage animation data to create animated sprites. The `atlas.png` image we are currently using contains the frames of animation for both a slime and a bat, as well as sprites for other things. We will first update our `atlas-definition.xml` file to include all regions in the atlas, as well as add new `` elements to define the animations. Open the `atlas-definition.xml` file in your code editor and replace the contents with the following: @@ -123,7 +123,7 @@ The class uses three private fields to manage its animation state: - `_elapsed`: Keeps track of how much time has passed since the last frame change. - `_animation`: Stores the current animation being played. -The `Animation` property provides access to the current animation while ensuring the sprite always starts with the first frame when a new animation is set. When you assign a new animation, the property's setter automatically updates the sprite's region to display the first frame of that animation. +The `Animation` property provides access to the current animation and ensures the sprite always starts with the first frame when a new animation is set. When you assign a new animation, the property's setter automatically updates the sprite's region to display the first frame of that animation. > [!NOTE] > Starting with the first frame when setting a new animation ensures consistent behavior when switching between different animations. @@ -176,7 +176,7 @@ We can simplify this process by adding an animated sprite creation method to the ## Using the AnimatedSprite Class -We can now adjust our game now to use the `AnimatedSprite` class to see our sprites come to life. Update the contents of `Game1.cs` with the following: +We can now adjust our game to use the `AnimatedSprite` class to see our sprites come to life. Update the contents of `Game1.cs` with the following: [!code-csharp[](./snippets/game1.cs?highlight=11-15,34-40,48-52)] diff --git a/articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/game1.cs b/articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/game1.cs index a790893a..1f49ce33 100644 --- a/articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/09_the_animatedsprite_class/snippets/game1.cs @@ -28,7 +28,7 @@ protected override void Initialize() protected override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml"); // Create the slime animated sprite from the atlas. diff --git a/articles/tutorials/building_2d_games/10_handling_input/index.md b/articles/tutorials/building_2d_games/10_handling_input/index.md index 0f506383..f8ecc39e 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/index.md +++ b/articles/tutorials/building_2d_games/10_handling_input/index.md @@ -12,7 +12,7 @@ When you play a game, you need ways to control what is happening; using a keyboa Each of these input types has a `GetState` method that, when called, checks what is happening with that device at that moment. Think of it like taking a snapshot; when you call `GetState`, MonoGame looks at that exact moment to see which buttons are pressed, where the mouse is, or how the controller is being used. -In this chapter you will, we will learn how to use each of these dedicated input classes to handle player input. +In this chapter, you will learn how to use each of these dedicated input classes to handle player input. ## Keyboard Input @@ -45,7 +45,7 @@ The [**MouseState**](xref:Microsoft.Xna.Framework.Input.MouseState) struct conta | Property | Type | Description | |----------------------------------------------------------------------------------------|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| | [**LeftButton**](xref:Microsoft.Xna.Framework.Input.MouseState.LeftButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the left mouse button. | -| [**MiddleButton**](xref:Microsoft.Xna.Framework.Input.MouseState.MiddleButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the middle mouse button. This is often the button when pressing the scroll wheel down as a button | +| [**MiddleButton**](xref:Microsoft.Xna.Framework.Input.MouseState.MiddleButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the middle mouse button. This is often the button activated by pressing the scroll wheel down. | | [**Position**](xref:Microsoft.Xna.Framework.Input.MouseState.Position) | [**Point**](xref:Microsoft.Xna.Framework.Point) | Returns the position of the mouse cursor relative to the bounds of the game window. | | [**RightButton**](xref:Microsoft.Xna.Framework.Input.MouseState.RightButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the right mouse button. | | [**ScrollWheelValue**](xref:Microsoft.Xna.Framework.Input.MouseState.ScrollWheelValue) | `int` | Returns the **cumulative** scroll wheel value since the start of the game | @@ -164,7 +164,7 @@ The [**Triggers**](xref:Microsoft.Xna.Framework.Input.GamePadState.Triggers) pro | [**Left**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers.Left) | `float` | The value of the left trigger. | | [**Right**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers.Right) | `float` | The value of the left trigger. | -The trigger values are represented as a float value between `0.0f` (not pressed) to `1.0f` (fully pressed). The triggers on a gamepad, however, can be either *analog* or *digital* depending the gamepad manufacturer. For gamepads with *digital* triggers, the value will always be either `0.0f` or `1.0f`, as a digital trigger does not register values in between based on the amount of pressure applied to the trigger. +The trigger values are represented as a float value between `0.0f` (not pressed) to `1.0f` (fully pressed). The triggers on a gamepad, however, can be either *analog* or *digital* depending on the gamepad manufacturer. For gamepads with *digital* triggers, the value will always be either `0.0f` or `1.0f`, as a digital trigger does not register values in between based on the amount of pressure applied to the trigger. For example, if we were creating a racing game, the right trigger could be used for acceleration like the following: @@ -211,7 +211,7 @@ You can use the [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.G > [!CAUTION] > While you can use these methods to get the state of any of these button inputs, the state will only tell you if it is being pressed or released. For the actual thumbstick values and trigger values, you would need to use the properties instead. -For example, if we wanted to check if the A button on the the first gamepad is pressed, you could use the following: +For example, if we wanted to check if the A button on the first gamepad is pressed, you could use the following: [!code-csharp[](./snippets/isbuttondown.cs)] @@ -272,7 +272,7 @@ The state of a touch location progresses through the states typically in order o - [**Pressed**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State): Initial contact with the screen. - [**Moved**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) : Touch point moved while maintaining contact. - [**Released**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State): Contact with screen ended. -- [**Invalid**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) : Touch data is invalid (using when tracking data is lost). +- [**Invalid**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) : Touch data is invalid (used when tracking data is lost). ### GestureSample @@ -296,7 +296,7 @@ To determine what type of gesture is performed, we can get that from the [**Gest | [**DoubleTap**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user double tapped the device twice which is always preceded by a Tap gesture. | | [**DragComplete**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | States completion of a drag gesture (VerticalDrag, HorizontalDrag, or FreeDrag). | | [**Flick**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | States that a touch was combined with a quick swipe. | -| [**FreeDrag**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched a point and the performed a free-form drag. | +| [**FreeDrag**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched a point and then performed a free-form drag. | | [**Hold**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched a single point for approximately one second. | | [**HorizontalDrag**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched the screen and performed either a left-to-right or right-to-left drag gesture. | | [**None**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | No gesture. | @@ -343,7 +343,7 @@ The key changes made here are: 4. The `CheckGamePadInput` method was added which checks for input from the gamepad based on the input table above and moves the slime based the gamepad input detected. > [!NOTE] - > The gamepad implementation includes a priority system for directional input. The code prioritizes the analog thumbstick values over the digital DPad buttons. This design choice provides players with more nuanced control, as analog inputs allow for a variable movements speed based on how far the thumbstick is pushed, while DPad buttons only provide on/off input states. The code first checks if either thumbstick axis has a non-zero value, and only falls back to DPad input when the thumbstick is centered. + > The gamepad implementation includes a priority system for directional input. The code prioritizes the analog thumbstick values over the digital DPad buttons. This design choice provides players with more nuanced control, as analog inputs allow for a variable movement speed based on how far the thumbstick is pushed, while DPad buttons only provide on/off input states. The code first checks if either thumbstick axis has a non-zero value, and only falls back to DPad input when the thumbstick is centered. > > To enhance player experience, the gamepad implementation also includes gamepad vibration when the speed boost is activated. Haptic feedback like this creates a more immersive experience by engaging additional senses for the player beyond just visual and auditory feedback. @@ -357,7 +357,7 @@ Running the game now, you can move the slime around using the keyboard with the | **Figure 10-1: The slime moving around based on device input** | > [!NOTE] -> You may notice that the slime is capable of moving completely off the screen, this is completely normal as we have not yet implemented any logic to prevent it from doing so, it only doing what we currently tell it to do. +> You may notice that the slime is capable of moving completely off the screen, this is completely normal as we have not yet implemented any logic to prevent it from doing so, it is only doing what we currently tell it to do. ## Input Buffering diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/buttonstate.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/buttonstate.cs index c68d5132..fdf09a8b 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/buttonstate.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/buttonstate.cs @@ -1,4 +1,4 @@ -// Get the current state of player one's gamepad +// Get the current state of player one's gamepad. GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); // Check if the down on the DPad is pressed. diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/enablegestures.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/enablegestures.cs index 75b25268..bf9156dc 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/enablegestures.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/enablegestures.cs @@ -2,7 +2,7 @@ protected override void Initialize() { base.Initialize(); - // Enable gestures we want to handle + // Enable gestures we want to handle. TouchPanel.EnabledGestures = GestureType.Tap | GestureType.HorizontalDrag | diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/game1.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/game1.cs index a48e5d3d..51c2b8bf 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/game1.cs @@ -34,7 +34,7 @@ protected override void Initialize() protected override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml"); // Create the slime animated sprite from the atlas. diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/gamepadstate.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/gamepadstate.cs index ab65dc08..ae39aa2c 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/gamepadstate.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/gamepadstate.cs @@ -1,4 +1,4 @@ -// Get the current state of player one's gamepad +// Get the current state of player one's gamepad. GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); // Check if the A button is pressed down. diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/inputbuffer.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/inputbuffer.cs index dec0fa1f..7ddae06a 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/inputbuffer.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/inputbuffer.cs @@ -26,13 +26,13 @@ newDirection = Vector2.UnitX; } -// Only add if a valid direction and does not exceed the buffer size +// Only add if a valid direction and does not exceed the buffer size. if(newDirection != Vector2.Zero && _inputBuffer.Count < MAX_BUFFER_SIZE) { _inputBuffer.Enqueue(newDirection); } -// In movement update code +// In movement update code. if(_inputBuffer.COunt > 0) { Vector2 nextDirection = _inputBuffer.Dequeue(); diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/isbuttondown.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/isbuttondown.cs index 85526ae6..67f70919 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/isbuttondown.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/isbuttondown.cs @@ -1,4 +1,4 @@ -// Get the current state of player one's gamepad +// Get the current state of player one's gamepad. GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); // Check if the A button is down. diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/thumbstick.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/thumbstick.cs index d2525cac..ec12630d 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/thumbstick.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/thumbstick.cs @@ -1,10 +1,10 @@ -// Get the current state of player one's gamepad +// Get the current state of player one's gamepad. GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); // Get the value of the left thumbstick. Vector2 leftStick = gamePadState.Thumbsticks.Left; -// Invert the y-axis value +// Invert the y-axis value. leftStick.Y *= -1.0f; // Apply the value to the position of the sprite. diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/triggers.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/triggers.cs index fdde9d06..0b1fd169 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/triggers.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/triggers.cs @@ -1,4 +1,4 @@ -// Get the current state of player one's gamepad +// Get the current state of player one's gamepad. GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); // Get the acceleration based on how far the right trigger is pushed down. diff --git a/articles/tutorials/building_2d_games/10_handling_input/snippets/vibration.cs b/articles/tutorials/building_2d_games/10_handling_input/snippets/vibration.cs index 06805dec..395ae659 100644 --- a/articles/tutorials/building_2d_games/10_handling_input/snippets/vibration.cs +++ b/articles/tutorials/building_2d_games/10_handling_input/snippets/vibration.cs @@ -1,8 +1,8 @@ -// Make the gamepad vibrate at full intensity +// Make the gamepad vibrate at full intensity. GamePad.SetVibration(PlayerIndex.One, 1.0f, 1.0f); -// Stop all vibration +// Stop all vibration. GamePad.SetVibration(PlayerIndex.One, 0.0f, 0.0f); -// Create a subtle, low-intensity vibration +// Create a subtle, low-intensity vibration. GamePad.SetVibration(PlayerIndex.One, 0.3f, 0.1f); \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/11_input_management/index.md b/articles/tutorials/building_2d_games/11_input_management/index.md index 1820e7f4..c160c383 100644 --- a/articles/tutorials/building_2d_games/11_input_management/index.md +++ b/articles/tutorials/building_2d_games/11_input_management/index.md @@ -36,13 +36,13 @@ If both conditions are true, we know the key was just pressed. If we were to mo [!code-csharp[](./snippets/compare_previous_state.cs)] -If you need to know the inverse state, when the key was just released, then it is simply a matter of swiching the checking of the states, for example, is the key up this frame and was it down in the previous frame. +If you need to know the inverse state, when the key was just released, then it is simply a matter of switching the checking of the states, for example, is the key up this frame and was it down in the previous frame. This same concept applies to mouse buttons and gamepad input as well. Any time you need to detect a "just pressed" or "just released" state, you will need to compare the current input state with the previous frame's state. -So far, we have only been working with our game within the `Game1.cs` file. This has been fine for the examples given. Overtime, as the game grows, we are going to have a more complex system setup with different scenes, and each scene will need a way to track the state of input over time. We could do this by creating a lot of variables in each scene to track this information, or we can use object-oriented design concepts to create a reusable `InputManager` class to simplify this for us. +So far, we have only been working with our game within the `Game1.cs` file. This has been fine for the examples given. Over time, as the game grows, we are going to have a more complex system set up with different scenes, and each scene will need a way to track the state of input over time. We could do this by creating a lot of variables in each scene to track this information, or we can use object-oriented design concepts to create a reusable `InputManager` class to simplify this for us. -Before we create the `InputManager` class, we should first create classes for the keyboard, mouse, and gamepad that encapsulates the information about those inputs which will then be exposed through the `InputManager`. +Before we create the `InputManager` class, we should first create classes for the keyboard, mouse, and gamepad that encapsulates the information about those inputs, which will then be exposed through the `InputManager`. To get started, create a new folder called `Input` in the *MonoGameLibrary* project. We will put all of our input related classes here. @@ -245,7 +245,7 @@ To manage gamepad input effectively, we need to track both current and previous - Check if gamepad buttons are being held down. - Start and Stop vibration of a gamepad. -To get started, in the `Input` folder of the *MonoGameLibrary* project, create a new file name `GamePadInfo.cs` with the following initial structure: +To get started, in the `Input` folder of the *MonoGameLibrary* project, create a new file named `GamePadInfo.cs` with the following initial structure: [!code-csharp[](./snippets/gamepadinfo.cs#declaration)] @@ -343,7 +343,7 @@ The `InputManager` class needs properties to access each type of input device. A ### InputManager Constructor -The constructor for the `InputManager` initializes the keybaord, mouse, and gamepad states. +The constructor for the `InputManager` initializes the keyboard, mouse, and gamepad states. Add the following constructor: @@ -351,7 +351,7 @@ Add the following constructor: ### InputManager Methods -The `Update` method for the `InputManager` calls update for each device so that they can update their internal states. +The `Update` method for the `InputManager` calls update for each device, so that they can update their internal states. [!code-csharp[](./snippets/inputmanager.cs#methods)] diff --git a/articles/tutorials/building_2d_games/11_input_management/snippets/core.cs b/articles/tutorials/building_2d_games/11_input_management/snippets/core.cs index 21457ac5..1fc370a6 100644 --- a/articles/tutorials/building_2d_games/11_input_management/snippets/core.cs +++ b/articles/tutorials/building_2d_games/11_input_management/snippets/core.cs @@ -67,25 +67,25 @@ public Core(string title, int width, int height, bool fullScreen) // Create a new graphics device manager. Graphics = new GraphicsDeviceManager(this); - // Set the graphics defaults + // Set the graphics defaults. Graphics.PreferredBackBufferWidth = width; Graphics.PreferredBackBufferHeight = height; Graphics.IsFullScreen = fullScreen; - // Apply the graphic presentation changes + // Apply the graphic presentation changes. Graphics.ApplyChanges(); - // Set the window title + // Set the window title. Window.Title = title; // Set the core's content manager to a reference of hte base Game's // content manager. Content = base.Content; - // Set the root directory for content + // Set the root directory for content. Content.RootDirectory = "Content"; - // Mouse is visible by default + // Mouse is visible by default. IsMouseVisible = true; } @@ -100,13 +100,13 @@ protected override void Initialize() // Create the sprite batch instance. SpriteBatch = new SpriteBatch(GraphicsDevice); - // Create a new input manager + // Create a new input manager. Input = new InputManager(); } protected override void Update(GameTime gameTime) { - // Update the input manager + // Update the input manager. Input.Update(gameTime); if (ExitOnEscape && Input.Keyboard.IsKeyDown(Keys.Escape)) diff --git a/articles/tutorials/building_2d_games/11_input_management/snippets/game1.cs b/articles/tutorials/building_2d_games/11_input_management/snippets/game1.cs index ab380346..2906fb24 100644 --- a/articles/tutorials/building_2d_games/11_input_management/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/11_input_management/snippets/game1.cs @@ -36,7 +36,7 @@ protected override void Initialize() protected override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml"); // Create the slime animated sprite from the atlas. diff --git a/articles/tutorials/building_2d_games/11_input_management/snippets/gamepadinfo.cs b/articles/tutorials/building_2d_games/11_input_management/snippets/gamepadinfo.cs index 5999999f..b59a02ad 100644 --- a/articles/tutorials/building_2d_games/11_input_management/snippets/gamepadinfo.cs +++ b/articles/tutorials/building_2d_games/11_input_management/snippets/gamepadinfo.cs @@ -122,7 +122,7 @@ public bool IsButtonUp(Buttons button) /// /// Returns a value that indicates whether the specified gamepad button was just pressed on the current frame. /// - /// + /// The gamepad button to check. /// true if the specified gamepad button was just pressed on the current frame; otherwise, false. public bool WasButtonJustPressed(Buttons button) { @@ -132,7 +132,7 @@ public bool WasButtonJustPressed(Buttons button) /// /// Returns a value that indicates whether the specified gamepad button was just released on the current frame. /// - /// + /// The gamepad button to check. /// true if the specified gamepad button was just released on the current frame; otherwise, false. public bool WasButtonJustReleased(Buttons button) { diff --git a/articles/tutorials/building_2d_games/11_input_management/snippets/inputmanager.cs b/articles/tutorials/building_2d_games/11_input_management/snippets/inputmanager.cs index 68d765e4..395c09f5 100644 --- a/articles/tutorials/building_2d_games/11_input_management/snippets/inputmanager.cs +++ b/articles/tutorials/building_2d_games/11_input_management/snippets/inputmanager.cs @@ -28,7 +28,6 @@ public class InputManager { } /// /// Creates a new InputManager. /// - /// The game this input manager belongs to. public InputManager() { Keyboard = new KeyboardInfo(); diff --git a/articles/tutorials/building_2d_games/11_input_management/snippets/keyboardinfo.cs b/articles/tutorials/building_2d_games/11_input_management/snippets/keyboardinfo.cs index 4d6e2518..3144e52b 100644 --- a/articles/tutorials/building_2d_games/11_input_management/snippets/keyboardinfo.cs +++ b/articles/tutorials/building_2d_games/11_input_management/snippets/keyboardinfo.cs @@ -20,7 +20,7 @@ public class KeyboardInfo { } #region ctors /// - /// Creates a new KeyboardInfo + /// Creates a new KeyboardInfo. /// public KeyboardInfo() { diff --git a/articles/tutorials/building_2d_games/11_input_management/snippets/mouseinfo.cs b/articles/tutorials/building_2d_games/11_input_management/snippets/mouseinfo.cs index a9f11cf3..7d9afbb6 100644 --- a/articles/tutorials/building_2d_games/11_input_management/snippets/mouseinfo.cs +++ b/articles/tutorials/building_2d_games/11_input_management/snippets/mouseinfo.cs @@ -184,7 +184,7 @@ public bool WasButtonJustPressed(MouseButton button) /// Returns a value that indicates whether the specified mouse button was just released on the current frame. /// /// The mouse button to check. - /// true if the specified mouse button was just released on the current frame; otherwise, false.F + /// true if the specified mouse button was just released on the current frame; otherwise, false. public bool WasButtonJustReleased(MouseButton button) { switch (button) diff --git a/articles/tutorials/building_2d_games/12_collision_detection/index.md b/articles/tutorials/building_2d_games/12_collision_detection/index.md index 8e728469..266d51bb 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/index.md +++ b/articles/tutorials/building_2d_games/12_collision_detection/index.md @@ -16,7 +16,7 @@ In this chapter you will: We will first start by understanding the basics of collision detection and the different approaches that can be used. > [!NOTE] -> There is a lot to understand when it comes to collision detection and the many complex ways that two objects can be considered IN collision or NEAR collision. It is critical to get an understanding of the basics before jumping into code. So buckle up, we have a story to tell before you can get back to the keyboard. +> There is a lot to understand when it comes to collision detection and the many complex ways that two objects can be considered IN collision or NEAR collision. It is critical to get an understanding of the basics before jumping into code. So buckle up, we have a story to tell before you can get back to the keyboard. > > Feel free to keep coming back to this chapter and refer to the content when you need to, with a fresh cup of coffee. @@ -26,20 +26,20 @@ Before we start implementing collision detection, we should discuss what collisi ### Proximity Collision Detection -The simplest form is checking if objects are within a certain range of each other. This is useful when you only need to know if objects are "near" each other like detecting if an enemy is close enough to chase a player or if two objects are close enough to perform a more complex collision check. +The simplest form is checking if objects are within a certain range of each other. This is useful when you only need to know if objects are "near" each other like detecting if an enemy is close enough to chase a player or if two objects are close enough to perform a more complex collision check. ### Simple Shape Based Collision Detection -Shaped based collision detection checks if two shapes overlap. The most common and simple shapes used are circles and rectangles: +Shaped based collision detection checks if two shapes overlap. The most common and simple shapes used are circles and rectangles: #### Circle Collision Detection -Circle collision detection is computationally a simpler check than rectangles. There are also no special considerations if the circles are rotated, which makes them easier to use. To determine if two circle shapes are overlapping, we only need to check if the square of the sum of the radii between the two circles is less than the squared distance between the two circles with the following formula: +Circle collision detection is computationally a simpler check than rectangles. There are also no special considerations if the circles are rotated, which makes them easier to use. To determine if two circle shapes are overlapping, we only need to check if the square of the sum of the radii between the two circles is less than the squared distance between the two circles with the following formula: -To find the distance between two circles, imagine drawing a line from the center of one circle to the center of the other. The length of this line is the distance, but we could also calculate it by first walking up or down and then walking left or right from the center of one circle to another, forming a right triangle. +To find the distance between two circles, imagine drawing a line from the center of one circle to the center of the other. The length of this line is the distance, but we could also calculate it by first walking up or down and then walking left or right from the center of one circle to another, forming a right triangle. | ![Figure 12-1: Showing the distance between the center of two circles forms a right triangle](./images/circle-distance-right-triangle.svg) | -| :---------------------------------------------------------------------------------------------------------------------------------------: | +| :----------------------------------------------------------------------------------------------------------------------------------------: | | **Figure 12-1: Showing the distance between the center of two circles forms a right triangle** | In *Figure 12-1* above @@ -58,33 +58,33 @@ $$(radius_{circle1} + radius_{circle2})^2 > a^2 + b^2$$ It is easy to confuse the direction of the inequality sign. As a quick mental test, think of how the math works when the origin of two circles are at the same position, i.e., when the *squared distance* is zero. -To calculate the squared distance between two points, MonoGame provides the [**Vector2.DistanceSquared**](xref:Microsoft.Xna.Framework.Vector2.DistanceSquared(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) method: +To calculate the squared distance between two points, MonoGame provides the [**Vector2.DistanceSquared**]() method: [!code-csharp[](./snippets/vector2_distance.cs)] > [!TIP] -> MonoGame also provides a distance calculation method with [**Vector2.Distance**](xref:Microsoft.Xna.Framework.Vector2.Distance(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) which returns the distance by providing the square root of the distance squared. So why not use this instead? +> MonoGame also provides a distance calculation method with [**Vector2.Distance**]() which returns the distance by providing the square root of the distance squared. So why not use this instead? > -> Square root operations are more computationally complex for a CPU. So instead of getting the normal distance, which would require the square root operation, it is more efficient for the cpu to multiply the sum of the radii by itself to get the squared sum and use that for comparison instead. +> Square root operations are more computationally complex for a CPU. So instead of getting the normal distance, which would require the square root operation, it is more efficient for the cpu to multiply the sum of the radii by itself to get the squared sum and use that for comparison instead. #### Rectangle Collision Detection -Rectangles, often called *bounding boxes*, typically uses what is called *Axis-Aligned Bounding Box* (AABB) collision detection to determine if two rectangle shapes overlap. Unlike circles, to perform AABB collision detection, the x- and y-axes of both rectangles must be aligned with the x- and y-axes of the screen. This is just another way of saying that the rectangles cannot be rotated. +Rectangles, often called *bounding boxes*, typically uses what is called *Axis-Aligned Bounding Box* (AABB) collision detection to determine if two rectangle shapes overlap. Unlike circles, to perform AABB collision detection, the x- and y-axes of both rectangles must be aligned with the x- and y-axes of the screen. This is just another way of saying that the rectangles cannot be rotated. | ![Figure 12-2: The rectangle on the left is axis-aligned since both the axes are aligned with the screen axes. The rectangle on the right is non axis-aligned since it is rotated and the axes do not align with the screen axe.](./images/aabb-vs-non-aabb.svg) | -| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | **Figure 12-2: The rectangle on the left is axis-aligned since both the axes are aligned with the screen axes. The rectangle on the right is non axis-aligned since it is rotated and the axes do not align with the screen axes** | MonoGame provides the [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) struct which represents a rectangle by its position (X,Y) and size (Width,Height). The following table shows some of the properties of the [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) struct: -| Property | Type | Description | -| ----------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [**Bottom**](xref:Microsoft.Xna.Framework.Rectangle.Bottom) | `int` | Returns the y-coordinate location of the bottom edge of the rectangle. This is equal to [**Rectangle.Y**](xref:Microsoft.Xna.Framework.Rectangle.Y) plus the height of the rectangle. | -| [**Left**](xref:Microsoft.Xna.Framework.Rectangle.Left) | `int` | Returns the x-coordinate location of the left edge of the rectangle. This is equal to [**Rectangle.X**](xref:Microsoft.Xna.Framework.Rectangle.X). | -| [**Right**](xref:Microsoft.Xna.Framework.Rectangle.Right) | `int` | Returns the x-coordinate location of the right edge of the rectangle. This is equal to [**Rectangle.X**](xref:Microsoft.Xna.Framework.Rectangle.X) plus the width of the rectangle. | -| [**Top**](xref:Microsoft.Xna.Framework.Rectangle.Top) | `int` | Returns the y-coordinate location of the top edge of the rectangle. This is equal to [**Rectangle.Y**](xref:Microsoft.Xna.Framework.Rectangle.Y). | +| Property | Type | Description | +| ----------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [**Bottom**](xref:Microsoft.Xna.Framework.Rectangle.Bottom) | `int` | Returns the y-coordinate location of the bottom edge of the rectangle. This is equal to [**Rectangle.Y**](xref:Microsoft.Xna.Framework.Rectangle.Y) plus the height of the rectangle. | +| [**Left**](xref:Microsoft.Xna.Framework.Rectangle.Left) | `int` | Returns the x-coordinate location of the left edge of the rectangle. This is equal to [**Rectangle.X**](xref:Microsoft.Xna.Framework.Rectangle.X). | +| [**Right**](xref:Microsoft.Xna.Framework.Rectangle.Right) | `int` | Returns the x-coordinate location of the right edge of the rectangle. This is equal to [**Rectangle.X**](xref:Microsoft.Xna.Framework.Rectangle.X) plus the width of the rectangle. | +| [**Top**](xref:Microsoft.Xna.Framework.Rectangle.Top) | `int` | Returns the y-coordinate location of the top edge of the rectangle. This is equal to [**Rectangle.Y**](xref:Microsoft.Xna.Framework.Rectangle.Y). | -To determine if two rectangles overlap using AABB collision detection, there are four conditions that need to be checked, and all four conditions must be true. Given two rectangles $A$ and $B$, these conditions are: +To determine if two rectangles overlap using AABB collision detection, there are four conditions that need to be checked, and all four conditions must be true. Given two rectangles $A$ and $B$, these conditions are: 1. $A_{Left}$ must be less than $B_{Right}$. 2. $A_{Right}$ must be greater than $B_{Left}$. @@ -93,7 +93,7 @@ To determine if two rectangles overlap using AABB collision detection, there are If even a single one of these conditions is false, then the rectangles are not overlapping and thus not colliding. -MonoGame provides the [**Rectangle.Intersects**](xref:Microsoft.Xna.Framework.Rectangle.Intersects(Microsoft.Xna.Framework.Rectangle)) method which will perform an AABB collision check for us: +MonoGame provides the [**Rectangle.Intersects**]() method which will perform an AABB collision check for us: [!code-csharp[](./snippets/rectangle_intersects.cs)] @@ -103,7 +103,7 @@ MonoGame provides the [**Rectangle.Intersects**](xref:Microsoft.Xna.Framework.Re #### Complex Polygon Collision Detection -Complex polygon collision detection uses a method called *Separating Axis Theorem* (SAT) to determine if two polygon shapes overlap. SAT uses more complex calculations that can determine if any polygon shape overlaps with another polygon shape, including if they are rotated. There are performance considerations to consider when using SAT. +Complex polygon collision detection uses a method called *Separating Axis Theorem* (SAT) to determine if two polygon shapes overlap. SAT uses more complex calculations that can determine if any polygon shape overlaps with another polygon shape, including if they are rotated. There are performance considerations to consider when using SAT. Implementing SAT is out-of-scope for this tutorial. If you are interested in further reading about this, please see the following articles as a good starting point: @@ -113,7 +113,7 @@ Implementing SAT is out-of-scope for this tutorial. If you are interested in fur #### Choosing a Collision Detection Method -When determining which collision detection method to use, you should start with the simplest one that meets the needs of your game. If distance checks work for your game mechanic, there's no need to implement more complex shape based detections. Similarly, if a circle can represent the bounding area of a game object, start with that before moving on to rectangles. +When determining which collision detection method to use, you should start with the simplest one that meets the needs of your game. If distance checks work for your game mechanic, there's no need to implement more complex shape based detections. Similarly, if a circle can represent the bounding area of a game object, start with that before moving on to rectangles. Some other points to consider are @@ -128,13 +128,13 @@ Some other points to consider are ### Collision Detection vs Collision Response -Often times when talking about collision detection, the term is used to mean both the detection of overlapping shapes and what to do once a positive check has occurred. What you do after a positive collision check has occurred is called the *collision response*. Some of the common responses are: +Often times when talking about collision detection, the term is used to mean both the detection of overlapping shapes and what to do once a positive check has occurred. What you do after a positive collision check has occurred is called the *collision response*. Some of the common responses are: #### Blocking Collision Response -A blocking collision response is the most basic response which just prevents the two objects from overlapping. This is commonly used for walls, platforms and other solid objects. To perform a blocking collision response: +A blocking collision response is the most basic response which just prevents the two objects from overlapping. This is commonly used for walls, platforms and other solid objects. To perform a blocking collision response: -1. Store the location of an object calculating the new location to move it to. +1. Store the object’s current location and calculate the new position it should move to. 2. Check if it is overlapping an object at the new location: - If it is overlapping, then set the position to the position before it was moved. @@ -144,7 +144,7 @@ For example: [!code-csharp[](./snippets/blocking_example.cs)] -Sometimes, instead of preventing an object from moving onto another object, we want to ensure an object remains contained within a certain bounding area. MonoGame also provides the [**Rectangle.Contains**](xref:Microsoft.Xna.Framework.Rectangle.Contains(Microsoft.Xna.Framework.Rectangle)) method that we can use to determine this. [**Rectangle.Contains**](xref:Microsoft.Xna.Framework.Rectangle.Contains(Microsoft.Xna.Framework.Rectangle)) can check if any of the following are completely contained within the bounds of the rectangle; +Sometimes, instead of preventing an object from moving onto another object, we want to ensure an object remains contained within a certain bounding area. MonoGame also provides the [**Rectangle.Contains**]() method that we can use to determine this. [**Rectangle.Contains**]() can check if any of the following are completely contained within the bounds of the rectangle; - [**Point**](xref:Microsoft.Xna.Framework.Point) - [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) @@ -155,11 +155,11 @@ For example, if we wanted to perform a blocking collision response that ensure a [!code-csharp[](./snippets/contains_example.cs)] > [!TIP] -> Use [**GraphicsDevice.PresentationParameters**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice.PresentationParameters) to get the actual screen dimensions instead of [**GraphicsDeviceManager.PreferredBackBufferWidth**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferWidth) and [**GraphicsDeviceManager.PreferredBackBufferHeight**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferHeight). The preferred values are only hints and may not reflect the actual back buffer size. +> Use [**GraphicsDevice.PresentationParameters**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice.PresentationParameters) to get the actual screen dimensions instead of [**GraphicsDeviceManager.PreferredBackBufferWidth**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferWidth) and [**GraphicsDeviceManager.PreferredBackBufferHeight**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager.PreferredBackBufferHeight). The preferred values are only hints and may not reflect the actual back buffer size. #### Trigger Collision Response -Sometimes you want to trigger an event, rather than block movement, when a collision occurs. Common examples include +Sometimes you want to trigger an event, rather than block movement, when a collision occurs. Common examples include: - Collecting items. - Activating switches. @@ -174,9 +174,9 @@ For example: #### Bounce Collision Response -For games that need objects to bounce off each other (like the ball in a Pong game), we need to calculate how their velocity should change after the collision. MonoGame provides the [**Vector2.Reflect**](xref:Microsoft.Xna.Framework.Vector2.Reflect(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) method to handle this calculation for us. The method needs two pieces of information: +For games that need objects to bounce off each other (like the ball in a Pong game), we need to calculate how their velocity should change after the collision. MonoGame provides the [**Vector2.Reflect**]() method to handle this calculation for us. The method needs two pieces of information: -1. The incoming vector (the direction that something is moving). +1. The incoming vector (the direction the object is moving in before the collision). 2. The normal vector (the direction perpendicular to the surface). | ![Figure 12-4: A diagram showing how an incoming vector reflects off of a surface base around the normal vector of the surface](./images/reflection-diagram.svg) | @@ -192,12 +192,11 @@ For example, if we had a ball moving around the screen and wanted it to bounce o [!code-csharp[](./snippets/bounce_example.cs)] -> [!TIP] -> [**Vector2.UnitX**](xref:Microsoft.Xna.Framework.Vector2.UnitX) is $(1, 0)$ and [**Vector2.UnitY**](xref:Microsoft.Xna.Framework.Vector2.UnitY) is $(0, 1)$. We use these to get the screen edge normal since the edges of the screen are not at an angle. For more complex surfaces, you would need to calculate the appropriate normal vector based on the surface angle. +> [!TIP] > [**Vector2.UnitX**](xref:Microsoft.Xna.Framework.Vector2.UnitX) is $(1, 0)$ and [**Vector2.UnitY**](xref:Microsoft.Xna.Framework.Vector2.UnitY) is $(0, 1)$. We use these to get the screen edge normal since the edges of the screen are not at an angle. For more complex surfaces, you would need to calculate the appropriate normal vector based on the surface angle. ### Optimizing Collision Performance -When checking for collisions between multiple objects, testing every object against every other object (often called brute force checking) becomes inefficient as your game grows. Brute force checking can be calculated as $(n * (n - 1)) / 2$ where $n$ is the total number of objects. For example, if you have 100 objects in your game, that's $(100 * 99) / 2 = 4950$ collision checks every frame. To improve performance, we can use a two-phase approach: +When checking for collisions between multiple objects, testing every object against every other object (often called brute force checking) becomes inefficient as your game grows. Brute force checking can be calculated as $(n * (n - 1)) / 2$ where $n$ is the total number of objects. For example, if you have 100 objects in your game, that's $(100 * 99) / 2 = 4950$ collision checks every frame. To improve performance, we can use a two-phase approach: 1. Broad Phase: A quick, simple check to rule out objects that definitely are not colliding. 2. Narrow Phase: A more precise check that is only performed on objects that have passed the broad phase. @@ -205,18 +204,18 @@ When checking for collisions between multiple objects, testing every object agai For our simple game with just two objects, this optimization is not necessary. However, as you develop more complex games, implementing a broad-phase check can significantly improve performance. > [!NOTE] -> Time to get back to the code! The fun starts again here. +> Time to get back to the code! The fun starts again here. ## The Circle Struct -For our game, we are going to implement circle based collision detection. MonoGame does not have a `Circle` struct to represent a circle like it does with [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle). Before we can perform circle collision, we will need to create our own. +For our game, we are going to implement circle based collision detection. MonoGame does not have a `Circle` struct to represent a circle like it does with [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle). Before we can perform circle collision, we will need to create our own. -In the root of the *MonoGameLibrary* project, add a new file named `Circle.cs`. Add the following code as the foundation of the `Circle` struct: +In the root of the *MonoGameLibrary* project, add a new file named `Circle.cs`. Add the following code as the foundation of the `Circle` struct: [!code-csharp[](./snippets/cirlce.cs#declaration)] > [!NOTE] -> Notice that the struct has declared it will implement the [`IEquatable`](https://learn.microsoft.com/en-us/dotnet/api/system.iequatable-1) Interface. When creating value types like this, it is recommended to implement `IEquatable` because it has better performance for comparing objects and can help avoid boxing. +> Notice that the struct has declared it will implement the [`IEquatable`](https://learn.microsoft.com/en-us/dotnet/api/system.iequatable-1) interface. When creating value types like this, it is recommended to implement `IEquatable` because it has better performance for comparing objects and can help avoid boxing. > > For more information on recommended design guidelines for structs, see [Struct Design - Framework Design Guidelines | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/struct) > @@ -267,8 +266,8 @@ The `Circle` struct provides two ways to create a new circle: [!code-csharp[](./snippets/cirlce.cs#ctors)] -* The first constructor accepts individual x and y coordinates for the circle's center. -* The second accepts a [**Point**](xref:Microsoft.Xna.Framework.Point) struct that combines both coordinates. +- The first constructor accepts individual x and y coordinates for the circle's center. +- The second accepts a [**Point**](xref:Microsoft.Xna.Framework.Point) struct that combines both coordinates. Both constructors require a radius value that defines the circle's size. @@ -288,7 +287,7 @@ Next, add the following override for `GetHashCode` to support using circles in h [!code-csharp[](./snippets/cirlce.cs#methods_hashcode)] -Finally, add the following operator overloads to support using == and != with circles: +Finally, add the following operator overloads to support using == and != with circles: [!code-csharp[](./snippets/cirlce.cs#methods_operators)] @@ -309,7 +308,7 @@ If you run the game right now and move the slime around, you will notice a few i 2. Nothing occurs when the slime collides with the bat. 3. The bat does not move, providing no challenge in the game. -We can now implement these features using collision detection and response in our game. In the *DungeonSlime* project (your main game project), open the `Game1.cs` file and make the following changes to the `Game1` class: +We can now implement these features using collision detection and response in our game. In the *DungeonSlime* project (your main game project), open the `Game1.cs` file and make the following changes to the `Game1` class: [!code-csharp[](./snippets/game1.cs?highlight=1,5,25-29,40-45,79-179,184-196,296-297)] @@ -319,15 +318,15 @@ The key changes made here are: 2. The field `_batVelocity` was added to track the velocity of the bat. 3. The `AssignRandomBatVelocity()` method was added which calculates a random x and y velocity for the bat to move at when called. 4. In [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize), the initial position of the bat is set and `AssignRandomVelocity` is called to assign the initial velocity for the bat. -5. In [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), collision detection and response logic was added to perform the following in order: - 1. A [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) bound is created to represent the bounds of the screen. - 2. A `Circle` bound is created to represent the bounds of the slime. - 3. Distance based checks are performed to ensure that the slime cannot move outside of the screen, the resolution of which is to perform a blocking response. - 4. A new position for the bat is calculated based on the current velocity of the bat. - 5. A `Circle` bound is created to represent the bounds of the bat. - 6. Distance based checks are performed to ensure the bat cannot move outside of the screen, the resolution of which is to perform a bounce response. - 7. A collision check is made to determine if the slime and bat are colliding (bat "eating" the slime). If so, the bat is assigned a new random position within the screen and assigned a new random velocity. -6. In [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)), the bat is now drawn using the `_batPosition` value. +5. In [**Update**](), collision detection and response logic was added to perform the following in order: + 1. A [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) bound is created to represent the bounds of the screen. + 2. A `Circle` bound is created to represent the bounds of the slime. + 3. Distance based checks are performed to ensure that the slime cannot move outside of the screen, the resolution of which is to perform a blocking response. + 4. A new position for the bat is calculated based on the current velocity of the bat. + 5. A `Circle` bound is created to represent the bounds of the bat. + 6. Distance based checks are performed to ensure the bat cannot move outside of the screen, the resolution of which is to perform a bounce response. + 7. A collision check is made to determine if the slime and bat are colliding (bat "eating" the slime). If so, the bat is assigned a new random position within the screen and assigned a new random velocity. +6. In [**Draw**](), the bat is now drawn using the `_batPosition` value. Running the game now @@ -368,52 +367,52 @@ In the next chapter, we will explore using tilesets and tilemaps to create tile 1. What is the difference between collision detection and collision response? - ::: question-answer - Collision detection is determining when two objects overlap or intersect, while collision response is what happens after a collision is detected (like blocking movement, triggering events, or bouncing objects off each other). - ::: + ::: question-answer + Collision detection is determining when two objects overlap or intersect, while collision response is what happens after a collision is detected (like blocking movement, triggering events, or bouncing objects off each other). + ::: 2. When using Rectangle.Intersects for AABB collision, what four conditions must all be true for a collision to occur? - ::: question-answer - For two rectangles A and B to collide: + ::: question-answer + For two rectangles A and B to collide: - 1. A's left edge must be less than B's right edge. - 2. A's right edge must be greater than B's left edge. - 3. A's top edge must be less than B's bottom edge. - 4. A's bottom edge must be greater than B's top edge. + 1. A's left edge must be less than B's right edge. + 2. A's right edge must be greater than B's left edge. + 3. A's top edge must be less than B's bottom edge. + 4. A's bottom edge must be greater than B's top edge. ::: 3. When implementing circle collision, why do we compare the distance between centers to the sum of the radii? - ::: question-answer - Two circles are colliding if the distance between their centers is less than the sum of their radii. If the distance is greater, they are separate. If the distance equals the sum of radii, they are just touching at one point. - ::: + ::: question-answer + Two circles are colliding if the distance between their centers is less than the sum of their radii. If the distance is greater, they are separate. If the distance equals the sum of radii, they are just touching at one point. + ::: -4. When implementing bounce collision response, what two pieces of information does [**Vector2.Reflect**](xref:Microsoft.Xna.Framework.Vector2.Reflect(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) need? +4. When implementing bounce collision response, what two pieces of information does [**Vector2.Reflect**]() need? - ::: question-answer - [**Vector2.Reflect**](xref:Microsoft.Xna.Framework.Vector2.Reflect(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) needs: + ::: question-answer + [**Vector2.Reflect**]() needs: - 1. The incoming vector (direction the object is moving). - 2. The normal vector (direction perpendicular to the surface being hit). + 1. The incoming vector (direction the object is moving). + 2. The normal vector (direction perpendicular to the surface being hit). - ::: + ::: 5. Why might you choose to use circle collision over rectangle collision for certain objects? - ::: question-answer - Circle collision might be chosen because: + ::: question-answer + Circle collision might be chosen because: - - It is more accurate for round objects. - - It handles rotating objects better. - - It is simpler for continuous collision detection. - - It is natural for radius-based interactions. + - It is more accurate for round objects. + - It handles rotating objects better. + - It is simpler for continuous collision detection. + - It is natural for radius-based interactions. - ::: + ::: 6. In the blocking collision response example, why do we store the previous position before handling input? - ::: question-answer - We store the previous position so that if a collision occurs after movement, we can reset the object back to its last valid position. This prevents objects from moving through each other by undoing any movement that would cause overlap. - ::: + ::: question-answer + We store the previous position so that if a collision occurs after movement, we can reset the object back to its last valid position. This prevents objects from moving through each other by undoing any movement that would cause overlap. + ::: diff --git a/articles/tutorials/building_2d_games/12_collision_detection/snippets/blocking_example.cs b/articles/tutorials/building_2d_games/12_collision_detection/snippets/blocking_example.cs index 0716995f..181f2672 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/snippets/blocking_example.cs +++ b/articles/tutorials/building_2d_games/12_collision_detection/snippets/blocking_example.cs @@ -1,10 +1,10 @@ -// Store the current location +// Store the current location. Vector2 previousLocation = _spriteLocation; -// Calculate a new location +// Calculate a new location. Vector2 newLocation = _spriteLocation + new Vector2(10, 0); -// Create a bounding box for the sprite object +// Create a bounding box for the sprite object. Rectangle spriteBounds = new Rectangle( (int)newLocation.X, (int)newLocation.Y, @@ -12,7 +12,7 @@ (int)_sprite.Height ); -// Create a bounding box for the blocking object +// Create a bounding box for the blocking object. Rectangle blockingBounds = new Rectangle( (int)_blockingLocation_.X, (int)_blockingLocation_.Y, diff --git a/articles/tutorials/building_2d_games/12_collision_detection/snippets/bounce_example.cs b/articles/tutorials/building_2d_games/12_collision_detection/snippets/bounce_example.cs index b839521d..9ee8a448 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/snippets/bounce_example.cs +++ b/articles/tutorials/building_2d_games/12_collision_detection/snippets/bounce_example.cs @@ -1,7 +1,7 @@ -// Calculate the new position of the ball based on the velocity +// Calculate the new position of the ball based on the velocity. Vector2 newPosition = _ballPosition + _ballVelocity; -// Get the bounds of the ball as a rectangle +// Get the bounds of the ball as a rectangle. Rectangle ballBounds = new Rectangle( (int)_ballPosition.X, (int)_ballPosition.Y, @@ -9,7 +9,7 @@ (int)_ball.Height ); -// Get the bounds of the screen as a rectangle +// Get the bounds of the screen as a rectangle. Rectangle screenBounds = new Rectangle( 0, 0, @@ -17,7 +17,7 @@ GraphicsDevice.PresentationParameters.BackBufferHeight ); -// Detect if the ball object is within the screen bounds +// Detect if the ball object is within the screen bounds. if(!screenBounds.Contains(ballBounds)) { // Ball would move outside the screen @@ -27,42 +27,42 @@ float distanceTop = Math.Abs(screenBounds.Top - ballBounds.Top); float distanceBottom = Math.Abs(screenBounds.Bottom - ballBounds.Bottom); - // Determine which screen edge is the closest + // Determine which screen edge is the closest. float minDistance = Math.Min( Math.Min(distanceLeft, distanceRight), Math.Min(distanceTop, distanceBottom) ); - // Determine the normal vector based on which screen edge is the closest + // Determine the normal vector based on which screen edge is the closest. Vector2 normal; if (minDistance == distanceLeft) { - // Closest to the left edge + // Closest to the left edge. normal = Vector2.UnitX; newPosition.X = 0; } else if (minDistance == distanceRight) { - // Closest to the right edge + // Closest to the right edge. normal = -Vector2.UnitX; newPosition.X = screenBounds.Right - _ball.Width; } else if (minDistance == distanceTop) { - // Closest to the top edge + // Closest to the top edge. normal = Vector2.UnitY; newPosition.Y = 0; } else { - // Closest to the bottom edge + // Closest to the bottom edge. normal = -Vector2.UnitY; newPosition.Y = screenBounds.Bottom - _ball.Height; } - // Reflect the velocity about the normal + // Reflect the velocity about the normal. _ballVelocity = Vector2.Reflect(_ballVelocity, normal); } -// Set the new position of the ball +// Set the new position of the ball. _ballVelocity = newPosition; \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/12_collision_detection/snippets/contains_example.cs b/articles/tutorials/building_2d_games/12_collision_detection/snippets/contains_example.cs index 9bec3770..7b19c24b 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/snippets/contains_example.cs +++ b/articles/tutorials/building_2d_games/12_collision_detection/snippets/contains_example.cs @@ -23,7 +23,7 @@ // Detect if the sprite is contained within the bounds of the screen if(!screenBounds.Contains(spriteBounds)) { - // Respond by not allowing the sprite to move to move outside the screen + // Respond by not allowing the sprite to move outside the screen // bounds by setting the location back to the previous location. newLocation = previousLocation; } diff --git a/articles/tutorials/building_2d_games/12_collision_detection/snippets/game1.cs b/articles/tutorials/building_2d_games/12_collision_detection/snippets/game1.cs index 7a85b431..87ff04b3 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/12_collision_detection/snippets/game1.cs @@ -47,7 +47,7 @@ protected override void Initialize() protected override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml"); // Create the slime animated sprite from the atlas. @@ -76,7 +76,7 @@ protected override void Update(GameTime gameTime) // Check for gamepad input and handle it. CheckGamePadInput(); - // Create a bounding rectangle for the screen + // Create a bounding rectangle for the screen. Rectangle screenBounds = new Rectangle( 0, 0, @@ -112,10 +112,10 @@ protected override void Update(GameTime gameTime) _slimePosition.Y = screenBounds.Bottom - _slime.Height; } - // Calculate the new position of the bat based on the velocity + // Calculate the new position of the bat based on the velocity. Vector2 newBatPosition = _batPosition + _batVelocity; - // Create a bounding circle for the bat + // Create a bounding circle for the bat. Circle batBounds = new Circle( (int)(newBatPosition.X + (_bat.Width * 0.5f)), (int)(newBatPosition.Y + (_bat.Height * 0.5f)), @@ -126,7 +126,7 @@ protected override void Update(GameTime gameTime) // Use distance based checks to determine if the bat is within the // bounds of the game screen, and if it is outside that screen edge, - // reflect it about the screen edge normal + // reflect it about the screen edge normal. if (batBounds.Left < screenBounds.Left) { normal.X = Vector2.UnitX.X; @@ -183,15 +183,15 @@ protected override void Update(GameTime gameTime) private void AssignRandomBatVelocity() { - // Generate a random angle + // Generate a random angle. float angle = (float)(Random.Shared.NextDouble() * Math.PI * 2); - // Convert angle to a direction vector + // Convert angle to a direction vector. float x = (float)Math.Cos(angle); float y = (float)Math.Sin(angle); Vector2 direction = new Vector2(x, y); - // Multiply the direction vector by the movement speed + // Multiply the direction vector by the movement speed. _batVelocity = direction * MOVEMENT_SPEED; } diff --git a/articles/tutorials/building_2d_games/12_collision_detection/snippets/trigger_example.cs b/articles/tutorials/building_2d_games/12_collision_detection/snippets/trigger_example.cs index 0d65cb6e..0b8d6ca9 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/snippets/trigger_example.cs +++ b/articles/tutorials/building_2d_games/12_collision_detection/snippets/trigger_example.cs @@ -1,4 +1,4 @@ -// Create a bounding box for the sprite object +// Create a bounding box for the sprite object. Rectangle spriteBounds = new Rectangle( (int)_spriteLocation.X, (int)_spriteLocation.Y, @@ -6,9 +6,9 @@ (int)_sprite.Height ); -// Detect if the sprite object is within the trigger zone +// Detect if the sprite object is within the trigger zone. if(_spriteBounds.Intersects(_triggerBounds)) { - // Perform some event + // Perform some event. CollectItem(); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/12_collision_detection/snippets/vector2_distance.cs b/articles/tutorials/building_2d_games/12_collision_detection/snippets/vector2_distance.cs index 411c5d7e..be035aa3 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/snippets/vector2_distance.cs +++ b/articles/tutorials/building_2d_games/12_collision_detection/snippets/vector2_distance.cs @@ -16,7 +16,7 @@ // r^2 = 100 int radiiSquared = (circle1Radius + circle2Radius) * (circle1Radius + circle2Radius); -// The circles overlap because 100 is greater than 25 +// The circles overlap because 100 is greater than 25. if(radiiSquared > distanceSquared) { diff --git a/articles/tutorials/building_2d_games/13_working_with_tilemaps/index.md b/articles/tutorials/building_2d_games/13_working_with_tilemaps/index.md index 29168949..4313b6aa 100644 --- a/articles/tutorials/building_2d_games/13_working_with_tilemaps/index.md +++ b/articles/tutorials/building_2d_games/13_working_with_tilemaps/index.md @@ -3,7 +3,7 @@ title: "Chapter 13: Working with Tilemaps" description: "Learn how to implement tile-based game environments using tilemaps and tilesets, including creating reusable classes for managing tiles and loading level designs from XML configuration files." --- -In the previous chapters, you have learned how to draw individual sprites and animated sprites from a texture atlas and handle collision detection. However, the game so far is lacking an actual world or environment to exist in; it is just sprites on a cornflower blue background. Most 2D games feature game worlds built from many tiles arranged in a grid-like patten. These *tilemaps* allow you to efficiently create large game environments without managing thousands of individual sprites. +In the previous chapters, you have learned how to draw individual sprites and animated sprites from a texture atlas and handle collision detection. However, the game so far is lacking an actual world or environment to exist in; it is just sprites on a cornflower blue background. Most 2D games feature game worlds built from many tiles arranged in a grid-like patten. These *tilemaps* allow you to efficiently create large game environments without managing thousands of individual sprites. In this chapter you will: @@ -19,7 +19,7 @@ Tilemaps are a common technique used in 2D game development to create game world ### What is a Tileset? -A tileset is a collection of small images (tiles) that can be combined and arranged to create game environments. Typically these are stored in a single texture atlas, similar to how we have been handing sprites and animations. Common examples of tiles might include: +A tileset is a collection of small images (tiles) that can be combined and arranged to create game environments. Typically these are stored in a single texture atlas, similar to how we have been handing sprites and animations. Common examples of tiles might include: - Floor and ground tiles. - Walls and obstacle tiles. @@ -29,12 +29,12 @@ A tileset is a collection of small images (tiles) that can be combined and arran Each tile in a tileset is assigned an ID number, which the tilemap uses to reference which tile goes where. For example, in *Figure 13-1* below, the tileset we will add to our game in a moment is shown on the left and on the right is the same tileset with an overlay showing how each tile is assigned an ID number. | ![Figure 13-1: Left: Original dungeon tileset. Right: The same tileset with an overlay showing how each tile is assigned a numeric ID](./images/tileset-grid-comparison.png) | -|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| -| **Figure 13-1: Left: Original dungeon tileset. Right: The same tileset with an overlay showing how each tile is assigned a numeric ID** | +| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| **Figure 13-1: Left: Original dungeon tileset. Right: The same tileset with an overlay showing how each tile is assigned a numeric ID** | ### What is a Tilemap? -A tilemap is a grid-based data structure that defines while tiles from a tileset appear at each position in the game world. The tilemap stores an ID for each cell in the grid, where the ID corresponds to a specific tile in the tileset. +A tilemap is a grid-based data structure that defines which tiles from a tileset appear at each position in the game world. The tilemap stores an ID for each cell in the grid, where the ID corresponds to a specific tile in the tileset. For example, a simple tilemap may look like this conceptually: @@ -49,8 +49,8 @@ For example, a simple tilemap may look like this conceptually: If we took the above tilemap data and mapped each cell to the tile in the related tileset, it would look something similar to *Figure 13-2* below: | ![Figure 13-2: From tileset to tilemap. Left: Tileset with an overlay showing the tile IDs. Right: The tilemap created using the tiles arranged with the pattern from the code example above](./images/tileset-to-tilemap-example.png) | -|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| -| **Figure 13-2: From tileset to tilemap. Left: Tileset with an overlay showing the tile IDs. Right: The tilemap created using the tiles arranged with the pattern from the code example above** | +| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| **Figure 13-2: From tileset to tilemap. Left: Tileset with an overlay showing the tile IDs. Right: The tilemap created using the tiles arranged with the pattern from the code example above** | This approach offers several advantage: @@ -62,7 +62,7 @@ We will now take this concept and implement it in our game by creating a `Tilese ## The Tileset Class -The `Tileset` class will manage a collection of tiles from a texture atlas. Each tile will be represented as a `TextureRegion`, building on the tools in the library we created earlier. +The `Tileset` class will manage a collection of tiles from a texture atlas. Each tile will be represented as a `TextureRegion`, building on the tools in the library we created earlier. In the `Graphics` folder of the *MonoGameLibrary* project, create a new file named `Tileset.cs` with the following code as the initial structure: @@ -70,13 +70,13 @@ In the `Graphics` folder of the *MonoGameLibrary* project, create a new file nam ### Tileset Properties and Fields -The `Tileset` class needs to store a `TextureRegion` for each of the individual tiles in the tile set and provide the dimensions (with and height) of the tiles. It should also offers additional properties that provide the total number of rows and columns in the tileset and the total number of tiles. Add the following fields and properties: +The `Tileset` class needs to store a `TextureRegion` for each of the individual tiles in the tile set and provide the dimensions (width and height) of the tiles. It should also offers additional properties that provide the total number of rows and columns in the tileset and the total number of tiles. Add the following fields and properties: [!code-csharp[](./snippets/tileset.cs#properties)] ### Tileset Constructor -The `Tileset` class constructor requires a source `TextureRegion` that represents the tileset and the width and height of the tiles. Based on these parameters provided, it can automatically divide the source `TextureRegion` into a grid of smaller texture regions and calculate the total number of rows, columns, and tiles. +The `Tileset` class constructor requires a source `TextureRegion` that represents the tileset and the width and height of the tiles. Based on these parameters provided, it can automatically divide the source `TextureRegion` into a grid of smaller texture regions and calculate the total number of rows, columns, and tiles. Add the following constructor: @@ -84,13 +84,13 @@ Add the following constructor: ### Tileset Methods -The `Tileset` class needs to provide methods to retrieve the `TextureRegion` of a tile based on the index (tile ID) or by the location (row and column) of the tile in the tileset. Add the following methods: +The `Tileset` class needs to provide methods to retrieve the `TextureRegion` of a tile based on the index (tile ID) or by the location (row and column) of the tile in the tileset. Add the following methods: [!code-csharp[](./snippets/tileset.cs#methods)] ## The Tilemap Class -Now that we have a `Tileset` class to define our tile collection, we need a `Tilemap` class to arrange these tiles into a game level. The `Tilemap` class will store which tile goes where in our game world and provide methods to draw the entire map. +Now that we have a `Tileset` class to define our tile collection, we need a `Tilemap` class to arrange these tiles into a game level. The `Tilemap` class will store which tile goes where in our game world and provide methods to draw the entire map. In the `Graphics` folder of the *MonoGameLibrary* project, create a new file named `Tilemap.cs` with the following code as the initial structure: @@ -98,7 +98,7 @@ In the `Graphics` folder of the *MonoGameLibrary* project, create a new file nam ### Tilemap Properties and Fields -The `Tilemap` class needs to store a reference to the tileset being used, along with an array of the tile IDs representing each tile in the map. It should also offer additional properties that provide the total number of rows and columns are in the tilemap and the total number of tiles. Add the following fields and properties: +The `Tilemap` class needs to store a reference to the tileset being used, along with an array of the tile IDs representing each tile in the map. It should also offer additional properties that provide the total number of rows and columns in the tilemap and the total number of tiles. Add the following fields and properties: [!code-csharp[](./snippets/tilemap.cs#properties)] @@ -112,19 +112,19 @@ Add the following constructor: ### Tilemap Tile Management Methods -The `Tilemap` class should provide methods to set and retrieve tiles, either by index or location (rows and column). Add the following methods: +The `Tilemap` class should provide methods to set and retrieve tiles, either by index or location (rows and column). Add the following methods: [!code-csharp[](./snippets/tilemap.cs#tile-management)] ### Tilemap Draw Method -The `Tilemap` class needs a method to draw the tilemap by iterating through each of the tiles and drawing the `TextureRegion` for that tile at its correct position. Add the following method: +The `Tilemap` class needs a method to draw the tilemap by iterating through each of the tiles and drawing the `TextureRegion` for that tile at its correct position. Add the following method: [!code-csharp[](./snippets/tilemap.cs#draw)] ### Tilemap FromFile Method -The `Tilemap` class also requires a method to load and create an instance of the tilemap from an external configuration file. This allows us to separate level design from code. Add the following method: +The `Tilemap` class also requires a method to load and create an instance of the tilemap from an external configuration file. This allows us to separate level design from code. Add the following method: [!code-csharp[](./snippets/tilemap.cs#from-file)] @@ -146,7 +146,7 @@ Right-click the following image and save it as `atlas.png` in the `Content/image > You do not need to do this in the MGCB editor as you are simply replacing the file and not altering any of its import properties. | ![Figure 13-3: The texture atlas for our game updated to include the tileset for the tilemap](./images/atlas.png) | -|:-----------------------------------------------------------------------------------------------------------------:| +| :---------------------------------------------------------------------------------------------------------------: | | **Figure 13-3: The texture atlas for our game updated to include the tileset for the tilemap** | > [!NOTE] @@ -154,7 +154,7 @@ Right-click the following image and save it as `atlas.png` in the `Content/image ## Creating a Tilemap XML Configuration -Now that we have the texture atlas updated to include the tileset, we need to create a tilemap configuration that our game can load. The configuration will be an XML file that specifies the tileset to use and the arrangement of tiles in the tilemap. +Now that we have the texture atlas updated to include the tileset, we need to create a tilemap configuration that our game can load. The configuration will be an XML file that specifies the tileset to use and the arrangement of tiles in the tilemap. We need to add this configuration file to our content project in the *Content/images* folder with the MGCB Editor, in the same way we did with the "atlas-definition.xml": @@ -166,7 +166,7 @@ We need to add this configuration file to our content project in the *Content/im 6. Save the changes in the MGCB Editor. | ![Figure 13-4: The Content project in the MGCB Editor with the tilemap-definition.xml file added and the Build Action property set to copy](./images/mgcb-editor.png) | -|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------:| +| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | **Figure 13-4: The Content project in the MGCB Editor with the tilemap-definition.xml file added and the Build Action property set to copy** | 7. Open the `tilemap-definition.xml` file in your code editor and replace its contents with the following and save it: @@ -184,7 +184,7 @@ This tilemap configuration creates a simple dungeon layout with walls around the ### Update the Game1 Class -With all of the assets now in place and configured, we can update the `Game1` class to load the tilemap and draw it. We will also need to update the collision logic so that the boundary is no longer the edge of the screen, but instead the edges of the wall tiles of the tilemap. Open `Game1.cs` and make the following updates: +With all of the assets now in place and configured, we can update the `Game1` class to load the tilemap and draw it. We will also need to update the collision logic so that the boundary is no longer the edge of the screen, but instead the edges of the wall tiles of the tilemap. Open `Game1.cs` and make the following updates: [!code-csharp[](./snippets/game1.cs?highlight=31-35,46-61,80-82,112,114,116,128,121,123,125,127,145,148,150,153,156,159,161,164,179-181,303-304)] @@ -198,13 +198,13 @@ The key changes to the `Game1` class include: 4. In [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent): 1. The tilemap is loaded from the XML configuration file. 2. The scale of the tilemap is set to a factor of 4.0. -5. In [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), the `screenBounds` variable was removed and the collision logic has been updated to instead use the `_roomBounds` instead. -6. In [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) the tilemap is drawn. +5. In [**Update**](), the `screenBounds` variable was removed and the collision logic has been updated to instead use the `_roomBounds` instead. +6. In [**Draw**]() the tilemap is drawn. Running the game now with these changes, our game now visually transforms from a simple screen with sprites to a proper game environment with walls and floors. The slime and bat are now confined within the walls of the dungeon defined by our tilemap. | ![Figure 13-5: Gameplay with the tilemap rendered and the bat and slime contained within the dungeon walls](./videos/gameplay.webm) | -|:-----------------------------------------------------------------------------------------------------------------------------------:| +| :---------------------------------------------------------------------------------------------------------------------------------: | | **Figure 13-5: Gameplay with the tilemap rendered and the bat and slime contained within the dungeon walls** | ## Additional Notes @@ -225,36 +225,36 @@ In this chapter, you accomplished the following: - Implemented a `Tilemap` class to render grid-based game environments. - Created an XML-based tilemap definition system for storing level layouts. - Updated our game to use tilemaps for the game environment. - + In the next chapter, we will start exploring audio to add sound effects when a collision occurs and background music to our game. ## Test Your Knowledge 1. What is the main advantage of using tilemaps for game environments rather than individual sprites? - :::question-answer - Tilemaps offer several advantages: memory efficiency (reusing tiles instead of storing complete environments), performance optimization (batched rendering), and design flexibility (easier to create and modify levels). They allow creating large game worlds by reusing a small set of tiles in different arrangements. - ::: + :::question-answer + Tilemaps offer several advantages: memory efficiency (reusing tiles instead of storing complete environments), performance optimization (batched rendering), and design flexibility (easier to create and modify levels). They allow creating large game worlds by reusing a small set of tiles in different arrangements. + ::: 2. What is the relationship between a tileset and a tilemap? - :::question-answer - A tileset is a collection of individual tiles stored in a texture atlas, where each tile has a unique ID. A tilemap is a grid-based structure that references tiles from the tileset by their IDs to create a complete game environment. The tileset provides the visual elements, while the tilemap defines their arrangement. - ::: + :::question-answer + A tileset is a collection of individual tiles stored in a texture atlas, where each tile has a unique ID. A tilemap is a grid-based structure that references tiles from the tileset by their IDs to create a complete game environment. The tileset provides the visual elements, while the tilemap defines their arrangement. + ::: 3. Why might you use an XML definition for a tilemap instead of hardcoding the tile layout? - :::question-answer - Using XML definitions for tilemaps separates level design from game code, offering several benefits: easier level editing (without changing code), support for multiple levels, ability to create external level editors, and better organization of game content. It also allows non-programmers like game designers to create and modify levels. - ::: + :::question-answer + Using XML definitions for tilemaps separates level design from game code, offering several benefits: easier level editing (without changing code), support for multiple levels, ability to create external level editors, and better organization of game content. It also allows non-programmers like game designers to create and modify levels. + ::: 4. In our implementation, how does the Tilemap's Draw method work? - :::question-answer - The Tilemap's Draw method iterates through each position in the grid. For each position, it: + :::question-answer + The Tilemap's Draw method iterates through each position in the grid. For each position, it: - 1. Retrieves the tile ID stored at that position. - 2. Gets the corresponding texture region from the tileset. - 3. Calculates the screen position based on the grid coordinates and tile size. - 4. Draws the texture region at that position using the sprite batch. - ::: + 1. Retrieves the tile ID stored at that position. + 2. Gets the corresponding texture region from the tileset. + 3. Calculates the screen position based on the grid coordinates and tile size. + 4. Draws the texture region at that position using the sprite batch. + ::: diff --git a/articles/tutorials/building_2d_games/13_working_with_tilemaps/snippets/game1.cs b/articles/tutorials/building_2d_games/13_working_with_tilemaps/snippets/game1.cs index adbd0a34..c6b52199 100644 --- a/articles/tutorials/building_2d_games/13_working_with_tilemaps/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/13_working_with_tilemaps/snippets/game1.cs @@ -57,7 +57,7 @@ protected override void Initialize() int centerColumn = _tilemap.Columns / 2; _slimePosition = new Vector2(centerColumn * _tilemap.TileWidth, centerRow * _tilemap.TileHeight); - // Initial bat position will the in the top left corner of the room + // Initial bat position will be in the top left corner of the room _batPosition = new Vector2(_roomBounds.Left, _roomBounds.Top); // Assign the initial random velocity to the bat. diff --git a/articles/tutorials/building_2d_games/15_audio_controller/index.md b/articles/tutorials/building_2d_games/15_audio_controller/index.md index 80f7bc69..77bb62a1 100644 --- a/articles/tutorials/building_2d_games/15_audio_controller/index.md +++ b/articles/tutorials/building_2d_games/15_audio_controller/index.md @@ -29,10 +29,10 @@ To get started, in the *MonoGameLibrary* project: 2. Add a new class file named `AudioController.cs` to the `Audio` folder you just created. 3. Add the following code as the initial structure for the class - [!code-csharp[](./snippets/audiocontroller.cs#declaration)] + [!code-csharp[](./snippets/audiocontroller.cs#declaration)] - > [!NOTE] - > The `AudioController` class will implement the `IDisposable` interface, This interface is part of .NET and provides a standardized implementation for an object to release resources. Implementing `IDisposable` allows other code to properly clean up the resources held by our audio controller when it is no longer needed. For more information on `IDisposable`, you can read the [Implement a Dispose Method](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose) article on Microsoft Learn. + > [!NOTE] + > The `AudioController` class will implement the `IDisposable` interface, This interface is part of .NET and provides a standardized implementation for an object to release resources. Implementing `IDisposable` allows other code to properly clean up the resources held by our audio controller when it is no longer needed. For more information on `IDisposable`, you can read the [Implement a Dispose Method](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose) article on Microsoft Learn. ### AudioController Properties and Fields @@ -51,7 +51,7 @@ Add the following constructor and finalizer: [!code-csharp[](./snippets/audiocontroller.cs#ctors)] > [!NOTE] -> The `AudioController` class implements a [finalizer](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/finalizers) method `~AudioManager()`. This method is called when an instance of the class is collected by the garbage collector and is here as part of the `IDisposable` implementation. +> The `AudioController` class implements a [finalizer](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/finalizers) method `~AudioManager()`. This method is called when an instance of the class is collected by the garbage collector and is here as part of the `IDisposable` implementation. ### AudioController Methods @@ -67,13 +67,13 @@ So lets add them below. #### AudioController Update -The `Update` method will check for existing sound effect instances that have expired and properly dispose of them. Add the following method: +The `Update` method will check for existing sound effect instances that have expired and properly dispose of them. Add the following method: [!code-csharp[](./snippets/audiocontroller.cs#update)] #### AudioController Playback -While the MonoGame simplified audio API allows sound effects to be played in a fire and forget manner, doing it this way does not work if you need to pause them because the game paused. Instead, we can add playback methods through the `AudioController` that can track the sound effect instances and pause them if needed, as well as checking the media player state before playing a song. +While the MonoGame simplified audio API allows sound effects to be played in a fire and forget manner, doing it this way does not work if you need to pause them because the game paused. Instead, we can add playback methods through the `AudioController` that can track the sound effect instances and pause them if needed, as well as checking the media player state before playing a song. Add the following methods: @@ -108,14 +108,14 @@ For our `AudioController`, implementing `IDisposable` means we can ensure all so ## Implementing the AudioController Class -Now that we have the audio controller class complete, we can update the game to use it. We will do this in two steps: +Now that we have the audio controller class complete, we can update the game to use it. We will do this in two steps: 1. First, update the `Core` class to add the `AudioController` globally. 1. Update the `Game1` class to use the global audio controller from `Core`. ### Updating the Core Class -The `Core` class serves as our the base game class, so we will update it first to add and expose the `AudioController` globally. Open the `Core.cs` file in the *MonoGameLibrary* project and update it to the following: +The `Core` class serves as our base game class, so we will update it first to add and expose the `AudioController` globally. Open the `Core.cs` file in the *MonoGameLibrary* project and update it to the following: [!code-csharp[](./snippets/core.cs?highlight=6,50-53,112-113,116-122,129-130)] @@ -129,32 +129,32 @@ The key changes made here are: ### Updating the Game1 Class -Next, update the `Game1` class to use the audio controller for audio playback. Open `Game1.cs` and make the following updates: +Next, update the `Game1` class to use the audio controller for audio playback. Open `Game1.cs` and make the following updates: [!code-csharp[](./snippets/game1.cs?highlight=45-46,77-78,104-105,197-198,216-217,270-288)] > [!NOTE] -> Note there were a lot of replacements in the `LoadContent` method, switching from loading and initializing the background Song and replacing it with a call to the new `AudioController` to do all the work managing the Song reference. Much cleaner. +> Note there were a lot of replacements in the `LoadContent` method, switching from loading and initializing the background Song and replacing it with a call to the new `AudioController` to do all the work managing the Song reference. Much cleaner. The key changes made here are: 1. The `_themeSong` field is added to store a reference to the background song to play. -2. In [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent), the background theme song is loaded using hte content manager. +2. In [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent), the background theme song is loaded using the content manager. 3. In [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize), the audio manager is used to play the background theme song. -4. In [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)) the audio manager is used to play the bounce and collect sound effects. +4. In [**Update**]() the audio manager is used to play the bounce and collect sound effects. 5. In `CheckKeyboardInput` the following checks were added 1. If the M key on the keyboard is pressed, it will toggle mute for all audio. 2. If the + key is pressed, the song and sound effect volumes are increased by `0.1f`. 3. If the - key is pressed, the song and sound effect volumes are decreased by `0.1f`. -Running the game now will produce the same result as the previous chapter, only now the lifetime of sound effects and the state management of audio is done through the new audio controller. You can also mute and unumte the audio with the M key and increase and decrease the volume using the + and - keys. +Running the game now will produce the same result as the previous chapter, only now the lifetime of sound effects and the state management of audio is done through the new audio controller. You can also mute and unumte the audio with the M key and increase and decrease the volume using the + and - keys. | ![Figure 15-1: Gameplay with audio.](./videos/gameplay.webm) | -|:--------------------------------------------------------------------------------------:| -| **Figure 15-1: Gameplay with audio.** | +| :----------------------------------------------------------: | +| **Figure 15-1: Gameplay with audio.** | > [!NOTE] -> You may note that while we added keybindings to change the audio settings, we did not add any bindings for the GamePad. This is simply becuase this is not normally how you would adjust these values on a console, on consoles you would have a settings/options screen to update them. +> You may note that while we added keybindings to change the audio settings, we did not add any bindings for the GamePad. This is simply becuase this is not normally how you would adjust these values on a console, on consoles you would have a settings/options screen to update them. > > Later in [Chapter 20: Implementing UI with GUM](../20_implementing_ui_with_gum/index.md) we will add an Options screen to adjust all the audio values for the game. diff --git a/articles/tutorials/building_2d_games/15_audio_controller/snippets/audiocontroller.cs b/articles/tutorials/building_2d_games/15_audio_controller/snippets/audiocontroller.cs index 34b57261..a3909fc6 100644 --- a/articles/tutorials/building_2d_games/15_audio_controller/snippets/audiocontroller.cs +++ b/articles/tutorials/building_2d_games/15_audio_controller/snippets/audiocontroller.cs @@ -100,13 +100,13 @@ public AudioController() _activeSoundEffectInstances = new List(); } - // Finalizer called when object is collected by the garbage collector + // Finalizer called when object is collected by the garbage collector. ~AudioController() => Dispose(false); #endregion #region update /// - /// Updates this audio controller + /// Updates this audio controller. /// public void Update() { @@ -226,10 +226,10 @@ public void PlaySong(Song song, bool isRepeating = true) /// public void PauseAudio() { - // Pause any active songs playing + // Pause any active songs playing. MediaPlayer.Pause(); - // Pause any active sound effects + // Pause any active sound effects. foreach (SoundEffectInstance soundEffectInstance in _activeSoundEffectInstances) { soundEffectInstance.Pause(); @@ -244,7 +244,7 @@ public void ResumeAudio() // Resume paused music MediaPlayer.Resume(); - // Resume any active sound effects + // Resume any active sound effects. foreach (SoundEffectInstance soundEffectInstance in _activeSoundEffectInstances) { soundEffectInstance.Resume(); @@ -272,7 +272,7 @@ public void MuteAudio() /// public void UnmuteAudio() { - // Restore the previous volume values + // Restore the previous volume values. MediaPlayer.Volume = _previousSongVolume; SoundEffect.MasterVolume = _previousSoundEffectVolume; diff --git a/articles/tutorials/building_2d_games/15_audio_controller/snippets/core.cs b/articles/tutorials/building_2d_games/15_audio_controller/snippets/core.cs index 592e1bd9..99f79aba 100644 --- a/articles/tutorials/building_2d_games/15_audio_controller/snippets/core.cs +++ b/articles/tutorials/building_2d_games/15_audio_controller/snippets/core.cs @@ -73,25 +73,25 @@ public Core(string title, int width, int height, bool fullScreen) // Create a new graphics device manager. Graphics = new GraphicsDeviceManager(this); - // Set the graphics defaults + // Set the graphics defaults. Graphics.PreferredBackBufferWidth = width; Graphics.PreferredBackBufferHeight = height; Graphics.IsFullScreen = fullScreen; - // Apply the graphic presentation changes + // Apply the graphic presentation changes. Graphics.ApplyChanges(); - // Set the window title + // Set the window title. Window.Title = title; // Set the core's content manager to a reference of hte base Game's // content manager. Content = base.Content; - // Set the root directory for content + // Set the root directory for content. Content.RootDirectory = "Content"; - // Mouse is visible by default + // Mouse is visible by default. IsMouseVisible = true; } @@ -106,7 +106,7 @@ protected override void Initialize() // Create the sprite batch instance. SpriteBatch = new SpriteBatch(GraphicsDevice); - // Create a new input manager + // Create a new input manager. Input = new InputManager(); // Create a new audio controller. diff --git a/articles/tutorials/building_2d_games/15_audio_controller/snippets/game1.cs b/articles/tutorials/building_2d_games/15_audio_controller/snippets/game1.cs index 77c8d393..3b31ea0e 100644 --- a/articles/tutorials/building_2d_games/15_audio_controller/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/15_audio_controller/snippets/game1.cs @@ -68,19 +68,19 @@ protected override void Initialize() int centerColumn = _tilemap.Columns / 2; _slimePosition = new Vector2(centerColumn * _tilemap.TileWidth, centerRow * _tilemap.TileHeight); - // Initial bat position will the in the top left corner of the room + // Initial bat position will the in the top left corner of the room. _batPosition = new Vector2(_roomBounds.Left, _roomBounds.Top); // Assign the initial random velocity to the bat. AssignRandomBatVelocity(); - // Start playing the background music + // Start playing the background music. Audio.PlaySong(_themeSong); } protected override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml"); // Create the slime animated sprite from the atlas. @@ -95,13 +95,13 @@ protected override void LoadContent() _tilemap = Tilemap.FromFile(Content, "images/tilemap-definition.xml"); _tilemap.Scale = new Vector2(4.0f, 4.0f); - // Load the bounce sound effect + // Load the bounce sound effect. _bounceSoundEffect = Content.Load("audio/bounce"); - // Load the collect sound effect + // Load the collect sound effect. _collectSoundEffect = Content.Load("audio/collect"); - // Load the background theme music + // Load the background theme music. _themeSong = Content.Load("audio/theme"); } @@ -122,7 +122,7 @@ protected override void Update(GameTime gameTime) // Check for gamepad input and handle it. CheckGamePadInput(); - // Creating a bounding circle for the slime + // Creating a bounding circle for the slime. Circle slimeBounds = new Circle( (int)(_slimePosition.X + (_slime.Width * 0.5f)), (int)(_slimePosition.Y + (_slime.Height * 0.5f)), @@ -150,10 +150,10 @@ protected override void Update(GameTime gameTime) _slimePosition.Y = _roomBounds.Bottom - _slime.Height; } - // Calculate the new position of the bat based on the velocity + // Calculate the new position of the bat based on the velocity. Vector2 newBatPosition = _batPosition + _batVelocity; - // Create a bounding circle for the bat + // Create a bounding circle for the bat. Circle batBounds = new Circle( (int)(newBatPosition.X + (_bat.Width * 0.5f)), (int)(newBatPosition.Y + (_bat.Height * 0.5f)), @@ -194,7 +194,7 @@ protected override void Update(GameTime gameTime) { _batVelocity = Vector2.Reflect(_batVelocity, normal); - // Play the bounce sound effect + // Play the bounce sound effect. Audio.PlaySoundEffect(_bounceSoundEffect); } @@ -210,10 +210,10 @@ protected override void Update(GameTime gameTime) // the column and row multiplied by the width and height. _batPosition = new Vector2(column * _bat.Width, row * _bat.Height); - // Assign a new random velocity to the bat + // Assign a new random velocity to the bat. AssignRandomBatVelocity(); - // Play the collect sound effect + // Play the collect sound effect. Audio.PlaySoundEffect(_collectSoundEffect); } @@ -222,15 +222,15 @@ protected override void Update(GameTime gameTime) private void AssignRandomBatVelocity() { - // Generate a random angle + // Generate a random angle. float angle = (float)(Random.Shared.NextDouble() * Math.PI * 2); - // Convert angle to a direction vector + // Convert angle to a direction vector. float x = (float)Math.Cos(angle); float y = (float)Math.Sin(angle); Vector2 direction = new Vector2(x, y); - // Multiply the direction vector by the movement speed + // Multiply the direction vector by the movement speed. _batVelocity = direction * MOVEMENT_SPEED; } diff --git a/articles/tutorials/building_2d_games/16_working_with_spritefonts/index.md b/articles/tutorials/building_2d_games/16_working_with_spritefonts/index.md index f13de652..8bff8951 100644 --- a/articles/tutorials/building_2d_games/16_working_with_spritefonts/index.md +++ b/articles/tutorials/building_2d_games/16_working_with_spritefonts/index.md @@ -211,7 +211,7 @@ Next, open the *04B_30.spritefont* file in your code editor and make the followi The key changes here are: 1. The `` element was updated to `04B_11.ttf`, the exact filename with extension of the TTF font we just downloaded. -2. The `` element was updated to be `32`. +2. The `` element was updated to be `17.5`. ### Updating the Game @@ -245,7 +245,7 @@ In this chapter, you accomplished the following: - Learned how to load SpriteFonts through the content pipeline. - Learned how to draw text with various parameters to control appearance. - Learned how to measure text dimensions. -- Implementing a score display and boost indicator in our game. +- Implemented a score display in our game. In the next chapter we will discuss MonoGame's service container and how we can use it to start breaking our monolithic game file into modules for better maintainability. diff --git a/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/center_example.cs b/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/center_example.cs index ed01e160..47a945bd 100644 --- a/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/center_example.cs +++ b/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/center_example.cs @@ -4,10 +4,10 @@ // Measure the size of the message to get the text dimensions. Vector2 textSize = font.MeasureString(message); -// Set the origin to the center of the text dimensions +// Set the origin to the center of the text dimensions. Vector2 origin = textSize * 0.5f; -// Position will be the center of the screen +// Position will be the center of the screen. Vector2 position = new Vector2( GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight diff --git a/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/game1.cs b/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/game1.cs index 458f4a45..fa7250d9 100644 --- a/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/game1.cs @@ -42,10 +42,10 @@ public class Game1 : Core // The sound effect to play when the slime eats a bat. private SoundEffect _collectSoundEffect; - // The background theme song + // The background theme song. private Song _themeSong; - // The SpriteFont Description used to draw text + // The SpriteFont Description used to draw text. private SpriteFont _font; // Tracks the players score. @@ -81,13 +81,13 @@ protected override void Initialize() int centerColumn = _tilemap.Columns / 2; _slimePosition = new Vector2(centerColumn * _tilemap.TileWidth, centerRow * _tilemap.TileHeight); - // Initial bat position will the in the top left corner of the room + // Initial bat position will the in the top left corner of the room. _batPosition = new Vector2(_roomBounds.Left, _roomBounds.Top); // Assign the initial random velocity to the bat. AssignRandomBatVelocity(); - // Start playing the background music + // Start playing the background music. Audio.PlaySong(_themeSong); // Set the position of the score text to align to the left edge of the @@ -101,7 +101,7 @@ protected override void Initialize() protected override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml"); // Create the slime animated sprite from the atlas. @@ -116,13 +116,13 @@ protected override void LoadContent() _tilemap = Tilemap.FromFile(Content, "images/tilemap-definition.xml"); _tilemap.Scale = new Vector2(4.0f, 4.0f); - // Load the bounce sound effect + // Load the bounce sound effect. _bounceSoundEffect = Content.Load("audio/bounce"); - // Load the collect sound effect + // Load the collect sound effect. _collectSoundEffect = Content.Load("audio/collect"); - // Load the background theme music + // Load the background theme music. _themeSong = Content.Load("audio/theme"); // Load the font @@ -146,7 +146,7 @@ protected override void Update(GameTime gameTime) // Check for gamepad input and handle it. CheckGamePadInput(); - // Creating a bounding circle for the slime + // Creating a bounding circle for the slime. Circle slimeBounds = new Circle( (int)(_slimePosition.X + (_slime.Width * 0.5f)), (int)(_slimePosition.Y + (_slime.Height * 0.5f)), @@ -218,7 +218,7 @@ protected override void Update(GameTime gameTime) { _batVelocity = Vector2.Reflect(_batVelocity, normal); - // Play the bounce sound effect + // Play the bounce sound effect. Audio.PlaySoundEffect(_bounceSoundEffect); } @@ -234,10 +234,10 @@ protected override void Update(GameTime gameTime) // the column and row multiplied by the width and height. _batPosition = new Vector2(column * _bat.Width, row * _bat.Height); - // Assign a new random velocity to the bat + // Assign a new random velocity to the bat. AssignRandomBatVelocity(); - // Play the collect sound effect + // Play the collect sound effect. Audio.PlaySoundEffect(_collectSoundEffect); // Increase the player's score. @@ -249,7 +249,7 @@ protected override void Update(GameTime gameTime) private void AssignRandomBatVelocity() { - // Generate a random angle + // Generate a random angle. float angle = (float)(Random.Shared.NextDouble() * Math.PI * 2); // Convert angle to a direction vector diff --git a/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/measurestring.cs b/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/measurestring.cs index 1e7d5dec..5888f7f7 100644 --- a/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/measurestring.cs +++ b/articles/tutorials/building_2d_games/16_working_with_spritefonts/snippets/measurestring.cs @@ -1,4 +1,4 @@ -// The text to measure +// The text to measure. string message = "Hello, MonoGame!"; // Measure the size of the message to get the text dimensions. diff --git a/articles/tutorials/building_2d_games/17_scenes/index.md b/articles/tutorials/building_2d_games/17_scenes/index.md index 57658750..ae966d60 100644 --- a/articles/tutorials/building_2d_games/17_scenes/index.md +++ b/articles/tutorials/building_2d_games/17_scenes/index.md @@ -20,7 +20,7 @@ In this chapter, you will: - Create a title scene and gameplay scene for our game - Refactor our existing game to use the scene system -We will being by first defining the lifecycle of a scene that will be followed. +We will begin by first defining the lifecycle of a scene that will be followed. ## Scene Lifecycle @@ -87,7 +87,7 @@ Add the following methods to the `Scene` class to complete the implementation of [!code-csharp[](./snippets/scene.cs#disposable)] -This completes our Base scene implementation that we will use to create actual scenes from in our project, next we need a manager that will organise the screens for use in the game. +With the Base scene implementation complete, we can now use it to create actual scenes for our project. The next step is to implement a manager that organizes and coordinates these scenes within the game. ## Scene Management @@ -104,18 +104,18 @@ The key changes here are: 2. A check is made to see if there is an active scene, and if so, updates it. 4. An override for the `Draw` method was added where a check is made to see if there is an active scene, and if so, draws it. 5. The `ChangeScene` method was added which can be called when we want to tell the core to change from one scene to another one. -6. The `TransitionScene` method was add that gracefully transitions from the current scene to the next scene by - 1. A check is made to see if there is an active scene, and if so, disposes it. - 2. The garbage collector is told to perform a collection to clear out memory from the disposal of the current scene. - 3. The next scene is set as the current scene. - 4. A check is made to see if there is now a current scene, and if so, initializes it. +6. The `TransitionScene` method was added to gracefully switch from the current scene to the next by: + 1. Checking if there is an active scene, and disposing it if so. + 2. Triggering garbage collection to reclaim memory from the disposed scene. + 3. Assigning the next scene as the current scene. + 4. Checking if a current scene now exists, and initializing it if so. > [!TIP] > Notice that we use a two-step process for scene transitions with separate `_activeScene` and `_nextScene` fields. This design allows the current scene to complete its update/draw cycle before the transition occurs, preventing potential issues that could arise from changing scenes in the middle of processing. The actual transition happens at a controlled point in the game loop, ensuring clean disposal of the old scene before initializing the new one. ## Updating the Game -With the scene architecture in place, the game can now be updated so that it is broken down into scenes. We will create two scenes; a title scene and a gameplay scene. First, however, we need to add an additional SpriteFont Description that will be used during the title scene to display the title of the game. Open the *Content.mgcb* content project file in the MGCB Editor and perform the following: +With the scene architecture in place, we’re now ready to update the game by breaking it into separate scenes. We will create two scenes; a title scene and a gameplay scene. First, however, we need to add an additional SpriteFont Description that will be used during the title scene to display the title of the game. Open the *Content.mgcb* content project file in the MGCB Editor and perform the following: 1. Right-click the `fonts` folder and choose `Add > New Item...`. 2. Select `SpriteFont Description (.spritefont)` from the options. @@ -154,7 +154,7 @@ Add the following fields to the `TitleScene` class: - Three `const` fields (`DUNGEON_TEXT`, `SLIME_TEXT`, `PRESS_ENTER_TEXT`) are added for the text that will be displayed on the title screen. - The `_font` field stores a reference to the [**SpriteFont**](xref:Microsoft.Xna.Framework.Graphics.SpriteFont) we will use to draw the press enter prompt with. -- The `_font3x` field stores a reference to the [**SpriteFont**](xref:Microsoft.Xna.Framework.Graphics.SpriteFont) we will use to draw the dungeon and slime text with that will make up the title of the game. +- The `_font5x` field stores a reference to the [**SpriteFont**](xref:Microsoft.Xna.Framework.Graphics.SpriteFont) we will use to draw the dungeon and slime text with that will make up the title of the game. - The `_dungeonTextPos` and `_dungeonTextOrigin` fields store the position and origin we will use to draw the "Dungeon" text at. - The `_slimeTextPos` and `_slimeTextOrigin` fields store the position and origin we will draw the "Slime" text at. - The `_pressEnterPos` and `_pressEnterOrigin` fields store the position and origin we will draw the "Press Enter To Start" text at. diff --git a/articles/tutorials/building_2d_games/17_scenes/snippets/core.cs b/articles/tutorials/building_2d_games/17_scenes/snippets/core.cs index a9eda94e..b693cb52 100644 --- a/articles/tutorials/building_2d_games/17_scenes/snippets/core.cs +++ b/articles/tutorials/building_2d_games/17_scenes/snippets/core.cs @@ -85,20 +85,20 @@ public Core(string title, int width, int height, bool fullScreen) Graphics.PreferredBackBufferHeight = height; Graphics.IsFullScreen = fullScreen; - // Apply the graphic presentation changes + // Apply the graphic presentation changes. Graphics.ApplyChanges(); // Set the window title Window.Title = title; - // Set the core's content manager to a reference of hte base Game's + // Set the core's content manager to a reference of the base Game's // content manager. Content = base.Content; - // Set the root directory for content + // Set the root directory for content. Content.RootDirectory = "Content"; - // Mouse is visible by default + // Mouse is visible by default. IsMouseVisible = true; } @@ -113,7 +113,7 @@ protected override void Initialize() // Create the sprite batch instance. SpriteBatch = new SpriteBatch(GraphicsDevice); - // Create a new input manager + // Create a new input manager. Input = new InputManager(); // Create a new audio controller. @@ -142,7 +142,7 @@ protected override void Update(GameTime gameTime) } // if there is a next scene waiting to be switch to, then transition - // to that scene + // to that scene. if (s_nextScene != null) { TransitionScene(); @@ -180,16 +180,16 @@ public static void ChangeScene(Scene next) private static void TransitionScene() { - // If there is an active scene, dispose of it + // If there is an active scene, dispose of it. if (s_activeScene != null) { s_activeScene.Dispose(); } - // Force the garbage collector to collect to ensure memory is cleared + // Force the garbage collector to collect to ensure memory is cleared. GC.Collect(); - // Change the currently active scene to the new scene + // Change the currently active scene to the new scene. s_activeScene = s_nextScene; // Null out the next scene value so it does not trigger a change over and over. diff --git a/articles/tutorials/building_2d_games/17_scenes/snippets/game1.cs b/articles/tutorials/building_2d_games/17_scenes/snippets/game1.cs index 2b8fffb4..ed25a3a8 100644 --- a/articles/tutorials/building_2d_games/17_scenes/snippets/game1.cs +++ b/articles/tutorials/building_2d_games/17_scenes/snippets/game1.cs @@ -6,7 +6,7 @@ namespace DungeonSlime; public class Game1 : Core { - // The background theme song + // The background theme song. private Song _themeSong; public Game1() : base("Dungeon Slime", 1280, 720, false) @@ -18,7 +18,7 @@ protected override void Initialize() { base.Initialize(); - // Start playing the background music + // Start playing the background music. Audio.PlaySong(_themeSong); // Start the game with the title scene. @@ -27,7 +27,7 @@ protected override void Initialize() protected override void LoadContent() { - // Load the background theme music + // Load the background theme music. _themeSong = Content.Load("audio/theme"); } } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/17_scenes/snippets/gamescene.cs b/articles/tutorials/building_2d_games/17_scenes/snippets/gamescene.cs index 930a4c05..d8c5f260 100644 --- a/articles/tutorials/building_2d_games/17_scenes/snippets/gamescene.cs +++ b/articles/tutorials/building_2d_games/17_scenes/snippets/gamescene.cs @@ -85,7 +85,7 @@ public override void Initialize() int centerColumn = _tilemap.Columns / 2; _slimePosition = new Vector2(centerColumn * _tilemap.TileWidth, centerRow * _tilemap.TileHeight); - // Initial bat position will the in the top left corner of the room + // Initial bat position will the in the top left corner of the room. _batPosition = new Vector2(_roomBounds.Left, _roomBounds.Top); // Set the position of the score text to align to the left edge of the @@ -104,7 +104,7 @@ public override void Initialize() #region loadcontent public override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Core.Content, "images/atlas-definition.xml"); // Create the slime animated sprite from the atlas. @@ -119,13 +119,13 @@ public override void LoadContent() _tilemap = Tilemap.FromFile(Content, "images/tilemap-definition.xml"); _tilemap.Scale = new Vector2(4.0f, 4.0f); - // Load the bounce sound effect + // Load the bounce sound effect. _bounceSoundEffect = Content.Load("audio/bounce"); - // Load the collect sound effect + // Load the collect sound effect. _collectSoundEffect = Content.Load("audio/collect"); - // Load the font + // Load the font. _font = Core.Content.Load("fonts/04B_30"); } #endregion @@ -145,7 +145,7 @@ public override void Update(GameTime gameTime) // Check for gamepad input and handle it. CheckGamePadInput(); - // Creating a bounding circle for the slime + // Creating a bounding circle for the slime. Circle slimeBounds = new Circle( (int)(_slimePosition.X + (_slime.Width * 0.5f)), (int)(_slimePosition.Y + (_slime.Height * 0.5f)), @@ -173,10 +173,10 @@ public override void Update(GameTime gameTime) _slimePosition.Y = _roomBounds.Bottom - _slime.Height; } - // Calculate the new position of the bat based on the velocity + // Calculate the new position of the bat based on the velocity. Vector2 newBatPosition = _batPosition + _batVelocity; - // Create a bounding circle for the bat + // Create a bounding circle for the bat. Circle batBounds = new Circle( (int)(newBatPosition.X + (_bat.Width * 0.5f)), (int)(newBatPosition.Y + (_bat.Height * 0.5f)), @@ -187,7 +187,7 @@ public override void Update(GameTime gameTime) // Use distance based checks to determine if the bat is within the // bounds of the game screen, and if it is outside that screen edge, - // reflect it about the screen edge normal + // reflect it about the screen edge normal. if (batBounds.Left < _roomBounds.Left) { normal.X = Vector2.UnitX.X; @@ -217,7 +217,7 @@ public override void Update(GameTime gameTime) { _batVelocity = Vector2.Reflect(_batVelocity, normal); - // Play the bounce sound effect + // Play the bounce sound effect. Core.Audio.PlaySoundEffect(_bounceSoundEffect); } @@ -233,10 +233,10 @@ public override void Update(GameTime gameTime) // the column and row multiplied by the width and height. _batPosition = new Vector2(column * _bat.Width, row * _bat.Height); - // Assign a new random velocity to the bat + // Assign a new random velocity to the bat. AssignRandomBatVelocity(); - // Play the collect sound effect + // Play the collect sound effect. Core.Audio.PlaySoundEffect(_collectSoundEffect); // Increase the player's score. @@ -248,10 +248,10 @@ public override void Update(GameTime gameTime) #region helpers private void AssignRandomBatVelocity() { - // Generate a random angle + // Generate a random angle. float angle = (float)(Random.Shared.NextDouble() * Math.PI * 2); - // Convert angle to a direction vector + // Convert angle to a direction vector. float x = (float)Math.Cos(angle); float y = (float)Math.Sin(angle); Vector2 direction = new Vector2(x, y); @@ -265,7 +265,7 @@ private void CheckKeyboardInput() // Get a reference to the keyboard inof KeyboardInfo keyboard = Core.Input.Keyboard; - // If the escape key is pressed, return to the title screen + // If the escape key is pressed, return to the title screen. if (Core.Input.Keyboard.WasKeyJustPressed(Keys.Escape)) { Core.ChangeScene(new TitleScene()); @@ -396,7 +396,7 @@ public override void Draw(GameTime gameTime) // Draw the bat sprite. _bat.Draw(Core.SpriteBatch, _batPosition); - // Draw the score + // Draw the score. Core.SpriteBatch.DrawString( _font, // spriteFont $"Score: {_score}", // text diff --git a/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs b/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs index 17501401..05678ea3 100644 --- a/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs +++ b/articles/tutorials/building_2d_games/17_scenes/snippets/titlescene.cs @@ -76,7 +76,7 @@ public override void LoadContent() // Load the font for the standard text. _font = Core.Content.Load("fonts/04B_30"); - // Load the font for the title text + // Load the font for the title text. _font5x = Content.Load("fonts/04B_30_5x"); } #endregion @@ -104,20 +104,20 @@ public override void Draw(GameTime gameTime) Color dropShadowColor = Color.Black * 0.5f; // Draw the Dungeon text slightly offset from it is original position and - // with a transparent color to give it a drop shadow + // with a transparent color to give it a drop shadow. Core.SpriteBatch.DrawString(_font5x, DUNGEON_TEXT, _dungeonTextPos + new Vector2(10, 10), dropShadowColor, 0.0f, _dungeonTextOrigin, 1.0f, SpriteEffects.None, 1.0f); - // Draw the Dungeon text on top of that at its original position + // Draw the Dungeon text on top of that at its original position. Core.SpriteBatch.DrawString(_font5x, DUNGEON_TEXT, _dungeonTextPos, Color.White, 0.0f, _dungeonTextOrigin, 1.0f, SpriteEffects.None, 1.0f); // Draw the Slime text slightly offset from it is original position and - // with a transparent color to give it a drop shadow + // with a transparent color to give it a drop shadow. Core.SpriteBatch.DrawString(_font5x, SLIME_TEXT, _slimeTextPos + new Vector2(10, 10), dropShadowColor, 0.0f, _slimeTextOrigin, 1.0f, SpriteEffects.None, 1.0f); - // Draw the Slime text on top of that at its original position + // Draw the Slime text on top of that at its original position. Core.SpriteBatch.DrawString(_font5x, SLIME_TEXT, _slimeTextPos, Color.White, 0.0f, _slimeTextOrigin, 1.0f, SpriteEffects.None, 1.0f); - // Draw the press enter text + // Draw the press enter text. Core.SpriteBatch.DrawString(_font, PRESS_ENTER_TEXT, _pressEnterPos, Color.White, 0.0f, _pressEnterOrigin, 1.0f, SpriteEffects.None, 0.0f); // Always end the sprite batch when finished. diff --git a/articles/tutorials/building_2d_games/18_texture_sampling/index.md b/articles/tutorials/building_2d_games/18_texture_sampling/index.md index a19a4550..f5890816 100644 --- a/articles/tutorials/building_2d_games/18_texture_sampling/index.md +++ b/articles/tutorials/building_2d_games/18_texture_sampling/index.md @@ -146,7 +146,7 @@ spriteBatch.Begin(samplerState: SamplerState.PointClamp); ## Adding a Scrolling Background to the Title Scene -We will now update title scene of our game by adding a scrolling background pattern using [**SamplerState.PointWrap**](xref:Microsoft.Xna.Framework.Graphics.SamplerState.PointWrap). By using the Wrap addressing mode, we can create a large scrolling background using only a small texture. When the texture is drawn with a destination rectangle larger than the texture itself, the Wrap mode will automatically tile the texture to fill the space. By adjusting the source rectangle over time, we can create a scrolling effect with minimal effort. +We will now update the title scene of our game by adding a scrolling background pattern using [**SamplerState.PointWrap**](xref:Microsoft.Xna.Framework.Graphics.SamplerState.PointWrap). By using the Wrap addressing mode, we can create a large scrolling background using only a small texture. When the texture is drawn with a destination rectangle larger than the texture itself, the Wrap mode will automatically tile the texture to fill the space. By adjusting the source rectangle over time, we can create a scrolling effect with minimal effort. First, download the following image of a repeatable background pattern by right-clicking it and saving it as `background-pattern.png` in the `Content/images` folder of the game project: diff --git a/articles/tutorials/building_2d_games/18_texture_sampling/snippets/titlescene.cs b/articles/tutorials/building_2d_games/18_texture_sampling/snippets/titlescene.cs index e3d9c982..d4747c1c 100644 --- a/articles/tutorials/building_2d_games/18_texture_sampling/snippets/titlescene.cs +++ b/articles/tutorials/building_2d_games/18_texture_sampling/snippets/titlescene.cs @@ -73,11 +73,11 @@ public override void Initialize() _pressEnterPos = new Vector2(640, 620); _pressEnterOrigin = size * 0.5f; - // Initialize the offset of the background pattern at zero + // Initialize the offset of the background pattern at zero. _backgroundOffset = Vector2.Zero; // Set the background pattern destination rectangle to fill the entire - // screen background + // screen background. _backgroundDestination = Core.GraphicsDevice.PresentationParameters.Bounds; } @@ -86,7 +86,7 @@ public override void LoadContent() // Load the font for the standard text. _font = Core.Content.Load("fonts/04B_30"); - // Load the font for the title text + // Load the font for the title text. _font5x = Content.Load("fonts/04B_30_5x"); // Load the background pattern texture. @@ -108,7 +108,7 @@ public override void Update(GameTime gameTime) _backgroundOffset.Y -= offset; // Ensure that the offsets do not go beyond the texture bounds so it is - // a seamless wrap + // a seamless wrap. _backgroundOffset.X %= _backgroundPattern.Width; _backgroundOffset.Y %= _backgroundPattern.Height; } @@ -129,20 +129,20 @@ public override void Draw(GameTime gameTime) Color dropShadowColor = Color.Black * 0.5f; // Draw the Dungeon text slightly offset from it is original position and - // with a transparent color to give it a drop shadow + // with a transparent color to give it a drop shadow. Core.SpriteBatch.DrawString(_font5x, DUNGEON_TEXT, _dungeonTextPos + new Vector2(10, 10), dropShadowColor, 0.0f, _dungeonTextOrigin, 1.0f, SpriteEffects.None, 1.0f); - // Draw the Dungeon text on top of that at its original position + // Draw the Dungeon text on top of that at its original position. Core.SpriteBatch.DrawString(_font5x, DUNGEON_TEXT, _dungeonTextPos, Color.White, 0.0f, _dungeonTextOrigin, 1.0f, SpriteEffects.None, 1.0f); // Draw the Slime text slightly offset from it is original position and - // with a transparent color to give it a drop shadow + // with a transparent color to give it a drop shadow. Core.SpriteBatch.DrawString(_font5x, SLIME_TEXT, _slimeTextPos + new Vector2(10, 10), dropShadowColor, 0.0f, _slimeTextOrigin, 1.0f, SpriteEffects.None, 1.0f); - // Draw the Slime text on top of that at its original position + // Draw the Slime text on top of that at its original position. Core.SpriteBatch.DrawString(_font5x, SLIME_TEXT, _slimeTextPos, Color.White, 0.0f, _slimeTextOrigin, 1.0f, SpriteEffects.None, 1.0f); - // Draw the press enter text + // Draw the press enter text. Core.SpriteBatch.DrawString(_font, PRESS_ENTER_TEXT, _pressEnterPos, Color.White, 0.0f, _pressEnterOrigin, 1.0f, SpriteEffects.None, 0.0f); // Always end the sprite batch when finished. diff --git a/articles/tutorials/building_2d_games/19_user_interface_fundamentals/index.md b/articles/tutorials/building_2d_games/19_user_interface_fundamentals/index.md index 760290b3..8344b8cf 100644 --- a/articles/tutorials/building_2d_games/19_user_interface_fundamentals/index.md +++ b/articles/tutorials/building_2d_games/19_user_interface_fundamentals/index.md @@ -17,7 +17,7 @@ We will first start by understanding what a user interface is and how it functio ## Understanding Game User Interfaces -A user interface in games serves as a bridge between the player and the game's systems. Well designed UIs help players navigate the game's mechanics, understand their current status, and make informed decisions. For new game developers, understanding UI principles is crucial because even the most mechanically sound game can fail if players ca not effectively interact with it. +A user interface in games serves as a bridge between the player and the game's systems. Well designed UIs help players navigate the game's mechanics, understand their current status, and make informed decisions. For new game developers, understanding UI principles is crucial because even the most mechanically sound game can fail if players can not effectively interact with it. Game UIs consist of various visual elements that serve different purposes: @@ -46,7 +46,7 @@ For example, a settings panel might contain multiple buttons, labels, and slider - **Inheritance of Properties**: Child elements can automatically inherit certain properties from their parents. For instance, if a parent element is hidden or disabled, all its children can be hidden or disabled as well. This cascading behavior simplifies state management across complex interfaces. - **Relative Positioning**: Child elements can be positioned relative to their parents rather than relative to the screen. This means you can place elements within a container and then move the entire container as a unit without having to update each child's position individually. -- **Simplified State Management**: Actions on parent elements can automatically propagate to their children. For example, disabling a menu panel can automatically disable all buttons within it, preventing interaction with elements that should be active. +- **Simplified State Management**: Actions on parent elements can automatically propagate to their children. For example, disabling a menu panel can automatically disable all buttons within it, preventing interaction with elements that should not be active. - **Batch Operations**: Operations like drawing and updating can be performed on a parent element and automatically cascade to all children, reducing the need for repetitive code. - **Logical Grouping**: The hierarchy naturally models the conceptual grouping of UI elements, making the code structure more intuitive and easier to maintain. @@ -124,7 +124,7 @@ In the next chapter, we will put these concepts into practice by implementing a - **Non-diegetic UI**: Elements that exist outside the game world, overlaid on top of gameplay (like traditional menus, health bars in screen corners, score displays). While less immersive, they are often clearer and easier to read. ::: -2. What are the some advantages of using a parent-child relationship in UI systems? +2. What are some advantages of using a parent-child relationship in UI systems? :::question-answer - **Inheritance of properties**: visual states cascade parent to children. diff --git a/articles/tutorials/building_2d_games/20_implementing_ui_with_gum/index.md b/articles/tutorials/building_2d_games/20_implementing_ui_with_gum/index.md index e947c249..5a1bffd7 100644 --- a/articles/tutorials/building_2d_games/20_implementing_ui_with_gum/index.md +++ b/articles/tutorials/building_2d_games/20_implementing_ui_with_gum/index.md @@ -294,7 +294,7 @@ startButton.Visual.Width = 100; Direct property assignment works well for initial setup, such as positioning elements or setting their dimensions when first creating your UI. However, when you need visual elements to respond to user interactions (like highlighting a button when it is focused), a different approach is required. -For these dynamic changes, Gum uses a system of **states** (implemented as `StateSave` objects), each Forms control maintains a collection of named states that are automatically applied in response to specific user interactions. When a button becomes focused, for instance, Gum looks for an applies a state named "Focused" to alter its appearance. +For these dynamic changes, Gum uses a system of **states** (implemented as `StateSave` objects), each Forms control maintains a collection of named states that are automatically applied in response to specific user interactions. When a button becomes focused, for instance, Gum looks for and applies a state named "Focused" to alter its appearance. > [!NOTE] > In the next chapter during the customization pass, we will create states to visually indicate when controls are focused, providing clear feedback to the player. @@ -349,11 +349,11 @@ To add the Gum NuGet package using the dotnet CLI: > You can verify the package was successfully added by examining your `DungeonSlime.csproj` file, which should now contain a reference like: > > ```xml -> +> > ``` > [!IMPORTANT] -> This tutorial uses version `2025.5.5.1` of Gum, which is the latest version of Gum as of this writing. That exact version is specified to use in the section above when installing the NuGet package to ensure compatibility throughout this tutorial. If there are newer versions of Gum available, please consult the [Gum documentation](https://docs.flatredball.com/gum/gum-tool/breaking-changes) before updating in case there are any breaking changes from the code that is presented in this tutorial. +> This tutorial uses version `2025.5.1.1` of Gum, which is the latest version of Gum as of this writing. That exact version is specified to use in the section above when installing the NuGet package to ensure compatibility throughout this tutorial. If there are newer versions of Gum available, please consult the [Gum documentation](https://docs.flatredball.com/gum/gum-tool/breaking-changes) before updating in case there are any breaking changes from the code that is presented in this tutorial. ### Adding UI Sound Effect @@ -403,7 +403,7 @@ The following is a breakdown of this initialization process: 2. **Content Loading**: Gum needs to be made aware of which content manager to use to load assets through the content pipeline. By setting `GumService.Default.ContentLoader.XnaContentManager = Core.Content`, we tell Gum to use our game's content manager when loading assets. By using the game's existing content manager, Gum also gets the benefit of the caching that the content manager performs when loading assets. 3. **Input Configuration**: * By default, all Forms controls automatically respond to mouse and touch screen input devices. We need to explicitly register keyboard and gamepad input devices by using th `FrameworkElement.KeyboardsForUiControl` and `Framework.GamePadsForUiControl` properties. - * By default, Forms controls will automatically respond to tab and shift-tab for navigation. By using the `FrameworkElement.TabKeyCombos` and `FrameworkElement.TabReverseKeyCombos` properties, we can add additional key combinations for tabbing. Here map the Up arrow for reverse tabbing and the Down arrow for forward tabbing. + * By default, Forms controls will automatically respond to tab and shift-tab for navigation. By using the `FrameworkElement.TabKeyCombos` and `FrameworkElement.TabReverseKeyCombos` properties, we can add additional key combinations for tabbing. Here we map the Up arrow for reverse tabbing and the Down arrow for forward tabbing. > [!TIP] > If you prefer different navigation keys, you can remove the built-in Tab/Shift+Tab navigation. @@ -451,7 +451,7 @@ Each button registers a `Click` event handler to respond when the players select [!code-csharp[](./snippets/titlescene/handlestartclicked.cs)] -When the "Start" button is clicked and this method is called, it will play the UI sound effect for auditory feedback then change the scene tot he game scene so the player can start playing the game. +When the "Start" button is clicked and this method is called, it will play the UI sound effect for auditory feedback then change the scene to the game scene so the player can start playing the game. Next is the handler for the "Options" button. Add the following method to the `TitleScene` class after the `HandleStartClicked` method: @@ -481,7 +481,7 @@ Next is the handler when the "Sound Effects Volume" slider has completed a value When the value of the "Sound Effects Volume" slider has completed a change and this method is called, it plays the UI sound effect to provide auditory feedback so the player can hear the difference in volume. -After this is the handler for when the "Music Volume" slider value changes. Add the following method to the `TitleScene` class after the `HandleSfxSliderChangeCompleted` method: +Next, add the handler for changes to the 'Music Volume' slider. Place the following method in the TitleScene class, directly after the HandleSfxSliderChangeCompleted method: [!code-csharp[](./snippets/titlescene/handlemusicslidervaluechanged.cs)] diff --git a/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md b/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md index 7aeee8b2..2891a43b 100644 --- a/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md +++ b/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md @@ -42,7 +42,7 @@ For example, if the snake is moving to the right, an invalid input would allow a ### Movement Cycle -Instead of moving every update frame as a directional input is being pressed, the snake instead only moves during regular timed intervals.  A timer is used to determine how much time has passed since the last movement cycle, and when it reaches a set threshold, the next movement cycle occurs.  During this movement cycle, the snake should move forward in the direction that was input by the player between the last and current movement cycles.  This creates the grid-based movement system typically found in snake-like games. +Instead of moving every update frame while a directional input is being pressed, the snake moves only at fixed time intervals.  A timer is used to determine how much time has passed since the last movement cycle, and when it reaches a set threshold, the next movement cycle occurs.  During this movement cycle, the snake should move forward in the direction that was input by the player between the last and current movement cycles.  This creates the grid-based movement system typically found in snake-like games. There are various methods for handling the movement, such as iterating through each segment of the snake and updating the position of that segment to move forward.  Methods such as this though are wasteful, since visually the only parts of the snake that move on the screen are the head and the tail.   @@ -107,7 +107,7 @@ This separation provides several benefits, including: 1. **Input Device Independence**: The game logic does not need to know which input device the player is using. Whether they are playing with a keyboard, gamepad, or touch screen, the game only cares that a "move up" action was triggered, not which specific button or key caused it. 2. **Simplified Input Handling**: Instead of checking multiple input combinations throughout the codebase, game objects can simply ask "Should I move up?" through a clean API call. 3. **Easy Rebinding**: If you want to add key rebinding features, you only need to modify the `GameController` class, not every piece of code that uses input. -4. **Consistent Input Logic**: The rules for determining if an action occurred (like checking if a button was just pressed version being held down) are defined in one place. +4. **Consistent Input Logic**: The rules for determining if an action occurred (like checking if a button was just pressed versus being held down) are defined in one place. 5. **Cross-Platform Compatibility**: When porting to different platforms with different input methods, you only need to update the `GameController` class to map the new input devices to your existing game actions. By implementing this pattern in our game, we are not only making our current input handling cleaner, but we are also establishing a foundation that would make it easier to add features like input customization or support for new input devices in the future. @@ -118,8 +118,8 @@ With our input handling system in place, now we can turn our attention to implem We will need to implement a structure that can represent each segment of the slime, this structure will store the position and movement data for each segment. -In the *DungeonSlime* project (your main game project), create a new directory named `GameObjects`. We will be putting all of our code related to the objects within the game here. -Then create a new file named `SlimeSegment.cs` inside the `GameObjects` directory you just created and add the following code: +In the *DungeonSlime* project (your main game project), create a new folder named `GameObjects`. We will be putting all of our code related to the objects within the game here. +Then create a new file named `SlimeSegment.cs` inside the `GameObjects` folder you just created and add the following code: [!code-csharp[](./snippets/slimesegment.cs)] @@ -142,7 +142,7 @@ By tracking both the current (`At`) and target (`To`) positions, we can implemen Next, we can implement a class to encapsulate the properties and functionality of our snake-like slime. -In the `GameObjects` directory of the *DungeonSlime* project (your main game project), create a new file named `Slime.cs` and add the following initial code: +In the `GameObjects` folder of the *DungeonSlime* project (your main game project), create a new file named `Slime.cs` and add the following initial code: [!code-csharp[](./snippets/slime/definition.cs)] @@ -249,7 +249,7 @@ Add the following method to the `Slime` class after the `Move` method: [!code-csharp[](./snippets/slime/grow.cs)] -Th `Grow` method works as follows: +The `Grow` method works as follows: 1. First it creates a copy of the current tail value. 2. It then adjusts the values of the copy so that it is now positioned behind the current tail by using the `ReverseDirection` value of the tail. @@ -268,9 +268,9 @@ This update method: 1. Updates the slime's `AnimatedSprite` to ensure the sprite animations occur. 2. Calls `HandleInput` to check for player input. 3. Increments the movement timer by the amount of time that has elapsed between the game's update cycles. -4. Performs a check to see if the movement timer has accumulated more time than the threshold to perform a movement cycle update.  If it has then: -   1. The movement timer is reduced by the threshold time. -   2. The `Move` method is called to perform a movement cycle update. +4. Performs a check to see if the movement timer has accumulated more time than the threshold to perform a movement cycle update. If it has then: + - The movement timer is reduced by the threshold time. + - The `Move` method is called to perform a movement cycle update. 5. Finally, the movement progress amount is calculated by dividing the number of seconds accumulated for the movement timer by the number of seconds for the threshold.  This gives us a normalized value between 0.0 and 1.0 that we can use for visual interpolation for fluid movement. > [!TIP] @@ -339,7 +339,7 @@ Now that we have our player-controlled character implemented, we can create the ### The Bat Class -In the `GameObjects` directory of the *DungeonSlime* project (your main game project), create a new file named `Bat.cs` and add the following initial code: +In the `GameObjects` folder of the *DungeonSlime* project (your main game project), create a new file named `Bat.cs` and add the following initial code: [!code-csharp[](./snippets/bat/definition.cs)] diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/index.md b/articles/tutorials/building_2d_games/23_completing_the_game/index.md index 697ae71f..43b3c42d 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/index.md +++ b/articles/tutorials/building_2d_games/23_completing_the_game/index.md @@ -18,7 +18,7 @@ In this chapter, you will: Currently, the `GameScene` class contains the methods for initializing and creating the pause menu.  However, now that we have a defined condition for game over, we need to create a game-over menu as well.  To do this, we will take the opportunity to refactor the current code and pull the UI-specific code into its own class. -In the `UI` directory of the *DungeonSlime* project, create a new file named `GameSceneUI.cs` and add the following initial code: +In the `UI` folder of the *DungeonSlime* project, create a new file named `GameSceneUI.cs` and add the following initial code: [!code-csharp[](./snippets/gamesceneui/definition.cs)] @@ -131,7 +131,7 @@ Now that we have created the encapsulated [`Slime`](../22_snake_game_mechanics/i We will rebuild/replace the existing `GameScene` class to coordinate the interactions between the components. -In the `Scenes` directory of the *DungeonSlime* project (your main game project), open the `GameScene.cs` file and replace **ALL** of the code with the following replacement code (starting fresh): +In the `Scenes` folder of the *DungeonSlime* project (your main game project), open the `GameScene.cs` file and replace **ALL** of the code with the following replacement code (starting fresh): [!code-csharp[](./snippets/gamescene/definition.cs)] @@ -220,7 +220,7 @@ Add the following method to the `GameScene` class after the `InitializeNewGame` This method loads all necessary assets for the game scene: -1. The texture atlas containing the sprite graphics +1. The texture atlas containing the sprite graphics. 2. The tilemap that defines the level layout. 3. The animated sprites for the slime and bat. 4. Sound effects for the bat bouncing and collecting. @@ -242,7 +242,7 @@ This method updates the scene in each frame to: ### GameScene CollisionChecks Method -In the `Update` method we just added, it makes a call to a `CollisionChecks` method to handle the collision detection and response so we will add tha now. +In the `Update` method we just added, it makes a call to a `CollisionChecks` method to handle the collision detection and response so we will add that now. Add the following method to the `GameScene` class after the `Update` method: @@ -390,7 +390,7 @@ In this chapter, we have transformed our technical demo into a complete game by - Implemented pause and game over screens that provide clear feedback to the player. - Refactored the `GameScene` class to coordinate all game components. - Added game state management to handle different gameplay conditions. -- Enhanced player control through input buffering for more responsive gameplay +- Enhanced player control through input buffering for more responsive gameplay. - Connected all of the elements to create a complete playable game. The refactoring process we undertook demonstrates an important game development principle: separating concerns into specialized components makes code more maintainable and easier to extend. The `Slime` class manages snake-like behavior, the `Bat` class handles movement and collision response, and the `GameSceneUI` class encapsulates all UI-related functionality. diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/collisionchecks.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/collisionchecks.cs index 7fc83597..67c408f3 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/collisionchecks.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/collisionchecks.cs @@ -1,6 +1,6 @@ private void CollisionChecks() { - // Capture the current bounds of the slime and bat + // Capture the current bounds of the slime and bat. Circle slimeBounds = _slime.GetBounds(); Circle batBounds = _bat.GetBounds(); @@ -23,7 +23,7 @@ private void CollisionChecks() // Update the score display on the UI. _ui.UpdateScoreText(_score); - // Play the collect sound effect + // Play the collect sound effect. Core.Audio.PlaySoundEffect(_collectSoundEffect); } diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/draw.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/draw.cs index d27a31dd..053eb528 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/draw.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/draw.cs @@ -18,6 +18,6 @@ public override void Draw(GameTime gameTime) // Always end the sprite batch when finished. Core.SpriteBatch.End(); - // Draw the UI + // Draw the UI. _ui.Draw(); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/eventhandlers.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/eventhandlers.cs index 806f7391..ca3eb45b 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/eventhandlers.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/eventhandlers.cs @@ -1,17 +1,17 @@ private void OnResumeButtonClicked(object sender, EventArgs args) { - // Change the game state back to playing + // Change the game state back to playing. _state = GameState.Playing; } private void OnRetryButtonClicked(object sender, EventArgs args) { - // Player has chosen to retry, so initialize a new game + // Player has chosen to retry, so initialize a new game. InitializeNewGame(); } private void OnQuitButtonClicked(object sender, EventArgs args) { - // Player has chosen to quit, so return back to the title scene + // Player has chosen to quit, so return back to the title scene. Core.ChangeScene(new TitleScene()); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initialize.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initialize.cs index ed935d06..1151b660 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initialize.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initialize.cs @@ -4,7 +4,7 @@ public override void Initialize() base.Initialize(); // During the game scene, we want to disable exit on escape. Instead, - // the escape key will be used to return back to the title screen + // the escape key will be used to return back to the title screen. Core.ExitOnEscape = false; // Create the room bounds by getting the bounds of the screen then @@ -19,7 +19,7 @@ public override void Initialize() _slime.BodyCollision += OnSlimeBodyCollision; // Create any UI elements from the root element created in previous - // scenes + // scenes. GumService.Default.Root.Children.Clear(); // Initialize the user interface for the game scene. diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initializenewgame.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initializenewgame.cs index cd6e1ade..36fc54b6 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initializenewgame.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/initializenewgame.cs @@ -6,16 +6,16 @@ private void InitializeNewGame() slimePos.X = (_tilemap.Columns / 2) * _tilemap.TileWidth; slimePos.Y = (_tilemap.Rows / 2) * _tilemap.TileHeight; - // Initialize the slime + // Initialize the slime. _slime.Initialize(slimePos, _tilemap.TileWidth); - // Initialize the bat + // Initialize the bat. _bat.RandomizeVelocity(); PositionBatAwayFromSlime(); - // Reset the score + // Reset the score. _score = 0; - // Set the game state to playing + // Set the game state to playing. _state = GameState.Playing; } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/loadcontent.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/loadcontent.cs index 58b2bb92..9b31e1b2 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/loadcontent.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/loadcontent.cs @@ -1,6 +1,6 @@ public override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Core.Content, "images/atlas-definition.xml"); // Create the tilemap from the XML configuration file. @@ -11,19 +11,19 @@ public override void LoadContent() AnimatedSprite slimeAnimation = atlas.CreateAnimatedSprite("slime-animation"); slimeAnimation.Scale = new Vector2(4.0f, 4.0f); - // Create the slime + // Create the slime. _slime = new Slime(slimeAnimation); // Create the animated sprite for the bat from the atlas. AnimatedSprite batAnimation = atlas.CreateAnimatedSprite("bat-animation"); batAnimation.Scale = new Vector2(4.0f, 4.0f); - // Load the bounce sound effect for the bat + // Load the bounce sound effect for the bat. SoundEffect bounceSoundEffect = Content.Load("audio/bounce"); - // Create the bat + // Create the bat. _bat = new Bat(batAnimation, bounceSoundEffect); - // Load the collect sound effect + // Load the collect sound effect. _collectSoundEffect = Content.Load("audio/collect"); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/positionbatawayfromslime.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/positionbatawayfromslime.cs index effc8599..4ba13787 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/positionbatawayfromslime.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/positionbatawayfromslime.cs @@ -6,7 +6,7 @@ private void PositionBatAwayFromSlime() float roomCenterY = _roomBounds.Y + _roomBounds.Height * 0.5f; Vector2 roomCenter = new Vector2(roomCenterX, roomCenterY); - // Get the bounds of the slime and calculate the center position + // Get the bounds of the slime and calculate the center position. Circle slimeBounds = _slime.GetBounds(); Vector2 slimeCenter = new Vector2(slimeBounds.X, slimeBounds.Y); @@ -14,7 +14,7 @@ private void PositionBatAwayFromSlime() // center of the slime. Vector2 centerToSlime = slimeCenter - roomCenter; - // Get the bounds of the bat + // Get the bounds of the bat. Circle batBounds =_bat.GetBounds(); // Calculate the amount of padding we will add to the new position of @@ -37,7 +37,7 @@ private void PositionBatAwayFromSlime() if (centerToSlime.X > 0) { // The slime is closer to the right side wall, so place the - // bat on the left side wall + // bat on the left side wall. newBatPosition.X = _roomBounds.Left + padding; } else @@ -60,7 +60,7 @@ private void PositionBatAwayFromSlime() if (centerToSlime.Y > 0) { // The slime is closer to the top wall, so place the bat on the - // bottom wall + // bottom wall. newBatPosition.Y = _roomBounds.Top + padding; } else @@ -71,6 +71,6 @@ private void PositionBatAwayFromSlime() } } - // Assign the new bat position + // Assign the new bat position. _bat.Position = newBatPosition; } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/statechanges.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/statechanges.cs index 1d8f7d8a..b194cabf 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/statechanges.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/statechanges.cs @@ -7,27 +7,27 @@ private void TogglePause() { if (_state == GameState.Paused) { - // We're now unpausing the game, so hide the pause panel + // We're now unpausing the game, so hide the pause panel. _ui.HidePausePanel(); - // And set the state back to playing + // And set the state back to playing. _state = GameState.Playing; } else { - // We're now pausing the game, so show the pause panel + // We're now pausing the game, so show the pause panel. _ui.ShowPausePanel(); - // And set the state to paused + // And set the state to paused. _state = GameState.Paused; } } private void GameOver() { - // Show the game over panel + // Show the game over panel. _ui.ShowGameOverPanel(); - // Set the game state to game over + // Set the game state to game over. _state = GameState.GameOver; } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/update.cs b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/update.cs index 5db7f901..d8a15a55 100644 --- a/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/update.cs +++ b/articles/tutorials/building_2d_games/23_completing_the_game/snippets/gamescene/update.cs @@ -1,33 +1,33 @@ public override void Update(GameTime gameTime) { - // Ensure the UI is always updated + // Ensure the UI is always updated. _ui.Update(gameTime); // If the game is in a game over state, immediately return back - // here + // here. if (_state == GameState.GameOver) { return; } - // If the pause button is pressed, toggle the pause state + // If the pause button is pressed, toggle the pause state. if (GameController.Pause()) { TogglePause(); } - // At this point, if the game is paused, just return back early + // At this point, if the game is paused, just return back early. if (_state == GameState.Paused) { return; } - // Update the slime; + // Update the slime. _slime.Update(gameTime); - // Update the bat; + // Update the bat. _bat.Update(gameTime); - // Perform collision checks + // Perform collision checks. CollisionChecks(); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/24_shaders/index.md b/articles/tutorials/building_2d_games/24_shaders/index.md index fb9e0036..a1c53a3a 100644 --- a/articles/tutorials/building_2d_games/24_shaders/index.md +++ b/articles/tutorials/building_2d_games/24_shaders/index.md @@ -18,7 +18,7 @@ In this chapter, you will: > > If you want to learn more about the shader language itself, a good place to start would be the [High-level shader language (HLSL)](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl) documentation on Microsoft Learn. > -> For inspiration on what can be achieved with shaders, check out [ShaderToy](https://www.shadertoy.com), which showcases real-time shader effects created by others. Note that ShaderToy uses [OpenGL Shading Language (GLSL)](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language) which has some syntactic differences from HLSL, but the underlying concepts and mathematics are very similar for inspiration. +> For inspiration on what can be achieved with shaders, check out [ShaderToy](https://www.shadertoy.com), which showcases real-time shader effects created by others. Note that ShaderToy uses [OpenGL Shading Language (GLSL)](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language) which has some syntactic differences from HLSL, but the underlying concepts and mathematics are very similar. We can begin by understanding what shaders are and how they work in MonoGame. @@ -59,7 +59,7 @@ Pixel shaders are useful in 2D games for creating effects like: For our Dungeon Slime game, we will focus primarily on pixel shaders since we want to create a color effect for our game over state. > [!NOTE] -> There are other types of shaders beyond vertex and pixel shaders, such as compute shaders, geometry shaders, and hull/domain shaders. These more advanced shader types enabled powerful features like physics simulations, procedural geometry, and complex post-processing effects. However, they are not currently supported in the standard MonoGame implementation and are beyond the scope of this beginner tutorial. As the MonoGame graphics pipeline evolves, support for these advanced shader types may be added in future versions. +> There are other types of shaders beyond vertex and pixel shaders, such as compute shaders, geometry shaders, and hull/domain shaders. These more advanced shader types enable powerful features like physics simulations, procedural geometry, and complex post-processing effects. However, they are not currently supported in the standard MonoGame implementation and are beyond the scope of this beginner tutorial. As the MonoGame graphics pipeline evolves, support for these advanced shader types may be added in future versions. ### The Shader Pipeline @@ -257,7 +257,7 @@ First, we need to create a new shader effect file and add it to our content proj 3. Give the new folder the name `effects` and click the `Ok` button. 4. Right-click on the new `effects` folder in the MGCB Editor and choose `Add > New Item...`. 5. Choose `Sprite Effect (.fx)` from the type list and name the file `grayscaleEffect`, then click the `Ok` button. -6. **Save the changes in the MGCB Editor the close it.** +6. **Save the changes in the MGCB Editor then close it.** The steps above will create a new shader effect (*.fx*) file with the default template we discussed earlier. Now, we need to modify this template to create our grayscale effect. @@ -340,7 +340,7 @@ Now that we have our grayscale shader, we can implement it in our game when the [!code-csharp[](./snippets/gamescene/draw.cs?highlight=6-18)] > [!NOTE] - > Notice how we set the shader parameters with the current saturation value every frame before beginning the sprite batch. This is because shaders are stateless; they do not remember any values from the previous draw cycle. Each time the GPU processes a shader, it only works with the parameters provided in that specific frame. Event if the saturation value has not changed since the last frame, we still need to send it to the shader again to apply it. This is why we constantly update the shader parameters in the `Draw` method rather than only when the value is changed. + > Notice how we set the shader parameters with the current saturation value every frame before beginning the sprite batch. This is because shaders are stateless; they do not remember any values from the previous draw cycle. Each time the GPU processes a shader, it only works with the parameters provided in that specific frame. Even if the saturation value has not changed since the last frame, we still need to send it to the shader again to apply it. This is why we constantly update the shader parameters in the `Draw` method rather than only when the value is changed. With these changes, when the game enters a paused or game over state, the screen will gradually fade to gray using the grayscale shader effect. This provides a clear indication that the game is inactive during these states. @@ -362,7 +362,7 @@ When working with effects in [**SpriteBatch**](xref:Microsoft.Xna.Framework.Grap // Begins sprite batch with a different effect. All draw calls made within this begin/end block will have the specified effect applied. spriteBatch.Begin(effect: exampleEffect2) - spriteBatch.DrawS(texture, position, color); + spriteBatch.Draw(texture, position, color); spriteBatch.End(); ``` diff --git a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/draw.cs b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/draw.cs index dd4ed215..bede55e4 100644 --- a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/draw.cs +++ b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/draw.cs @@ -29,6 +29,6 @@ public override void Draw(GameTime gameTime) // Always end the sprite batch when finished. Core.SpriteBatch.End(); - // Draw the UI + // Draw the UI. _ui.Draw(); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/fields.cs b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/fields.cs index acd8e25f..0bb4fa9f 100644 --- a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/fields.cs +++ b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/fields.cs @@ -1,7 +1,7 @@ // The grayscale shader effect. private Effect _grayscaleEffect; -// The amount of saturation to provide the grayscale shader effect +// The amount of saturation to provide the grayscale shader effect. private float _saturation = 1.0f; // The speed of the fade to grayscale effect. diff --git a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/gameover.cs b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/gameover.cs index 6abf1269..e417ebbf 100644 --- a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/gameover.cs +++ b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/gameover.cs @@ -1,11 +1,11 @@ private void GameOver() { - // Show the game over panel + // Show the game over panel. _ui.ShowGameOverPanel(); - // Set the game state to game over + // Set the game state to game over. _state = GameState.GameOver; - // Set the grayscale effect saturation to 1.0f; + // Set the grayscale effect saturation to 1.0f _saturation = 1.0f; } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/loadcontent.cs b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/loadcontent.cs index 5919ff4f..265ddeb0 100644 --- a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/loadcontent.cs +++ b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/loadcontent.cs @@ -1,6 +1,6 @@ public override void LoadContent() { - // Create the texture atlas from the XML configuration file + // Create the texture atlas from the XML configuration file. TextureAtlas atlas = TextureAtlas.FromFile(Core.Content, "images/atlas-definition.xml"); // Create the tilemap from the XML configuration file. @@ -11,22 +11,22 @@ public override void LoadContent() AnimatedSprite slimeAnimation = atlas.CreateAnimatedSprite("slime-animation"); slimeAnimation.Scale = new Vector2(4.0f, 4.0f); - // Create the slime + // Create the slime. _slime = new Slime(slimeAnimation); // Create the animated sprite for the bat from the atlas. AnimatedSprite batAnimation = atlas.CreateAnimatedSprite("bat-animation"); batAnimation.Scale = new Vector2(4.0f, 4.0f); - // Load the bounce sound effect for the bat + // Load the bounce sound effect for the bat. SoundEffect bounceSoundEffect = Content.Load("audio/bounce"); - // Create the bat + // Create the bat. _bat = new Bat(batAnimation, bounceSoundEffect); - // Load the collect sound effect + // Load the collect sound effect. _collectSoundEffect = Content.Load("audio/collect"); - // Load the grayscale effect + // Load the grayscale effect. _grayscaleEffect = Content.Load("effects/grayscaleEffect"); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/togglepause.cs b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/togglepause.cs index 4fca024c..d6361bd6 100644 --- a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/togglepause.cs +++ b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/togglepause.cs @@ -2,21 +2,21 @@ private void TogglePause() { if (_state == GameState.Paused) { - // We're now unpausing the game, so hide the pause panel + // We're now unpausing the game, so hide the pause panel. _ui.HidePausePanel(); - // And set the state back to playing + // And set the state back to playing. _state = GameState.Playing; } else { - // We're now pausing the game, so show the pause panel + // We're now pausing the game, so show the pause panel. _ui.ShowPausePanel(); - // And set the state to paused + // And set the state to paused. _state = GameState.Paused; - // Set the grayscale effect saturation to 1.0f; + // Set the grayscale effect saturation to 1.0f _saturation = 1.0f; } } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/update.cs b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/update.cs index 0786f32e..be4627d3 100644 --- a/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/update.cs +++ b/articles/tutorials/building_2d_games/24_shaders/snippets/gamescene/update.cs @@ -1,6 +1,6 @@ public override void Update(GameTime gameTime) { - // Ensure the UI is always updated + // Ensure the UI is always updated. _ui.Update(gameTime); if (_state != GameState.Playing) @@ -9,31 +9,31 @@ public override void Update(GameTime gameTime) // gradually decrease the saturation to create the fading grayscale. _saturation = Math.Max(0.0f, _saturation - FADE_SPEED); - // If its just a game over state, return back + // If its just a game over state, return back. if (_state == GameState.GameOver) { return; } } - // If the pause button is pressed, toggle the pause state + // If the pause button is pressed, toggle the pause state. if (GameController.Pause()) { TogglePause(); } - // At this point, if the game is paused, just return back early + // At this point, if the game is paused, just return back early. if (_state == GameState.Paused) { return; } - // Update the slime; + // Update the slime. _slime.Update(gameTime); - // Update the bat; + // Update the bat. _bat.Update(gameTime); - // Perform collision checks + // Perform collision checks. CollisionChecks(); } \ No newline at end of file diff --git a/articles/tutorials/building_2d_games/24_shaders/snippets/grayscaleeffect.fx b/articles/tutorials/building_2d_games/24_shaders/snippets/grayscaleeffect.fx index 2454ef19..65723c2e 100644 --- a/articles/tutorials/building_2d_games/24_shaders/snippets/grayscaleeffect.fx +++ b/articles/tutorials/building_2d_games/24_shaders/snippets/grayscaleeffect.fx @@ -30,7 +30,7 @@ float4 MainPS(VertexShaderOutput input) : COLOR // Sample the texture float4 color = tex2D(SpriteTextureSampler, input.TextureCoordinates) * input.Color; - // Calculate the grayscale value based on human perception of colors + // Calculate the grayscale value based on human perception of colors. float grayscale = dot(color.rgb, float3(0.3, 0.59, 0.11)); // create a grayscale color vector (same value for R, G, and B) @@ -40,7 +40,7 @@ float4 MainPS(VertexShaderOutput input) : COLOR // rgb values based on the saturation parameter. float3 finalColor = lerp(grayscale, color.rgb, Saturation); - // Return the final color with the original alpha value + // Return the final color with the original alpha value. return float4(finalColor, color.a); } diff --git a/articles/tutorials/building_2d_games/25_packaging_game/index.md b/articles/tutorials/building_2d_games/25_packaging_game/index.md index 4eb1cfa0..f8e8abd3 100644 --- a/articles/tutorials/building_2d_games/25_packaging_game/index.md +++ b/articles/tutorials/building_2d_games/25_packaging_game/index.md @@ -3,7 +3,7 @@ title: "Chapter 25: Packaging Your Game for Distribution" description: "Learn how to package your game for distribution across Windows, macOS, and Linux platforms." --- -After all of our work creating Dungeon Slime, we need to prepare the game for distribution to players. Properly packaging your game ensure it runs correctly on different platforms without requiring players to have development tools installed. +After all of our work creating Dungeon Slime, we need to prepare the game for distribution to players. Properly packaging your game ensures it runs correctly on different platforms without requiring players to have development tools installed. In this chapter you will: @@ -36,7 +36,7 @@ Before packaging your game for distribution, you should take some preparatory st 1. **Set Release Configuration**: Ensure your build configuration is set to "Release" rather than "Debug" for better performance and smaller executable size. 2. **Update Game Information**: Verify your game's title, version, and other information in the project's properties file (`.csproj`). -3. **Final Testing**: Perform thorough testing in Release mode to catch any issue that might not appear in Debug mode. +3. **Final Testing**: Perform thorough testing in Release mode to catch any issues that might not appear in Debug mode. 4. **Asset Optimization**: Consider optimizing larger content files to reduce the final package size. ## Platform-Specific Packaging @@ -54,7 +54,7 @@ Windows is the most straightforward platform to target since MonoGame developmen #### Building for Windows -To create a self-contained application for Window, open a new command prompt window in the same folder as the as the main game project (in our case the folder with the `DungeonSlime.csproj` file) and execute the following .NET CLI command: +To create a self-contained application for Windows, open a new command prompt window in the same folder as the main game project (in our case the folder with the `DungeonSlime.csproj` file) and execute the following .NET CLI command: ```sh dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=false -p:TieredCompilation=false --self-contained @@ -91,7 +91,7 @@ Packaging for macOS requires creating an **Application Bundle** (`.app`), which #### Building for macOS -For macOS, you will need to build for both the Intel (x64) and Apple Silicon (arm64) to support all modern mac computers. Open a new terminal window in the same folder as the `DungeonSlime.csproj` file (the main game project). +For macOS, you will need to build for both the Intel (x64) and Apple Silicon (arm64) to support all modern Mac computers. Open a new terminal window in the same folder as the `DungeonSlime.csproj` file (the main game project). > [!TIP] > The following sections will guide you through several terminal commands that build on each other. It is best to use a single terminal window located in your projects root directory (where the `DungeonSlime.csproj` file is) for all of these steps to ensure paths remain consistent. @@ -233,7 +233,7 @@ To create this structure, from the same terminal window: mkdir -p bin/Release/DungeonSlime.iconset ``` - 3. Now we use the `sips` command to generate the icon for each size required for a mac app bundle. Each size generated is neccessary for different display scenarios in macOS (Dock, Finder, etc.). To do this, execute the following commands: + 3. Now we use the `sips` command to generate the icon for each size required for a mac app bundle. Each size generated is necessary for different display scenarios in macOS (Dock, Finder, etc.). To do this, execute the following commands: ```sh sips -z 16 16 Icon.png --out bin/Release/DungeonSlime.iconset/icon_16x16.png @@ -319,7 +319,7 @@ The output will be placed in a directory like `bin/Release/net8.0/linux-x64/publ Once you have created a build for Linux, to create a distributable archive: -1. Ensure the main executable has proper execute permissions by executing the following command in the same terminal window: +1. Ensure the main executable has proper execution permissions by executing the following command in the same terminal window: ```sh chmod +x bin/Release/net8.0/linux-x64/publish/DungeonSlime @@ -391,9 +391,9 @@ Trimming (specified with `-p:Trimming:true`) removes unused code from your distr While trimming can significantly reduce your game's size, it may remove types that appear unused but are accessed indirectly through reflection or generics causing runtime errors. > [!IMPORTANT] -> Trimming can cause issues with content pipeline extensions that are used at runtime. When the compiler cannot detect that certain types are used (especially with reflection or generic collections), thy might be trimmed away, resulting in "type not found" exceptions when loading content. +> Trimming can cause issues with content pipeline extensions that are used at runtime. When the compiler cannot detect that certain types are used (especially with reflection or generic collections), they might be trimmed away, resulting in "type not found" exceptions when loading content. > -> If you encounter runtime exceptions about missing types when loading content with trimming enabled, you can resolve this by ensuring the compiler recognizes the types being uset at runtime by making the following call: +> If you encounter runtime exceptions about missing types when loading content with trimming enabled, you can resolve this by ensuring the compiler recognizes the types being used at runtime by making the following call: > > ```cs > ContentTypeReaderManager.AddTypeCreator(typeof(ReflectiveReader).FullName, () => new ReflectiveReader()) @@ -407,7 +407,7 @@ For more information on Trimming, refer to the [Trim self-contained applications Single file publishing packages your entire application into a single executable. While this sounds convenient, it is essentially a self-extracting archive that extracts to a temporary directory at runtime. -This can significantly increase startup time for larger games and may fail on system with restricted permissions of limited storage. For this reason, it is not recommended to use this option for games. +This can significantly increase startup time for larger games and may fail on system with restricted permissions or limited storage. For this reason, it is not recommended to use this option for games. For more information on Single File Publishing, refer to the [Create a single file for application deployment](https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli) documentation on Microsoft Learn. @@ -445,7 +445,7 @@ Texture2D text = Content.Load("images/atlas"); Try to minimize external dependencies. If your game requires additional libraries or runtimes, document these requirements clearly for players. > [!NOTE] -> When publishing to distribution platforms and app stores (such as Steam, Epic Game Sore, App Store, or Google Play), you are typically required to disclose all external dependencies in your privacy policy or a dedicate dependencies section. This includes third-party libraries, analytics tools, and any software components that your game depends on. +> When publishing to distribution platforms and app stores (such as Steam, Epic Game Store, App Store, or Google Play), you are typically required to disclose all external dependencies in your privacy policy or a dedicated dependencies section. This includes third-party libraries, analytics tools, and any software components that your game depends on. > > Check specific requirements for each distribution platform you plant to target, as well as requirements by third-party libraries for using them, as disclosure requirements may vary. @@ -476,7 +476,7 @@ If you are interested in extending the Dungeon Slime game, or future games, to m ## Third-Party Packaging Tools -While the platform-specific packaging steps outlined in this chapter give you complete control over the distribution process, they require multiple commands and potentially access to different operating system. Fortunately, the MonoGame community has developed several tools that can automate these packaging steps across platforms. +While the platform-specific packaging steps outlined in this chapter give you complete control over the distribution process, they require multiple commands and potentially access to different operating systems. Fortunately, the MonoGame community has developed several tools that can automate these packaging steps across platforms. ### GameBundle diff --git a/articles/tutorials/building_2d_games/26_publish_to_itch/index.md b/articles/tutorials/building_2d_games/26_publish_to_itch/index.md index d67b1d4c..87aafb1d 100644 --- a/articles/tutorials/building_2d_games/26_publish_to_itch/index.md +++ b/articles/tutorials/building_2d_games/26_publish_to_itch/index.md @@ -58,7 +58,7 @@ To create a new project, click the arrow beside your username in the top-right c | :---------------------------------------------------------------------------------------------------------------------------------------------: | | **Figure 26-1: Drop-down menu after clicking the arrow beside user name with "Upload new project" highlighted** | -This will open the "Create a new project page", which presents you with a form to to enter information abut the game, upload the game files, and add screenshots. +This will open the "Create a new project page", which presents you with a form to enter information about the game, upload the game files, and add screenshots. | ![Figure 26-2: The "Create a new project" page on itch.io](./images/create-a-new-project-page.png) | | :------------------------------------------------------------------------------------------------: | @@ -391,7 +391,7 @@ To add screenshots to your project: 1. Prepare 3-5 screenshots showcasing different aspects of your game. 2. Click the "Add Screenshots" button located below the cover image section. 3. Select your screenshot files in the file browser dialog. -4. Arrange screenshots in order of importance by hovering over then and using the "Move Up" and "Move Down" actions after uploading. +4. Arrange screenshots in order of importance by hovering over them and using the "Move Up" and "Move Down" actions after uploading. | ![Figure 26-12: Example of multiple screenshots added to a game project](./images/screenshots-added.png) | | :------------------------------------------------------------------------------------------------------: | @@ -402,7 +402,7 @@ To add screenshots to your project: ### Saving and Publishing Your Project -After filling out all the necessary form fields, uploading your game files, and adding visual assets, it is time to save your project and prepare it for publishing. This process involves several steps designed to ensure your project is ready for your intended audience. +After filling out all the necessary form fields, uploading your game files, and adding visual assets, it is time to save your project and prepare it for publishing. This process involves several steps designed to ensure your project is ready for your intended audience before publishing. #### Initial Save and Preview diff --git a/articles/tutorials/building_2d_games/27_conclusion/index.md b/articles/tutorials/building_2d_games/27_conclusion/index.md index 9965a11c..d629a4ce 100644 --- a/articles/tutorials/building_2d_games/27_conclusion/index.md +++ b/articles/tutorials/building_2d_games/27_conclusion/index.md @@ -61,7 +61,7 @@ The MonoGame community represents one of the most valuable resources available t The official [MonoGame Discord server](https://discord.gg/MonoGame) offers a space where developers of all skill levels exchange ideas and troubleshoot challenges together. Here, a question about optimization techniques might spark a conversation about architecture patterns, leading to insights you might never have discovered on your own. -For structure learning, several community members have created comprehensive educational resources: +For structured learning, several community members have created comprehensive educational resources: - [RB Whitaker's](http://rbwhitaker.wikidot.com/MonoGame-getting-started-tutorials) provides in-depth coverage of MonoGame fundamentals and advanced topics. - [Learn MonoGame](https://learn-MonoGame.github.io/) by community member [Jean-David Moisan (Apos)](https://github.com/Apostolique) provides a collection of focused tutorials on specific MonoGame topics. @@ -72,21 +72,21 @@ For structure learning, several community members have created comprehensive edu The [MonoGame Samples](https://github.com/MonoGame/MonoGame.Samples) repository offers practical examples of features implemented in working code. Similarly, the archived [XNA Game Studio educational resources](https://github.com/SimonDarksideJ/XNAGameStudio) maintained by Simon Jackson provide a wealth of examples that remain relevant despite XNA's official discontinuation. > [!TIP] -> **REMEMBER**, if you see content written for XNA, then it is more than likely it will still work for MonoGame due to MonoGame's commitment for backwards compatibility. There maybe a few bumps, minor changes (especially with earlier versions of XNA, like XNA 2) or differences with shaders (the biggest pain). But remember the community is here to help, so just ASK!. +> **REMEMBER**, if you see content written for XNA, then it is more than likely it will still work for MonoGame due to MonoGame's commitment for backwards compatibility. There may be a few bumps, minor changes (especially with earlier versions of XNA, like XNA 2) or differences with shaders (the biggest pain). But remember the community is here to help, so just ASK!. ## Your First Original Project -As you contemplate your next project, consider staring with a focused concept that builds on what you have learned while introducing one or two new challenges. This balanced approach allows you to reinforce existing skills while gradually expanding your capabilities. +As you contemplate your next project, consider starting with a focused concept that builds on what you have learned while introducing one or two new challenges. This balanced approach allows you to reinforce existing skills while gradually expanding your capabilities. Perhaps you might create a puzzle game where the core mechanics revolve around tile manipulation, or a side-scrolling adventure that expands on the collision and movement systems you have learned. Whatever you choose, the process will affirm and deepen your understanding in ways that tutorials alone cannot do. In other words, get your hands dirty. -Remember that your first project does not need to be a commercially successful or technically groundbreaking. Its primary value lies in the creative freedom it offers and the practical experience it provides. Each bug you fix and feature you implement strengthens your developer's intuition; the hard-to-define sense of how games work beneath their surface. +Remember that your first project does not need to be commercially successful or technically groundbreaking. Its primary value lies in the creative freedom it offers and the practical experience it provides. Each bug you fix and feature you implement strengthens your developer intuition; the hard-to-define sense of how games work beneath their surface. When you inevitably encounter obstacles, approach them with patience and curiosity. The solutions you discover will not just resolve immediate problems; they will become part of your growing toolkit for future projects. Every challenging moment represents an opportunity to become a more resourceful and knowledgeable developer. ## The Art of Finishing -Perhaps the most valuable skill you have practiced throughout this tutorial series is the art of finishing. Game development history is littered with ambitious projects that grew beyond their creators' capacity to complete them. By following this series to its conclusion, you have demonstrated the discipline and persistence for bringing games to life. +Perhaps the most valuable skill you have practiced throughout this tutorial series is the art of finishing. Game development history is littered with ambitious projects that grew beyond their creators' capacity to complete them. By following this series to its conclusion, you have demonstrated the discipline and persistence in bringing games to life. When working on longer projects, break development into milestones with clear, achievable goals. Celebrate these incremental victories to maintain momentum through the inevitable challenges of extended development. Remember that a simple, finished game will always provide more value, both to players and to your growth as a developer, than an ambitious project that remains perpetually incomplete. @@ -96,7 +96,7 @@ The knowledge you have gained throughout this tutorial series represents not an The MonoGame framework offers a rare combination of freedom and structure; providing the essential tools without limiting your creative expression. As you continue exploring its capabilities, you join a lineage of independent developers who have used these very same tools to create experiences that resonate with players around the world. -Your journey from here will be uniquely yours, shaped by the games you want to make and the challenges you choose to overcome. Whatever path you follow, approach it with the same curiosity, persistence, and analytical thinking that brought you through this tutorial series. The road may not always be easy, but it leads to one of the most rewarding creative disciplines available today - the art and science of making games. +Your journey from here will be uniquely yours, shaped by the games you want to make and by the challenges you choose to overcome. Whatever path you follow, approach it with the same curiosity, persistence, and analytical thinking that brought you through this tutorial series. The road may not always be easy, but it leads to one of the most rewarding creative disciplines available today - the art and science of making games. Instead of ending this like all other chapters with a section to "test your knowledge", I will just leave you with one simple question: diff --git a/articles/whats_new.md b/articles/whats_new.md index f490720f..5d356f34 100644 --- a/articles/whats_new.md +++ b/articles/whats_new.md @@ -9,7 +9,7 @@ description: What is new with the release of MonoGame 3.8.* > **Coming soon** > Refer to the [CHANGELOG](https://github.com/MonoGame/MonoGame/blob/develop/CHANGELOG.md) for a more complete list of the changes. -Notes for the final 3.8.4 release are still in progress. Preview builds available. +Notes for the final 3.8.4 release are still in progress. Preview builds are available. > [!NOTE] > More details to follow @@ -42,11 +42,11 @@ Notes for the final 3.8.4 release are still in progress. Preview builds availab * Update to .NET 8, including the MGCB tool (now a local tool rather than global, allowing different projects to use different versions) * Removal of Windows UWP due to Microsoft shutting support for it (you can still use UWP if you remain on 3.8.1) -* Updated to building dependencies to improve support. +* Updated build dependencies to improve support. * New documentation, including the continuing migration of the older XNA documentation. * A MASSIVE clean-up of the MonoGame XML documentation, big shout out to (@AristurtleDev and his team). * Build system improvements (mainly a MonoGame building backend thing). -* Various bugfixes to so many areas of the Framework. +* Various bug fixes across many areas of the framework. > [!NOTE] > More details to follow @@ -68,7 +68,7 @@ MonoGame 3.8.1 now comes with an optional Visual Studio extension which will ins This extension is available for Visual Studio 2022, and Visual Studio 2022 for Mac. -If you are migrating from 3.8.0 it is recommended to uninstall the existing global MGCB .NET tools. It would be a good idea to review the [Migrating from 3.8.0](./migration/migrate_38.md) documentation, as there are changes to how 3.8.1 works with the .NET tools and the new extension. +If you are migrating from 3.8.0 it is recommended to uninstall the existing global MGCB .NET tools. We recommend reviewing the [Migrating from 3.8.0](./migration/migrate_38.md) documentation, as there are changes to how 3.8.1 works with the .NET tools and the new extension. ## Visual Studio 2019 and prior are no longer supported @@ -84,7 +84,7 @@ JetBrains Rider and Visual Studio Code can be used regardless of the version of MonoGame 3.8.1 will be the last version to support building and running 32bit games on Windows. -This is motivated by the fact that 32bit players are nearly extinct (less than 0.24% of the Steam user base). It will also help developers with less complex distribution and less confusing debug/build experiences. +This is motivated by the fact that 32-bit players are nearly extinct (less than 0.24% of the Steam user base). It will also help developers with less complex distribution and less confusing debug/build experiences. ## Apple silicon (M1+) support From 21800f8e047cd5b30dbd2058570632ce20b1d7b8 Mon Sep 17 00:00:00 2001 From: QuestSWE Date: Thu, 26 Jun 2025 13:17:04 -0400 Subject: [PATCH 6/6] (Fix)Removed < > from xref links and set unordered list to ordered list --- .../12_collision_detection/index.md | 18 +++++++++--------- .../building_2d_games/17_scenes/index.md | 2 +- .../22_snake_game_mechanics/index.md | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/articles/tutorials/building_2d_games/12_collision_detection/index.md b/articles/tutorials/building_2d_games/12_collision_detection/index.md index 266d51bb..0b1dfeee 100644 --- a/articles/tutorials/building_2d_games/12_collision_detection/index.md +++ b/articles/tutorials/building_2d_games/12_collision_detection/index.md @@ -58,12 +58,12 @@ $$(radius_{circle1} + radius_{circle2})^2 > a^2 + b^2$$ It is easy to confuse the direction of the inequality sign. As a quick mental test, think of how the math works when the origin of two circles are at the same position, i.e., when the *squared distance* is zero. -To calculate the squared distance between two points, MonoGame provides the [**Vector2.DistanceSquared**]() method: +To calculate the squared distance between two points, MonoGame provides the [**Vector2.DistanceSquared**](xref:Microsoft.Xna.Framework.Vector2.DistanceSquared(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) method: [!code-csharp[](./snippets/vector2_distance.cs)] > [!TIP] -> MonoGame also provides a distance calculation method with [**Vector2.Distance**]() which returns the distance by providing the square root of the distance squared. So why not use this instead? +> MonoGame also provides a distance calculation method with [**Vector2.Distance**](xref:Microsoft.Xna.Framework.Vector2.Distance(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) which returns the distance by providing the square root of the distance squared. So why not use this instead? > > Square root operations are more computationally complex for a CPU. So instead of getting the normal distance, which would require the square root operation, it is more efficient for the cpu to multiply the sum of the radii by itself to get the squared sum and use that for comparison instead. @@ -93,7 +93,7 @@ To determine if two rectangles overlap using AABB collision detection, there are If even a single one of these conditions is false, then the rectangles are not overlapping and thus not colliding. -MonoGame provides the [**Rectangle.Intersects**]() method which will perform an AABB collision check for us: +MonoGame provides the [**Rectangle.Intersects**](xref:Microsoft.Xna.Framework.Rectangle.Intersects(Microsoft.Xna.Framework.Rectangle)) method which will perform an AABB collision check for us: [!code-csharp[](./snippets/rectangle_intersects.cs)] @@ -144,7 +144,7 @@ For example: [!code-csharp[](./snippets/blocking_example.cs)] -Sometimes, instead of preventing an object from moving onto another object, we want to ensure an object remains contained within a certain bounding area. MonoGame also provides the [**Rectangle.Contains**]() method that we can use to determine this. [**Rectangle.Contains**]() can check if any of the following are completely contained within the bounds of the rectangle; +Sometimes, instead of preventing an object from moving onto another object, we want to ensure an object remains contained within a certain bounding area. MonoGame also provides the [**Rectangle.Contains**](xref:Microsoft.Xna.Framework.Rectangle.Contains(Microsoft.Xna.Framework.Rectangle)) method that we can use to determine this. [**Rectangle.Contains**](xref:Microsoft.Xna.Framework.Rectangle.Contains(Microsoft.Xna.Framework.Rectangle)) can check if any of the following are completely contained within the bounds of the rectangle; - [**Point**](xref:Microsoft.Xna.Framework.Point) - [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) @@ -174,7 +174,7 @@ For example: #### Bounce Collision Response -For games that need objects to bounce off each other (like the ball in a Pong game), we need to calculate how their velocity should change after the collision. MonoGame provides the [**Vector2.Reflect**]() method to handle this calculation for us. The method needs two pieces of information: +For games that need objects to bounce off each other (like the ball in a Pong game), we need to calculate how their velocity should change after the collision. MonoGame provides the [**Vector2.Reflect**](xref:Microsoft.Xna.Framework.Vector2.Reflect(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) method to handle this calculation for us. The method needs two pieces of information: 1. The incoming vector (the direction the object is moving in before the collision). 2. The normal vector (the direction perpendicular to the surface). @@ -318,7 +318,7 @@ The key changes made here are: 2. The field `_batVelocity` was added to track the velocity of the bat. 3. The `AssignRandomBatVelocity()` method was added which calculates a random x and y velocity for the bat to move at when called. 4. In [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize), the initial position of the bat is set and `AssignRandomVelocity` is called to assign the initial velocity for the bat. -5. In [**Update**](), collision detection and response logic was added to perform the following in order: +5. In [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), collision detection and response logic was added to perform the following in order: 1. A [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) bound is created to represent the bounds of the screen. 2. A `Circle` bound is created to represent the bounds of the slime. 3. Distance based checks are performed to ensure that the slime cannot move outside of the screen, the resolution of which is to perform a blocking response. @@ -326,7 +326,7 @@ The key changes made here are: 5. A `Circle` bound is created to represent the bounds of the bat. 6. Distance based checks are performed to ensure the bat cannot move outside of the screen, the resolution of which is to perform a bounce response. 7. A collision check is made to determine if the slime and bat are colliding (bat "eating" the slime). If so, the bat is assigned a new random position within the screen and assigned a new random velocity. -6. In [**Draw**](), the bat is now drawn using the `_batPosition` value. +6. In [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)), the bat is now drawn using the `_batPosition` value. Running the game now @@ -389,10 +389,10 @@ In the next chapter, we will explore using tilesets and tilemaps to create tile Two circles are colliding if the distance between their centers is less than the sum of their radii. If the distance is greater, they are separate. If the distance equals the sum of radii, they are just touching at one point. ::: -4. When implementing bounce collision response, what two pieces of information does [**Vector2.Reflect**]() need? +4. When implementing bounce collision response, what two pieces of information does [**Vector2.Reflect**](xref:Microsoft.Xna.Framework.Vector2.Reflect(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) need? ::: question-answer - [**Vector2.Reflect**]() needs: + [**Vector2.Reflect**](xref:Microsoft.Xna.Framework.Vector2.Reflect(Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2)) needs: 1. The incoming vector (direction the object is moving). 2. The normal vector (direction perpendicular to the surface being hit). diff --git a/articles/tutorials/building_2d_games/17_scenes/index.md b/articles/tutorials/building_2d_games/17_scenes/index.md index ae966d60..95e1b65f 100644 --- a/articles/tutorials/building_2d_games/17_scenes/index.md +++ b/articles/tutorials/building_2d_games/17_scenes/index.md @@ -115,7 +115,7 @@ The key changes here are: ## Updating the Game -With the scene architecture in place, we’re now ready to update the game by breaking it into separate scenes. We will create two scenes; a title scene and a gameplay scene. First, however, we need to add an additional SpriteFont Description that will be used during the title scene to display the title of the game. Open the *Content.mgcb* content project file in the MGCB Editor and perform the following: +With the scene architecture in place, we are now ready to update the game by breaking it into separate scenes. We will create two scenes; a title scene and a gameplay scene. First, however, we need to add an additional SpriteFont Description that will be used during the title scene to display the title of the game. Open the *Content.mgcb* content project file in the MGCB Editor and perform the following: 1. Right-click the `fonts` folder and choose `Add > New Item...`. 2. Select `SpriteFont Description (.spritefont)` from the options. diff --git a/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md b/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md index 2891a43b..27569409 100644 --- a/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md +++ b/articles/tutorials/building_2d_games/22_snake_game_mechanics/index.md @@ -269,8 +269,8 @@ This update method: 2. Calls `HandleInput` to check for player input. 3. Increments the movement timer by the amount of time that has elapsed between the game's update cycles. 4. Performs a check to see if the movement timer has accumulated more time than the threshold to perform a movement cycle update. If it has then: - - The movement timer is reduced by the threshold time. - - The `Move` method is called to perform a movement cycle update. + 1. The movement timer is reduced by the threshold time. + 2. The `Move` method is called to perform a movement cycle update. 5. Finally, the movement progress amount is calculated by dividing the number of seconds accumulated for the movement timer by the number of seconds for the threshold.  This gives us a normalized value between 0.0 and 1.0 that we can use for visual interpolation for fluid movement. > [!TIP]