Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ We are going to add this class to the class library we created in [Chapter 04](.
[!code-csharp[](./snippets/textureregion.cs#declaration)]

> [!NOTE]
> The `TextureRegion.cs` class file is placed in the *MonoGame/Graphics* folder and the class uses the `MonoGameLibrary.Graphics` [namespace](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/namespaces#namespaces-overview) to keep graphics-related classes organized together. As we add more functionality to the library, we will continue to use directories and namespaces to maintain a clean structure.
> The `TextureRegion.cs` class file is placed in the *MonoGameLibrary/Graphics* folder and the class uses the `MonoGameLibrary.Graphics` [namespace](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/namespaces#namespaces-overview) to keep graphics-related classes organized together. As we add more functionality to the library, we will continue to use directories and namespaces to maintain a clean structure.

We will add several components to this class in sequence. Each section below should be added to the `TextureRegion` class in the order presented between the brackets ` { } ` of the class definition. As we go through each part, the class will gradually take shape to handle all the texture handling behavior we need.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Now that we have a fully configured XML configuration for the atlas, we need to

[!code-csharp[](./snippets/textureatlas/add_animation_management.cs)]

5. Update the `FromFile` method to parse the new `<Animation>` animation definitions from the XML configuration file
5. Update the `FromFile` method to parse the new `<Animation>` animation definitions from the XML configuration file:

[!code-csharp[](./snippets//textureatlas/update_from_file.cs?highlight=55-95)]

Expand All @@ -106,7 +106,7 @@ The updated `FromFile` method now handles both region and animation definitions

## The AnimatedSprite Class

With our `Animation` class handling animation data, and the `TextureAtlas` updated to store the animation data, we can now create a class that represents an animated sprites. Since an animated sprite is essentially a sprite that changes its texture region over time, we can build upon our existing `Sprite` class through [inheritance](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/inheritance).
With our `Animation` class handling animation data, and the `TextureAtlas` updated to store the animation data, we can now create a class that represents animated sprites. Since an animated sprite is essentially a sprite that changes its texture region over time, we can build upon our existing `Sprite` class through [inheritance](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/inheritance).

> [!NOTE]
> By inheriting from `Sprite`, our `AnimatedSprite` class automatically gets all the rendering properties (position, rotation, scale, etc.) while adding new animation-specific functionality.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Before we can add audio to our game, we need some sound files to work with. Down
>
> - *bounce.wav* is "Retro Impact Punch 07" by Davit Masia (<https://kronbits.itch.io/retrosfx>).
> - *collect.wav* is "Retro Jump Classic 08" by Davit Masia (<https://kronbits.itch.io/retrosfx>).
> - *theme.mp3* is "Exploration" by Luis Zuno ([@ansimuz](https://twitter.com/ansimuz)).
> - *theme.ogg* is "Exploration" by Luis Zuno ([@ansimuz](https://twitter.com/ansimuz)).

Add these files to your content project using the MGCB Editor:

Expand All @@ -170,7 +170,7 @@ Add these files to your content project using the MGCB Editor:
4. Navigate to and select the audio files you downloaded.
5. For each file that is added, check its properties in the Properties panel:
- For `.wav` files, ensure the *Processor* is set to `Sound Effect`.
- For `.mp3` files, ensure the *Processor* is set to `Song`.
- For `.ogg` files, ensure the *Processor* is set to `Song`.
6. Save the changes and close the MGCB Editor.

Next, open the `Game1.cs` file and update it to the following:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Think of `IDisposable` like a cleanup checklist that runs when you are finished
For our `AudioController`, implementing `IDisposable` means we can ensure all sound effect instances are properly stopped and disposed when our game ends, preventing resource leaks.

> [!NOTE]
> Fore more information on `IDisposable` and the `Dispose` method, check out the [Implementing a Dispose Method](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose) article on Microsoft Learn.
> For more information on `IDisposable` and the `Dispose` method, check out the [Implementing a Dispose Method](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose) article on Microsoft Learn.

## Implementing the AudioController Class

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class AudioController : IDisposable
/// </summary>
public bool IsMuted { get; private set; }

/// <summary>
/// <summary>
/// Gets or Sets the global volume of songs.
/// </summary>
/// <remarks>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,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, all Forms controls automatically respond to mouse and touch screen input devices. We need to explicitly register keyboard and gamepad input devices by using the `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 we map the Up arrow for reverse tabbing and the Down arrow for forward tabbing.

> [!TIP]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ private void InitializeGum()
// version.
GumService.Default.Initialize(this, DefaultVisualsVersion.V3);

// Tell the Gum service which content manager to use. We will tell it to
// Tell the Gum service which content manager to use. We will tell it to
// use the global content manager from our Core.
GumService.Default.ContentLoader.XnaContentManager = Core.Content;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
private void CreateOptionsPanel() {
private void CreateOptionsPanel()
{
_optionsPanel = new Panel();
_optionsPanel.Dock(Gum.Wireframe.Dock.Fill);
_optionsPanel.IsVisible = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ Finally, update the `CreatePausePanel` method so that

1. Instead of using a [`ColoredRectangleRuntime`](#visual-elements) for the background of the pause panel, it now uses a [`NineSliceRuntime`](#visual-elements) that uses the sprite from the texture atlas.
2. The `textInstance` is updated so that it uses the custom bitmap font file.
3. The `_resumeButton` and `quiteButton` are updated to use our custom [`AnimatedButton`](#the-animatedbutton-class) control instead of the default Gum `Button` Forms control.
3. The `_resumeButton` and `quitButton` are updated to use our custom [`AnimatedButton`](#the-animatedbutton-class) control instead of the default Gum `Button` Forms control.

[!code-csharp[](./snippets/gamescene/createpausepanel.cs?highlight=12-22,26-28,33,41)]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: "Chapter 22: Snake Game Mechanics"
description: "Learn how to implement classic snake-like game mechanics and organize game objects into reusable components."
---

In the previous chapters, we have built all the fundamental systems needed for our game: [graphics](../07_optimizing_texture_rendering/index.md), [input](../11_input_management/index.md), [collision detection](../12_collision_detection/index.md), [audio](../15_audio_controller/index.md), [scene management](../17_scenes/index.md), and a [user interface](../19_user_interface_fundamentals/index.md). Now it is time to transform our demo into a complete experience by implementing classic snake-like game mechanics. Before we do that, we first need to define what mechanics make a snake game.
In the previous chapters, we have built all the fundamental systems needed for our game: [graphics](../07_optimizing_texture_rendering/index.md), [input](../11_input_management/index.md), [collision detection](../12_collision_detection/index.md), [audio](../15_audio_controller/index.md), [scene management](../17_scenes/index.md), and a [user interface](../19_user_interface_fundamentals/index.md). Now it is time to transform our demo into a complete experience by implementing classic snake-like game mechanics. Before we do that, we first need to define what mechanics make a snake game.

In this chapter, you will:

Expand Down Expand Up @@ -36,13 +36,13 @@ In snake, players input a cardinal direction (up, down, left, and right), to ind

For example, if the snake is moving to the right, an invalid input would allow a player to move it to the left.  Doing so would cause the head of the snake to reverse direction and immediately collide with the first body segment. This means the only valid inputs are those where the next direction would be the same as the current direction or perpendicular to the current direction.

| ![Figure 22-1: An example snake with four segments, the head segment highlighted in orange, moving to the right. Arrows show that the only valid movements for the head segment are up or down (perpendicular) or to continue to the right.](./images/snake_directions.png) |
| ![Figure 22-1: An example snake with four segments, the head segment highlighted in orange, moving to the right. Arrows show that the only valid movements for the head segment are up or down (perpendicular) or to continue to the right.](./images/snake_directions.png) |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| **Figure 22-1: An example snake with four segments, the head segment highlighted in orange, moving to the right. Arrows show that the only valid movements for the head segment are up or down (perpendicular) or to continue to the right.** |
| **Figure 22-1: An example snake with four segments, the head segment highlighted in orange, moving to the right. Arrows show that the only valid movements for the head segment are up or down (perpendicular) or to continue to the right.** |

### Movement Cycle

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.
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.  

Expand Down Expand Up @@ -284,7 +284,7 @@ This update method:
> - If we reset to zero, we lose 0.04ms.
> - Over time, these small losses can add up and cause inconsistent movement.
>
> By subtracting the threshold instead of resetting to zero, we "bank" the excess time (0.06ms in this example) for the next movement cycle.  This ensures that:
> By subtracting the threshold instead of resetting to zero, we "bank" the excess time (0.04ms in this example) for the next movement cycle.  This ensures that:
>
> 1. Movement happens exactly at the intended frequency, maintaining consistent game speed.
> 2. The visual smoothness of movement remains intact even if the game occasionally drops frames.
Expand Down Expand Up @@ -345,7 +345,7 @@ In the `GameObjects` folder of the *DungeonSlime* project (your main game projec

This code establishes the foundation for our `Bat` class. We have included the necessary using statements for MonoGame components, audio functionality, and our library references. The class is placed in the same `DungeonSlime.GameObjects` namespace as our Slime class to maintain a consistent organization.

Now we will build this class step by step, adding all the functionality needed for the bat to serve as the collectible object in our game. Add each of the following sections to the `Bat` class in the order they are presented.
Now we will build this class step by step, adding all the functionality needed for the bat to serve as the collectible object in our game. Add each of the following sections to the `Bat` class in the order they are presented.

> [!NOTE]
> As with the Slime class, you may encounter compiler errors until all sections are in place. These errors will be resolved once all components of the class have been added.
Expand Down Expand Up @@ -381,7 +381,7 @@ This is a simple constructor that requires the bat to be given the `AnimatedSpri

#### Bat Randomize Velocity

Currently, we have the `AssignRandomVelocity` method in the `GameScene` that we call to randomize the velocity of the bat after it has been eaten by the slime. We can take this method out of the `GameScene` class and put it directly into the `Bat` class itself.
Currently, we have the `AssignRandomVelocity` method in the `GameScene` that we call to randomize the velocity of the bat after it has been eaten by the slime. We can take this method out of the `GameScene` class and put it directly into the `Bat` class itself.

Add the following method to the `Bat` class after the constructor:

Expand Down Expand Up @@ -433,9 +433,9 @@ With the `Bat` class complete, we have now encapsulated all the behavior needed
## Conclusion

> [!NOTE]
> To the observant, you should notice that the main game screen has not been updated and therefore nothing has changed if we run the game at this point. In the next chapter we will finalize the gameplay.
> To the observant, you should notice that the main game screen has not been updated and therefore nothing has changed if we run the game at this point. In the next chapter we will finalize the gameplay.

In this chapter, we have learned about and implemented the core mechanics of a class snake-like game. We created:
In this chapter, we have learned about and implemented the core mechanics of a class snake-like game. We created:

- A [`GameController`](#the-gamecontroller-class) class that provides a unified input interface, separating game actions from specific input devices.
- A [`SlimeSegment`](#the-slimesegment-struct) struct to efficiently store and manage individual segments of our snake-like character.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ This method checks for three types of collisions:

### GameScene PositionBatAwayFromSlime Method

The `CollisionCheck` method makes a call to `PositionBatAwayFromSlime`, previously, when we needed to set the position of the bat when it respawns, we simply chose a random tile within the tilemap to move it to. However, by choosing a completely random location it could be on top fo the head segment of the slime, forcing an instant collision, or it could spawn very close to the head segment, which is not challenging for the player. To ensure the bat appears in a random, but strategic location, we can instead set it to position away from the slime on the opposite side of the room.
The `CollisionCheck` method makes a call to `PositionBatAwayFromSlime`, previously, when we needed to set the position of the bat when it respawns, we simply chose a random tile within the tilemap to move it to. However, by choosing a completely random location it could be on top of the head segment of the slime, forcing an instant collision, or it could spawn very close to the head segment, which is not challenging for the player. To ensure the bat appears in a random, but strategic location, we can instead set it to position away from the slime on the opposite side of the room.

Add the following method to the `GameScene` class after the `CollisionCheck` method:

Expand Down
2 changes: 1 addition & 1 deletion articles/tutorials/building_2d_games/24_shaders/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ This diagram illustrates the fundamental steps of the shader pipeline:

### Shader Languages and Cross-Platform Considerations

MonoGame uses the [High-Level Shader Language (HLSL)](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl) for writing shader effects. **HLSL** is a C-like programming language developed by Microsoft for DirectX. As MonoGame also supports OpenGL which uses the [OpenGL Shading Language (GLSL)](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language) instead of DirectX as it needs a way to make shaders work everywhere.
MonoGame uses the [High-Level Shader Language (HLSL)](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl) for writing shader effects. **HLSL** is a C-like programming language developed by Microsoft for DirectX. MonoGame also supports OpenGL which uses the [OpenGL Shading Language (GLSL)](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language) instead of DirectX as it needs a way to make shaders work everywhere.

This is where MojoShader comes in. MojoShader is a library that automatically translates your **HLSL** shader code into whatever format the target platform requires (like **GLSL** for OpenGL platforms), this translation happens during the content build process when you compile your game.

Expand Down
Loading
Loading