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 efbf095b..c00ca414 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 @@ -35,7 +35,7 @@ The graphics pipeline in monogame starts with two components: the [**GraphicsDev [!code-csharp[](./snippets/game1.cs?start=9&end=10)] -The [**GraphicsDeviceManager**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager) initializes and the connection to the graphics hardware. It handles tasks such as setting the screen resolution, toggling between fullscreen and windowed mode, and managing the [**GraphicsDevice**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice), which is the interface between your game and the Graphics Processing Unit (GPU) the game is running on. The [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) optimizes 2D rendering by batching similar draw calls together, improving draw performance when rendering multiple sprites. +The [**GraphicsDeviceManager**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager) initializes and manages the connection to the graphics hardware. It handles tasks such as setting the screen resolution, toggling between fullscreen and windowed mode, and managing the [**GraphicsDevice**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice), which is the interface between your game and the Graphics Processing Unit (GPU) the game is running on. The [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) optimizes 2D rendering by batching similar draw calls together, improving draw performance when rendering multiple sprites. ## Initialization @@ -58,7 +58,7 @@ The [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) method serv [!code-csharp[](./snippets/game1.cs?start=24&end=27)] -This method is only call once during the startup of the game, but *when* it is called can be a little confusing at first. In the [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) method shown above, when the `base.Initialize` call is executed, the final task it performs is calling the [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) method. This means any initializations you need to perform that have a dependency on assets being loaded should be done *after* the `base.Initialize` call and not *before* it. +This method is only called once during the startup of the game, but *when* it is called can be a little confusing at first. In the [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) method shown above, when the `base.Initialize` call is executed, the final task it performs is calling the [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) method. This means any initializations you need to perform that have a dependency on assets being loaded should be done *after* the `base.Initialize` call and not *before* it. ## The Game Loop @@ -70,7 +70,7 @@ MonoGame is executing the [**Update**](xref:Microsoft.Xna.Framework.Game.Update( The [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)) method at the moment is not doing much, only checking for input from a controller or keyboard to determine if the game should exit. However, the [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) method is doing more than what it appears to at first glance. -The first line is executing the [**Clear**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color)) method of the [**GraphicsDevice**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice) property using the color [**CornflowerBlue**](xref:Microsoft.Xna.Framework.Color.CornflowerBlue). Recall that the [**GraphicsDevice**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice) object is your direct interface between the game and what is rendered to the screen. Every time the [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) method is called, this line of code of erasing the contents of the game window and refilling it with the color specified. Without clearing the contents of the screen first, every draw call would draw the new frame render over top of the previous render, and you'd end up with something like the old solitaire win screen +The first line is executing the [**Clear**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color)) method of the [**GraphicsDevice**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice) property using the color [**CornflowerBlue**](xref:Microsoft.Xna.Framework.Color.CornflowerBlue). Recall that the [**GraphicsDevice**](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice) object is your direct interface between the game and what is rendered to the screen. Every time the [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) method is called, this line of code is erasing the contents of the game window and refilling it with the color specified. Without clearing the contents of the screen first, every draw call would draw the new frame render on top of the previous render, and you'd end up with something like the old solitaire win screen. | ![Figure 3-2: Windows XP Solitaire Win Screen](./images/solitaire.webp) | | :---------------------------------------------------------------------: | @@ -86,18 +86,18 @@ While this can make for a neat effect, it is not something you want all the time > After making this change and running the game, the screen is cleared to the MonoGame Orange color. > > | ![Figure 3-3: The game window clearing the screen using the MonoGame Orange color](./images/monogame-orange.png) | -> | :---: | -> | **Figure 3-3: The game window clearing the screen using the MonoGame Orange color** | +> | :--------------------------------------------------------------------------------------------------------------: | +> | **Figure 3-3: The game window clearing the screen using the MonoGame Orange color** | -Each time the game loops completes and the game is drawn to the screen, we call this a *frame*. So if MonoGame is running the game loop at 60 frames per second, that means it is performing and update and a render of each frame every 16ms. Notice that both the [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)) and the [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) methods both receive a parameter of the type [**GameTime**](xref:Microsoft.Xna.Framework.GameTime). The [**GameTime**](xref:Microsoft.Xna.Framework.GameTime) parameter provides a snapshot of the timing values for the game, including the amount of time that it took for the previous frame to execute. This is commonly referred to as the *delta time*. +Each time the game loop completes and the game is drawn to the screen, we call this a *frame*. So, if MonoGame is running the game loop at 60 frames per second, that means it is performing an update and a render of a frame in 16ms. Notice that both the [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)) and the [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) methods both receive a parameter of the type [**GameTime**](xref:Microsoft.Xna.Framework.GameTime). The [**GameTime**](xref:Microsoft.Xna.Framework.GameTime) parameter provides a snapshot of the timing values for the game, including the amount of time that it took for the previous frame to execute. This is commonly referred to as the *delta time*. -*Delta time* allows you to track time accurately for things such as animations and events based on *game time* and not the speed of the processor (CPU) on the machine running the game. While in ideal circumstances, the delta time will always be 16ms, there are any number of things that could cause a temporary slow down or hiccup in a frame, and using the delta time ensures that timing based events are always correct. +*Delta time* allows you to track time accurately for things such as animations and events based on *game time* and not the speed of the processor (CPU) on the machine running the game. While in ideal circumstances, the delta time will always be 16ms, there are any number of things that could cause a temporary slow down or hiccup in a frame, and using the delta time ensures that time-based events are always correct. ## Conclusion In this chapter, you accomplished the following: -- You read through the default code provided in a `Game1.cs` file created by a MonoGame template. +- You read through the default code provided in the `Game1.cs` file created by a MonoGame template. - You learned about the lifecycle of a MonoGame game project. - You learned what a game loop is and how it is implemented in MonoGame. @@ -114,7 +114,7 @@ In the next chapter, you will start working with sprites and learn how to load a 2. What is the [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) used for? :::question-answer - The [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) provides an optimized method of rendering 2D graphics, like sprites, onto the screen + The [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) provides an optimized method of rendering 2D graphics, like sprites, onto the screen. ::: 3. When is the [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) method executed and why is it important to know this? 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 d6dd00c7..323bb731 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 @@ -19,31 +19,31 @@ Think of a class library like a toolbox for your game development. Just as a mec The following diagrams show how this works: -| ![Figure 4-1: Diagram displays the block for Game 1 on the left and Game 2 on the right. In this example, when not using a class library, code for input, physics and audio systems are duplicated between both game projects. If a bug is fixed in one system, the effort has to be duplicated in the same system in other game projects](./images/without-class-library-diagram.svg) | -| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| **Figure 4-1: Diagram displays the block for Game 1 on the left and Game 2 on the right. In this example, when not using a class library, code for input, physics and audio systems are duplicated between both game projects. If a bug is fixed in one system, the effort has to be duplicated in the same system in other game projects** | +| ![Figure 4-1: This diagram displays the block for Game 1 on the left and Game 2 on the right. In this example, when not using a class library, code for input, physics and audio systems is duplicated in both game projects. If a bug is fixed in one system, the effort has to be replicated in the same system in the other game projects.](./images/without-class-library-diagram.svg) | +| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| **Figure 4-1: This diagram displays the block for Game 1 on the left and Game 2 on the right. In this example, when not using a class library, code for input, physics and audio systems is duplicated in both game projects. If a bug is fixed in one system, the effort has to be replicated in the same system in the other game projects.** | -| ![Figure 4-2: Diagram displays a block for a class library which contains common modules at the top, which are then shared between the two game projects below. If a bug is found in a module, fixing the bug will fix it across all game projects that use the class library](./images/with-class-library-diagram.svg) | -| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| **Figure 4-2: Diagram displays a block for a class library which contains common modules at the top, which are then shared between the two game projects below. If a bug is found in a module, fixing the bug will fix it across all game projects that use the class library** | +| ![Figure 4-2: This diagram displays a block for a class library containing common modules at the top, which are then shared between the two game projects below. If a bug is found in a module, fixing the bug will fix it across all game projects that use the class library.](./images/with-class-library-diagram.svg) | +| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| **Figure 4-2: This diagram displays a block for a class library containing common modules at the top, which are then shared between the two game projects below. If a bug is found in a module, fixing the bug will fix it across all game projects that use the class library.** | > [!NOTE] > A class library is a project type that compiles into a [Dynamic Link Library](https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-libraries) (DLL) instead of an executable. It contains reusable code that can be referenced by other projects, making it perfect for sharing common functionality across multiple games. ## Why Create a Class Library? -Creating a class library offers several important advantages, especially as your games grow more complex: +Creating a class library offers several important advantages, especially as your games grow in complexity: 1. **Reusability**: Instead of rewriting the same code for each new game project, you build it once in your library and reuse it everywhere. This is like creating a multi-tool that works across all your projects. 2. **Organization**: Your game code stays focused on the unique aspects of each game, while common functionality lives in the library. This keeps your project folder neat and makes code easier to find. -3. **Maintainability**: When you improve or fix a bug in your library code, all games using that library benefit automatically. This means fixing one bug once instead of in multiple places. +3. **Maintainability**: When you improve or fix a bug in your library code, all games using that library benefit automatically. This means you only fix a bug once, rather than in several places. 4. **Testing**: You can test your library code independently from any specific game. This helps ensure your core systems are solid before you build a game on top of them. As your library grows, you will accumulate a personal collection of well-tested modules that make starting new projects much faster. The modules we will create in this library will handle common game tasks like input, audio, sprites, and animations. ## Adding the Class Library -MonoGame offers the *MonoGame Game Library* project template to add a new class library project that is configured with the correct monoGame framework references. Using this template saves time and ensures compatibility with MonoGame projects. +MonoGame offers the *MonoGame Game Library* project template to add a new class library project that is configured with the correct MonoGame framework references. Using this template saves time and ensures compatibility with MonoGame projects. To use the template to add the class library, perform the following based on which development environment you are using: @@ -66,7 +66,7 @@ To add the class library using the MonoGame Game Library project template in Vis 2. Choose Add > New Project from the context menu. 3. Enter "MonoGame Game Library" in the search box, select that template, then click Next. 4. Name the project "MonoGameLibrary". -5. The location by default will put the new project in a folder next to your game project; you do not need to adjust this. +5. The default location will place the new project in a folder next to your game project; you do not need to adjust this setting. 6. Click "Create". ### [dotnet CLI](#tab/dotnetcli) @@ -89,7 +89,7 @@ To add the game library project as a reference to the game project in Visual Stu 1. In the Solution Explorer panel, right-click the *DungeonSlime* project. 2. Choose "Add Project Reference" from the context menu. -3. Choose *MonoGameLibrary" from the available options. +3. Choose *MonoGameLibrary* from the available options. > [!TIP] > The Solution Explorer panel in VSCode is provided by the C# Dev Kit extension that was installed in [Chapter 02](../02_getting_started/index.md#install-the-c-dev-kit-extension). If you do not see this panel, you can open it by @@ -124,7 +124,7 @@ When using the *MonoGame Game Library* project template, the generated project c 3. The `Game1.cs` file. > [!TIP] -> These files are needed in more advanced scenarios such as creating a central code base for game logic that is referenced by other projects of which each target different platforms such as desktop, mobile, and console. Creating a project structure of this type is out of scope for this tutorial. +> These files are needed in more advanced scenarios, such as creating a central code base for game logic that is referenced by other projects, each targeting different platforms such as desktop, mobile, and console. Creating a project structure of this type is out of scope for this tutorial. > > 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. > @@ -132,23 +132,23 @@ When using the *MonoGame Game Library* project template, the generated project c ## Creating Our First Library Module -We will create a class for our library called `Core`. This class will extend the MonoGame [**Game**](xref:Microsoft.Xna.Framework.Game) class and provide a starting point for game development with some common functionality built in. Creating this will also let us validate that our class library reference setup was correct. +We will create a class for our library called `Core`. This class will extend the MonoGame [**Game**](xref:Microsoft.Xna.Framework.Game) class and provide a starting point for game development with a few common built-in functionalities. Creating this will also allow us to confirm that our class library reference setup is correct. Create a new file called `Core.cs` in the *MonoGameLibrary* project and add the following code: [!code-csharp[](./snippets/core.cs)] -The `Core` class provides the following features +The `Core` class provides the following features: 1. It extends the MonoGame [**Game**](xref:Microsoft.Xna.Framework.Game) class, so it inherits all of the base functionality. -2. It implements a [singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) through the `Instance` property, ensure only one core exists. +2. It implements a [singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) through the `Instance` property, ensuring that only one core exists. 3. It provides static access to the graphics device manager, the graphics device, the sprite batch, and the content manager. 4. It simplifies the game window setup with a constructor that handles common initializations. > [!NOTE] > The `new` keyword in the property declaration `public static new GraphicsDevice GraphicsDevice` and `public static new ContentManager Content` is used to intentionally hide (or "shadow") the inherited `GraphicsDevice` and `Content` properties from the base `Game` class. This creates new properties with the same name but different accessibility (static vs. instance) in the derived class. > -> When you access `Core.GraphicsDevice` or `Core.Content` you will be using this static properties, while `base.GraphicsDevice` or `base.Content` within instance methods of the `Core` class would still access the original property. This pattern allows us to provide convenient static access to the graphics device and content manager throughout our game without having to reference the Core instance every time. +> When you access `Core.GraphicsDevice` or `Core.Content` you will be using these static properties, while `base.GraphicsDevice` or `base.Content` within instance methods of the `Core` class will access the original property. This pattern allows us to provide convenient static access to the graphics device and content manager throughout our game without having to reference the Core instance every time. This approach provides a consistent foundation for all our games, handling common setup tasks and providing convenient access to core functionality. @@ -165,8 +165,8 @@ Open the `Game1.cs` file and make the following changes: The key changes made here are: -1. Adding `using MonoGameLibrary;` directive to reference our library. -1. Removed the [**GraphicsDeviceManager**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager) and [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) fields, these are now supplied through the `Core` class. +1. Added `using MonoGameLibrary;` directive to reference our library. +1. Removed the [**GraphicsDeviceManager**](xref:Microsoft.Xna.Framework.GraphicsDeviceManager) and [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) fields; these are now supplied through the `Core` class. 1. Changed `Game1` class to inherit from `Core` instead of `Game`. 1. Updated the constructor to call the `Core` base constructor with our game configuration. @@ -180,7 +180,7 @@ Running the game now will show the same window as before, only now it is at a 12 | **Figure 4-3: The game window at 1280x720 with the title Dungeon Slime** | > [!IMPORTANT] -> If you receive an error stating that the following: +> If you receive an error stating the following: > > *The type or namespace name 'Core' could not be found (are you missing a using directive or an assembly reference?)* > @@ -197,7 +197,7 @@ In this chapter, you accomplished the following: - Easier testing - Created a MonoGame class library project - Added the library as a reference to your game project -- Created your first reusable component and referenced and used it in the game project. +- Created your first reusable component, added a reference to it, and used it in the game project. In the next chapter, we will learn about the Content Pipeline and how to load game assets. @@ -222,5 +222,5 @@ In the next chapter, we will learn about the Content Pipeline and how to load ga 3. What happens if you do not add a reference to your class library in your game project? :::question-answer - > Without adding a reference, your game project will be unaware of any code in the class library. You wo not be able to use any of the classes or components from the library in your game. + > Without adding a reference, your game project will be unaware of any code in the class library. You will not be able to use any of the classes or components from the library in your game. ::: 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 aaf857aa..c2071229 100644 --- a/articles/tutorials/building_2d_games/05_content_pipeline/index.md +++ b/articles/tutorials/building_2d_games/05_content_pipeline/index.md @@ -1,13 +1,13 @@ --- title: "Chapter 05: Content Pipeline" -description: Learn the advantages of using the Content Pipeline to load assets and go through the processes of loading your first asset +description: Learn the advantages of using the Content Pipeline to load assets and go through the processes involved in loading your first asset --- -Every game has assets; images to represent the visual graphics to players, audio to provide sound effects and background music, fonts to render text with, and much more. These assets start out as raw files (e.g. *.png* image files or *.mp3* audio files), which you will need to load into the game to use. +Every game has assets: images to represent the visual graphics to players, audio to provide sound effects and background music, fonts to render text with, and much more. These assets start out as raw files (e.g. *.png* image files or *.mp3* audio files), which you will need to load into the game to use. ## Loading Assets -Loading assets can be done during runtime directly from file, or it can be loaded through the **Content Pipeline** Both of these methods are two sides of the same coin and there are trade offs to each approach. +Loading assets can be done during runtime directly from file, or it can be loaded through the **Content Pipeline**. Both of these methods are two sides of the same coin, and there are trade offs to each approach. For instance, to load an image file directly at runtime, you would need to: @@ -16,12 +16,12 @@ For instance, to load an image file directly at runtime, you would need to: 3. Load the image file as a texture at runtime using the [**Texture2D.FromFile**](xref:Microsoft.Xna.Framework.Graphics.Texture2D.FromFile(Microsoft.Xna.Framework.Graphics.GraphicsDevice,System.String)) method. > [!IMPORTANT] -> A big disadvantage to loading an image file as a texture directly, is that when it loads, it does so in its compressed format such as *.png* or *.jpg*. These compression formats are not understood by a Graphics Processing Unit (GPU); they will need to be decompressed into raw bytes as a format the GPU does understand before it can store the data. Doing this can potentially leave a larger memory footprint for your assets. You will also need to handle how different compression formats work on the platform you are targeting such as desktops, mobile, and consoles. +> One major drawback of loading an image file as a texture directly at runtime is that the data is in a compressed format, such as .png or .jpg. These compression formats are not understood by a Graphics Processing Unit (GPU). In order to be used by your game, this data will need to be decompressed into raw bytes, a format that the GPU understands. This operation can potentially leave a larger memory footprint for your assets. You will also need to handle how different compression formats work on the platform you are targeting, such as desktops, mobile and consoles. -On the other side of this coin, MonoGame offers the **Content Pipeline**; a workflow for managing assets. The workflow is made up of a set of tools and utilities that are automatically added by default when you create a new MonoGame project using the MonoGame project templates. To use this workflow, you need to: +On the other side of the coin, MonoGame offers the **Content Pipeline**; a workflow for managing assets. The workflow is made up of a set of tools and utilities that are automatically added by default when you create a new MonoGame project using the MonoGame project templates. To use this workflow, you need to: 1. Add the asset file to your content project (*Content.mgcb* file) using the *MonoGame Content Builder Editor* (MGCB Editor). -2. Perform a project build. Doing this, the *MonoGame.Content.Builder.Tasks* NuGet reference will compile the assets defined in the content project, optimized for the target platform, and automatically copy them to the game project build folder. +2. Perform a project build. This will cause the *MonoGame.Content.Builder.Tasks* NuGet reference to compile the assets defined in the content project, optimized for the target platform, and automatically copy them to the game project build folder. 3. Load the compiled asset at runtime using the [**ContentManager**](xref:Microsoft.Xna.Framework.Content.ContentManager). For the same amount of steps, you also get the benefit of the assets being pre-processed and compiled to an optimized format for the target platform. For instance, image files can be compiled using [DXT compression](https://en.wikipedia.org/wiki/S3\_Texture\_Compression), which is a format that is understood by GPUs without needing to be decompressed first, reducing the memory footprint. @@ -29,7 +29,7 @@ For the same amount of steps, you also get the benefit of the assets being pre-p > [!NOTE] > For more information on the benefits of compiling assets and what optimizations it can offer, see the [Content Pipeline](../../../getting_started/content_pipeline/index.md) documentation. -For this tutorial series, we are going to focus on using the content pipeline workflow to load assets. Doing this will get you as the developer accustomed to using the content pipeline tools and also give the benefits of having assets precompiled to optimized formats. +For this tutorial series, we are going to focus on using the content pipeline workflow to load assets. Doing this will allow you, as the developer, to get accustomed to using the content pipeline tools, and also give the benefits of having assets pre-compiled to optimized formats. ## The MGCB Editor @@ -39,7 +39,7 @@ Opening the MGCB Editor can be done in different ways depending on which IDE and ### [Visual Studio Code](#tab/vscode) -To open the *Content.mgcb* content project file in the MGCB Editor with Visual Studio Code, you can use the *MonoGame for VSCode* extension. You should have installed this extension in [Chapter 02](../02_getting_started/index.md#installing-the-monogame-for-vscode-extension). With this extension install, anytime you have a code file open, you will see the MonoGame logo in the top-right of the code window like below: +To open the *Content.mgcb* content project file in the MGCB Editor from Visual Studio Code, you can use the *MonoGame for VSCode* extension. You should already have installed this extension in [Chapter 02](../02_getting_started/index.md#installing-the-monogame-for-vscode-extension). With this extension installed, anytime you have a code file open, you will see the MonoGame logo in the top-right of the code window like below: | ![Figure 5-1: MonoGame for VSCode extension icon](./images/mgcb-editor-icon.png) | | :------------------------------------------------------------------------------: | @@ -49,7 +49,7 @@ Clicking the MonoGame logo here will open the *Content.mgcb* content project fil ### [Visual Studio 2022](#tab/vs2022) -To open the *Content.mgcb* content project file in the MGCB Editor with Visual Studio 2022, you can use the *MonoGame Framework C# project templates* extension. Despite the name, this extension does more than just install the MonoGame project templates. With this extension installed, simply double-click the *Content.mgcb* content project file in the Solution Explorer panel and it will open it in the MGCB Editor. +To open the *Content.mgcb* content project file in the MGCB Editor from Visual Studio 2022, you can use the *MonoGame Framework C# project templates* extension. Despite the name, this extension does more than just install the MonoGame project templates. With this extension installed, simply double-click the *Content.mgcb* content project file in the Solution Explorer panel to open it in the MGCB Editor. ### [dotnet CLI](#tab/dotnetcli) @@ -82,8 +82,8 @@ To open the *Content.mgcb* content project file in the MGCB Editor using the dot In *Figure 5-2* above, you can see the user interface for the MGCB Editor: - **Toolbar**: Contains icon buttons for common actions such as creating new items, opening files, saving changes, and building content. -- **Project Panel**: Located on the left of the MGCB Editor, displays a hierarchical tree view of all content items added to the content project. The root node *Content* represents the root of the content project. -- **Properties Panel**: Located on the bottom left of the MGCB Editor, shows the properties of the currently selected item in the project panel. The properties available are based on the item type selected. +- **Project Panel**: Located on the left side of the MGCB Editor, it displays a hierarchical tree view of all content items added to the content project. The root node *Content* represents the root of the content project. +- **Properties Panel**: Located in the lower left corner of the MGCB Editor, it lists the properties of the currently selected item in the project panel. The available properties are based on the selected element type. - **Build Output Panel**: The large area to the right side outputs build messages, warnings, and errors when content is processed. ### Creating Folders to Organize Content @@ -105,7 +105,7 @@ You have now created a folder that will help organize the game's image assets. ### Adding Your First Asset -Now that we have a folder structure, we can add our first image asset to the project. For this example, we will use the MonoGame logo. Perform the following +Now that we have a folder structure, we can add our first image asset to the project. For this example, we will use the MonoGame logo. Perform the following: 1. First, download the MonoGame logo by right-clicking the following image and saving it as `logo.png` somewhere on your computer: @@ -146,7 +146,7 @@ Now that we have added our first asset, we can take a moment to understand what The following diagram demonstrates this workflow: | ![Figure 5-7: MonoGame Content Pipeline Workflow diagram showing the process flow from source files (Images, Audio, Fonts, Effects, Models) through the MGCB Editor to generate the Content.mgcb file, which is then processed by MonoGame.Content.Builder.Tasks to create compiled .xnb assets (Xnb formats for each type), which are finally loaded by the ContentManager at runtime](./images/content-pipeline-workflow-full.png) | -| :--------------------------------------------------------------------------------------------: | +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | **Figure 5-7: MonoGame Content Pipeline Workflow diagram showing the process flow from source files (Images, Audio, Fonts, Effects, Models) through the MGCB Editor to generate the Content.mgcb file, which is then processed by MonoGame.Content.Builder.Tasks to create compiled .xnb assets (Xnb formats for each type), which are finally loaded by the ContentManager at runtime** | The Content Pipeline offers significant advantages: @@ -163,10 +163,10 @@ To load assets in your game code, MonoGame provides the [**ContentManager**](xre They key methods for asset loading are: -| Method | Returns | Description | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [**Load<T>(string)**](xref:Microsoft.Xna.Framework.Content.ContentManager.Load``1(System.String)) | `T` | Loads the assets of type `T` that has been processed by the content pipeline. | -| [**Unload**](xref:Microsoft.Xna.Framework.Content.ContentManager.Unload) | `void` | Unloads all assets that have been loaded by that content manager instance. | +| Method | Returns | Description | +| --------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------- | +| [**Load<T>(string)**](xref:Microsoft.Xna.Framework.Content.ContentManager.Load``1(System.String)) | `T` | Loads the asset of type `T` that has been processed by the content pipeline. | +| [**Unload**](xref:Microsoft.Xna.Framework.Content.ContentManager.Unload) | `void` | Unloads all assets that have been loaded by that content manager instance. | > [!TIP] > When an asset is loaded for the first time, the [**ContentManager**](xref:Microsoft.Xna.Framework.Content.ContentManager) internally caches it. Loading the same asset again will return the cached version, avoiding extra disk reads. @@ -191,11 +191,11 @@ DungeonSlime/ ``` > [!NOTE] -> Notice that the compile asset has an .xnb extension, but when loading the asset in code, you refer to it without any extension. +> Notice that the compiled asset has an .xnb extension, but when loading the asset in code, you refer to it without any extension. ## Loading and Displaying Your First Asset -Now that we have the MonoGame logo added as an asset in the content project, we can modify the game to display the logo. In the *DungeonSlime* project open the `Game1.cs` file and perform the following: +Now that we have the MonoGame logo added as an asset in the content project, we can modify the game to display the logo. In the *DungeonSlime* project, open the `Game1.cs` file and perform the following: 1. Add a field to store the logo texture by inserting this line after the class declaration: @@ -224,9 +224,9 @@ Now that we have the MonoGame logo added as an asset in the content project, we ``` > [!NOTE] - > We will go more into detail about the [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) in the next chapter. + > We will go into more details about the [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch) in the next chapter. -The complete updated `Game1.cs` file should now look like this +The complete updated `Game1.cs` file should now look like this: [!code-csharp[](./snippets/game1.cs?highlight=10-11,27,44-51)] @@ -272,7 +272,7 @@ In this chapter, you accomplished the following: - You understood the Content Pipeline workflow and how MonoGame automates the process. - You loaded and displayed your first asset using the [**ContentManager**](xref:Microsoft.Xna.Framework.Content.ContentManager). -In the next chapter, we will explore working with textures in more detail and learning about different rendering options. +In the next chapter, we will explore working with textures in more detail and learn about different rendering options. ## Test Your Knowledge 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 3cd1fc7d..37136c24 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 @@ -3,12 +3,12 @@ title: "Chapter 06: Working with Textures" description: Learn how to load and render textures using the MonoGame content pipeline and SpriteBatch. --- -Textures are images that are used in your game to represent the visual graphics to the player, commonly referred to as *Sprites*. In [Chapter 05](../05_content_pipeline/index.md#loading-assets), you went through the steps of using the **Content Pipeline** to load the MonoGame *logo.png* texture and rendering it to the screen. +Textures are images that are used in your game to represent the visual graphics to the player, commonly referred to as *Sprites*. In [Chapter 05](../05_content_pipeline/index.md#loading-assets), you learned how to use the **Content Pipeline** to load the MonoGame *logo.png* texture and render it to the screen. In this chapter, you will: - Learn how to render a texture with the [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch). -- Explorer how to manipulate the way the texture is rendered using the parameters of the [**SpriteBatch.Draw**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch.Draw(Microsoft.Xna.Framework.Graphics.Texture2D,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Color)) method. +- Explore how to manipulate the way the texture is rendered using the parameters of the [**SpriteBatch.Draw**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch.Draw(Microsoft.Xna.Framework.Graphics.Texture2D,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Color)) method. ## Drawing a Texture @@ -28,11 +28,11 @@ Three methods are used when rendering with the [**SpriteBatch**](xref:Microsoft. As mentioned in [Chapter 03](../03_the_game1_file/index.md#the-game-loop), all rendering should be done inside the [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) method. The [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) method's responsibility is to render the game state that was calculated in [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)); it should not contain any game logic or complex calculations. -At the end of [Chapter 05](../05_content_pipeline/index.md#loading-assets), you added the following code to [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) in the `Game1.cs` file: +At the end of [Chapter 05](../05_content_pipeline/index.md#loading-assets), you added the following code to the [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)) method inside the `Game1.cs` file: [!code-csharp[](./snippets/draw.cs?highlight=6-7,9-10,12-13)] -These lines initialize the [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch), draw the logo at [**Vector2.Zero**](xref:Microsoft.Xna.Framework.Vector2.Zero) (0, 0), and complete the batch. When you ran the game and the logo appeared in the window's upper-left corner: +These lines initialize the [**SpriteBatch**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch), draw the logo at [**Vector2.Zero**](xref:Microsoft.Xna.Framework.Vector2.Zero) (0, 0), and submit the batch. When you ran the game, the logo appeared in the upper-left corner of the window: | ![Figure 6-1: The MonoGame logo drawn to the game window](./images/logo-drawn.png) | | :--------------------------------------------------------------------------------: | @@ -47,16 +47,16 @@ The [**SpriteBatch.Draw**](xref:Microsoft.Xna.Framework.Graphics.SpriteBatch.Dra | *color* | [**Color**](xref:Microsoft.Xna.Framework.Color) | The color mask (tint) to apply to the image drawn. Specifying [**Color.White**](xref:Microsoft.Xna.Framework.Color.White) will render the texture with no tint. | > [!TIP] -> Try adjusting the position and color parameters and see how they can affect the image being drawn. +> 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. -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 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: +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: [!code-csharp[](./snippets/draw_center_wrong.cs?highlight=9-16)] > [!TIP] -> In the example above, we multiply the [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) created by `0.5f` to halve the value instead of dividing it by `2.0f`. If you are not used to seeing this, it might seem strange at first, but it is actually an optimization technique. CPUs are able to perform multiplication operations much faster than division operations and reading `* 0.5f` is easily understood to be the same thing as `/ 2.0f` when reading. +> In the example above, we multiply the [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) created by `0.5f` to halve the values instead of dividing it by `2.0f`. If you are not used to seeing this, it might seem strange at first, but it is actually an optimization technique. CPUs are able to perform multiplication operations much faster than division operations and reading `* 0.5f` is easily understood to be the same thing as `/ 2.0f`. We have now set the position to half the window's dimensions, which should center the logo. Run the game to see the result. @@ -66,11 +66,11 @@ We have now set the position to half the window's dimensions, which should cente The logo is not centered as we expected it to be. Even though we set the *position* parameter to the center of the game window, the texture starts drawing from its *origin*, which is the upper-left corner in this example. So when we set the position to the screen's center, we are actually placing the logo's upper-left corner at that point, not the center of the texture. -One way to correct this is to subtract half the width and height of the texture from the game window's center position like so: +One way to solve this is to subtract half the width and height of the texture from the center position of the game window, as shown below: [!code-csharp[](./snippets/draw_center.cs?highlight=12-14)] -This offsets the position so that it correctly centers the image to the game window. +This offsets the position so that it correctly centers the image inside the game window. | ![Figure 6-3: The MonoGame logo drawn centered on the game window](./images/logo-centered.png) | | :--------------------------------------------------------------------------------------------: | @@ -84,16 +84,16 @@ Update your code to: This overload produces the same centered result but exposes all parameters that control rendering for a draw operation. Unlike engines that abstract much of these details away, MonoGame provides explicit control for a flexible custom rendering pipeline. Here is what each parameter does: -| Parameter | Type | Description | -| ----------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| *texture* | [**Texture2D**](xref:Microsoft.Xna.Framework.Graphics.Texture2D) | The [**Texture2D**](xref:Microsoft.Xna.Framework.Graphics.Texture2D) to draw. | -| *position* | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The X and Y coordinate position at which the texture will be rendered, relative to the *origin* parameter. | -| *sourceRectangle* | [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) | An optional region within the texture to be rendered in order to draw only a portion of the texture. Specifying `null` will render the entire texture. | -| *color* | [**Color**](xref:Microsoft.Xna.Framework.Color) | The color mask (tint) to apply to the image drawn. Specifying [**Color.White**](xref:Microsoft.Xna.Framework.Color.White) will render the texture with no tint. | -| *rotation* | `float` | The amount of rotation, in radians, to apply to the texture when rendering. Specifying `0.0f` will render the image with no rotation. | -| *origin* | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The X and Y coordinate origin point of the texture when rendering. This will affect the offset of the texture when rendered as well being the origin in which the texture is rotated around and scaled from. | -| *scale* | `float` | The amount to scale the image across the x- and y-axes. Specifying `1.0f` will render the image at its default size with no scaling. | -| *effects* | [**SpriteEffects**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects) | A [**SpriteEffects**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects) enum value to that specifies if the texture should be rendered flipped across the horizontal axis, the vertical axis, or both axes. | +| Parameter | Type | Description | +| ----------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| *texture* | [**Texture2D**](xref:Microsoft.Xna.Framework.Graphics.Texture2D) | The [**Texture2D**](xref:Microsoft.Xna.Framework.Graphics.Texture2D) to draw. | +| *position* | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The X and Y position at which the texture will be rendered, relative to the *origin* parameter. | +| *sourceRectangle* | [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) | An optional region within the texture to be rendered in order to draw only a portion of the texture. Specifying `null` will render the entire texture. | +| *color* | [**Color**](xref:Microsoft.Xna.Framework.Color) | The color mask (tint) to apply to the image drawn. Specifying [**Color.White**](xref:Microsoft.Xna.Framework.Color.White) will render the texture with no tint. | +| *rotation* | `float` | The amount of rotation, in radians, to apply to the texture when rendering. Specifying `0.0f` will render the image with no rotation. | +| *origin* | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The X and Y coordinates defining the position of the texture origin. This affects the texture's offset during rendering and serves as a reference point around which the texture is rotated and scaled. | +| *scale* | `float` | The scale factor applied to the image across the x- and y-axes. Specifying `1.0f` will render the image at its original size with no scaling. | +| *effects* | [**SpriteEffects**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects) | A [**SpriteEffects**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects) enum value that specifies if the texture should be rendered flipped across the horizontal axis, the vertical axis, or both axes. | | *layerDepth* | `float` | Specifies the depth at which the texture is rendered. Textures with a higher layer depth value are drawn on top of those with a lower layer depth value. **Note: This value will only apply when using `SpriteSortMode.FrontToBack` or `SpriteSortMode.BackToFront`. We will cover this in a moment.** | ### Rotation @@ -112,13 +112,13 @@ The reason the sprite did not rotate as expected is because of the `origin` para ### Origin -The `origin` parameter specifies the point of origin in which the sprite is rendered from, rotated from, and scaled from. By default, if no origin is set, it will be [**Vector2.Zero**](xref:Microsoft.Xna.Framework.Vector2.Zero), the upper-left corner of the sprite. To visualize this, see *Figure 6-5* below. The red square represents where the origin is for the sprite, and we can see how it is rotated around this origin point. +The `origin` parameter determines the reference point from which the sprite is rendered, rotated, and scaled. By default, if no origin is set, it will be [**Vector2.Zero**](xref:Microsoft.Xna.Framework.Vector2.Zero), the upper-left corner of the sprite. To visualize this, see *Figure 6-5* below. The red square represents where the origin is for the sprite, and we can see how it is rotated around this origin point. | ![Figure 6-5: Demonstration of how a sprite is rotated around its origin](./videos/top-left-origin-rotation-example.webm) | | :-----------------------------------------------------------------------------------------------------------------------: | | **Figure 6-5: Demonstration of how a sprite is rotated around its origin** | -To resolve the rotation issue we had, we need to need to change two things: +To resolve this rotation issue, we need to need to change two things: 1. Set the `origin` parameter to the center of the sprite instead of defaulting to the upper-left corner. 2. Change the `position` parameter back to the center of the screen. @@ -127,10 +127,10 @@ Update the code to: [!code-csharp[](./snippets/origin.cs?highlight=12-14,18-20)] -By moving the sprite's origin point to its center, this not only corrects the point of rotation, but also allows us to use the screen center position directly without needing additional position offset calculations. Running the game now shows the log properly centered and rotated 90°. +Moving the sprite's origin to its center not only solves the rotation problem, but also allows us to use the screen's center position directly, without the need for additional offset calculations. Running the game now shows the logo properly centered and rotated by 90°. > [!NOTE] -> When setting the `origin` parameter, it is based on the sprites width and height, so the center origin will be half the width and half the height of the sprite. +> The `origin` parameter is based on the sprite's width and height, so the center will be half the width and half the height of the sprite. | ![Figure 6-6: The MonoGame logo drawn rotated 90° and centered on the game window](./images/logo-rotated-centered.png) | | :--------------------------------------------------------------------------------------------------------------------: | @@ -148,17 +148,17 @@ For this example, we will first reset the rotation back to `0.0f` (removing the | :-------------------------------------------------------------------------------------------: | | **Figure 6-7: The MonoGame logo drawn scaled at 1.5x the size** | -Note that the sprite scaled up from the center. This is because we still have the `origin` parameter set as the center of the sprite. If we instead adjusted the code so the `origin` parameter was back in the upper-left corner like so: +Note that the sprite is scaled up from the center. This is because the `origin` parameter is still set to the center of the sprite. If we adjust the code so that the origin parameter is back in the upper-left corner, as in the listing below: [!code-csharp[](./snippets/scale_no_origin.cs?highlight=18-19)] -Then the scaling is applied from the origin in the upper-left corner producing the following result: +Then the scaling is applied from the origin in the upper-left corner, producing the following result: -| ![Figure 6-8: The MonoGame logo drawn scaled at 1.5x the size with the origin set in the upper-left corner](./images/logo-scaled-1.5x-zero-origin.png) | -| :----------------------------------------------------------------------------------------------------------------------------------------------------: | -| **Figure 6-8: The MonoGame logo drawn scaled at 1.5x the size with the origin set in the upper-left corner** | +| ![Figure 6-8: The MonoGame logo drawn with a scale factor of 1.5x and the origin set in the upper-left corner](./images/logo-scaled-1.5x-zero-origin.png) | +| :-------------------------------------------------------------------------------------------------------------------------------------------------------: | +| **Figure 6-8: The MonoGame logo drawn with a scale factor of 1.5x and the origin set in the upper-left corner** | -Scaling can also be applied to the x- and y-axes independently by providing it with a [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) value instead of a float value. For instance, we can scale the x-axis of the sprite by 1.5x and reduce the scale of the y-axis to 0.5x: +Scaling can also be applied to the x- and y-axes independently by providing a [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) value instead of a float value. For instance, we can scale the x-axis of the sprite by 1.5x and reduce the scale of the y-axis to 0.5x: [!code-csharp[](./snippets/scale_vector2.cs?highlight=21)] @@ -170,7 +170,7 @@ Which will produce the following result: ### SpriteEffects -The `effects` parameter is used to flip the sprite when rendered on either the horizontal or vertical axis, or both. This value for this parameter will be one of the [**SpriteEffects**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects) enum values. +The `effects` parameter is used to flip the sprite when rendered on either the horizontal axis, vertical axis, or both. The value for this parameter will be one of the [**SpriteEffects**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects) enum values. | SpriteEffect | Description | | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | @@ -200,7 +200,7 @@ Now the sprite is flipped both horizontally and vertically ### Color and Opacity -The `color` parameter applies a color mask to the sprite when it is rendered. Note that this is not setting the actual color of the image, just a mask that is applied, like a tint. The default value is [**Color.White**](xref:Microsoft.Xna.Framework.Color.White). So if we are setting it to [**Color.White**](xref:Microsoft.Xna.Framework.Color.White), why does this not affect the tinting of the sprite drawn? +The `color` parameter applies a color mask to the sprite when it is rendered. Note that this is not setting the actual color of the image, just a mask that is applied on top of the image. The default value is [**Color.White**](xref:Microsoft.Xna.Framework.Color.White). So if we are setting it to [**Color.White**](xref:Microsoft.Xna.Framework.Color.White), why does this not affect the tinting of the sprite drawn? When the `color` parameter is applied, each color channel (Red, Green, Blue) of the sprite is multiplied by the corresponding channel in the `color` parameter, where each channel is represented as a value between `0.0f` and `1.0f`. For [**Color.White**](xref:Microsoft.Xna.Framework.Color.White), all color channels are set to `1.0f` (255 in byte form), so the multiplication looks like this: @@ -212,7 +212,7 @@ Final Blue = Sprite Blue * 1.0f; Since multiplying by `1.0f` does not change the value, [**Color.White**](xref:Microsoft.Xna.Framework.Color.White) essentially preserves the original colors of the sprite. -For this example, we will reset the `effects` parameter back to [**SpriteEffects.None**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects.None) and update the `color` parameter to use [**Color.Green**](xref:Microsoft.Xna.Framework.Color.Green): +For this example, we will reset the `effects` parameter back to [**SpriteEffects.None**](xref:Microsoft.Xna.Framework.Graphics.SpriteEffects.None) and update the `color` parameter to use [**Color.Green**](xref:Microsoft.Xna.Framework.Color.Green): [!code-csharp[](./snippets/color.cs?highlight=16,22)] @@ -223,7 +223,7 @@ This produces the following result: | **Figure 6-12: The MonoGame logo with a green color tint applied** | > [!NOTE] -> The icon and the word "GAME" in the logo look black after using a [**Color.Green**](xref:Microsoft.Xna.Framework.Color.Green) because the Red, Blue Green components of that color are (`0.0f`, `0.5f`, `0.0f`). The Orange color used in the logo is [**Color.MonoGameOrange**](xref:Microsoft.Xna.Framework.Color.MonoGameOrange), which has the component values of (`0.9f`, `0.23f`, `0.0f`). When multiplying the component values, the result is (`0.0f`, `0.125f`, `0.0f`) which would be Red 0, Green 31, Blue 0 in byte values. So it is not quite fully black, but it is very close. +> The icon and the word "GAME" in the logo look black after using [**Color.Green**](xref:Microsoft.Xna.Framework.Color.Green) because the Red, Blue and Green components of that color are (`0.0f`, `0.5f`, `0.0f`). The Orange color used in the logo is [**Color.MonoGameOrange**](xref:Microsoft.Xna.Framework.Color.MonoGameOrange), which has the component values of (`0.9f`, `0.23f`, `0.0f`). When multiplying the component values, the result is (`0.0f`, `0.125f`, `0.0f`) which would be Red 0, Green 31, Blue 0 in byte values. So it is not quite fully black, but it is very close. > > This is why it is important to understand how the `color` parameter values are applied to the sprite when it is rendered. @@ -239,7 +239,7 @@ Which will produce the following result: ### Source Rectangle -The `sourceRectangle` parameter specifies a specific boundary within the texture that should be rendered. So far, we have just set this parameter to `null`, which specifies that the full texture should be rendered. If we only wanted to render a portion of the texture as the sprite, we can set this parameter value. +The `sourceRectangle` parameter specifies a region within the texture that should be rendered. So far, we have just set this parameter to `null`, which specifies that the full texture should be rendered. If we only wanted to render a portion of the texture as the sprite, we can set this parameter value. For instance, take the logo image we have been using. We can break it down into two distinct regions; the MonoGame icon and the MonoGame wordmark. @@ -256,7 +256,7 @@ We can see this in action by drawing the icon and the wordmark separately from t The following changes were made: - Two new [**Rectangle**](xref:Microsoft.Xna.Framework.Rectangle) values called `iconSourceRect` and `wordmarkSourceRect` that represent the boundaries of the MonoGame icon and wordmark regions within the logo texture were added. -- The *sourceRectangle* parameter of the `_spriteBatch.Draw` was updated to use the new `iconSourceRect` value. **Notice that we are still telling it to draw the `_logo` for the *texture*, we have just supplied it with a source rectangle this time.** +- The *sourceRectangle* parameter of the `_spriteBatch.Draw` call was updated to use the new `iconSourceRect` value. **Notice that we are still telling it to draw the `_logo` for the *texture*, we have just supplied it with a source rectangle this time.** - The *origin* parameter was updated to use the width and height of the `iconSourceRect`. Since the overall dimensions of what we will be rendering has changed due to supplying a source rectangle, the origin needs to be adjusted to those dimensions as well. - Finally, a second `_spriteBatch.Draw` call is made, this time using the `wordmarkSourceRect` as the source rectangle so that the wordmark is drawn. @@ -267,7 +267,7 @@ If you run the game now, you should see the following: | **Figure 6-15: The MonoGame icon and wordmark, from the logo texture, centered in the game window** | > [!NOTE] -> Making use of the `sourceRectangle` parameter to draw different sprites from the same texture is optimization technique that we will explore further in the next chapter. +> Making use of the `sourceRectangle` parameter to draw different sprites from the same texture is an optimization technique that we will explore further in the next chapter. ### Layer Depth