From 44ac7a51fc07ed7c63fe520c027d6a06b09b8565 Mon Sep 17 00:00:00 2001 From: "Simon (Darkside) Jackson" Date: Mon, 5 Aug 2024 10:22:31 +0100 Subject: [PATCH 1/7] Update MGCB editor doc --- articles/getting_started/tools/mgcb_editor.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/articles/getting_started/tools/mgcb_editor.md b/articles/getting_started/tools/mgcb_editor.md index 58b021f7..c8e45a38 100644 --- a/articles/getting_started/tools/mgcb_editor.md +++ b/articles/getting_started/tools/mgcb_editor.md @@ -3,10 +3,6 @@ title: MGCB Editor description: Learn about the MonoGame Content Builder (MGCB) Editor, the front-end GUI editor for MonoGame content builder projects. --- -# MGCB Editor - -MonoGame Content Builder (MGCB) Editor is the front-end GUI editor for MonoGame content builder projects. - ![MCGB Editor](images/mgcb_editor.png) The MGCB Editor has the following features: @@ -24,7 +20,7 @@ The MGCB Editor has the following features: ## Installation Instructions -The MGCB Editor is automatically installed (if you are using MonoGame's templates) and accessible by double-clicking an .mgcb file from Visual Studio 2022 (if you have the extension installed). +The MGCB Editor is automatically installed (if you are using MonoGame's templates) and accessible by double-clicking an .mgcb file from Visual Studio 2022 if you have the extension installed, or right-clicking and selecting "Open in MGCB editor" in VSCode if you have the `MonoGame Content Builder (editor)` extension installed. Alternatively, you can open the MGCB Editor from the .NET command line. This will only work if you are using the MonoGame templates and executing the command from the root directory of your project: @@ -32,10 +28,11 @@ Alternatively, you can open the MGCB Editor from the .NET command line. This wil dotnet mgcb-editor ``` -If it is the first time you run the tool, you might need to restore tools first (.NET should invite you to do so if you try the above command): - -``` -dotnet tool restore -``` +> [!NOTE] +> You will need to buid the project at least once in order for the .NET system to download and register the tool with your project utilizing the `dotnet-tools.json` configuration file located in the `.config` folder, or use the `dotnet tool restore` command shown below. +> +> ``` +> dotnet tool restore +> ``` See [Using MGCB Editor](../content_pipeline/using_mgcb_editor.md) for more information. From 813d6d4745ae084eae41db17ccc21d3c85ce082e Mon Sep 17 00:00:00 2001 From: "Simon (Darkside) Jackson" Date: Mon, 5 Aug 2024 10:22:48 +0100 Subject: [PATCH 2/7] Update platforms to change platform lib to class lib --- articles/getting_started/platforms.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/articles/getting_started/platforms.md b/articles/getting_started/platforms.md index 0d8bf60b..156fe7be 100644 --- a/articles/getting_started/platforms.md +++ b/articles/getting_started/platforms.md @@ -29,7 +29,7 @@ Below is a list of public platforms with their corresponding NuGet package, the Beside these target platforms, MonoGame provides additional templates for shared game logic and extensions to the MonoGame Content Pipeline that can be used across all platforms. -- [.NET Standard Library](#net-standard-library) +- [.NET Class Library](#net-class-library) - [Shared Project](#shared-project) - [Content Pipeline Extension](#content-pipeline-extension) @@ -93,11 +93,11 @@ You can test and deploy an iOS game on Windows by [pairing your Visual Studio 20 ## Other templates -### .NET Standard Library +### .NET Class Library -**Template ID**: mgnetstandard +**Template ID**: mglib -A project template to create [.NET Standard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) libraries to distribute code through a DLL. This can be used to redistribute libraries or to share code between multiple projects (like different platforms). +A project template to create [.NET](https://learn.microsoft.com/en-us/dotnet/standard/class-library-overview) libraries to distribute code through a DLL. This can be used to redistribute libraries or to share code between multiple projects (like different platforms). ### Shared Project From 3fa1946f812cb1bf8a26f2e5b6d4c59181e8c957 Mon Sep 17 00:00:00 2001 From: "Simon (Darkside) Jackson" Date: Mon, 5 Aug 2024 10:23:06 +0100 Subject: [PATCH 3/7] Add initial Content Pipeline docs --- .../content_pipeline/HowTo_GameContent_Add.md | 133 ++++++++++++++++++ .../HowTo_LoadContentLibrary.md | 124 ++++++++++++++++ .../images/open_mgcb_vscode.png | Bin 0 -> 26218 bytes 3 files changed, 257 insertions(+) create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_GameContent_Add.md create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_LoadContentLibrary.md create mode 100644 articles/getting_to_know/howto/content_pipeline/images/open_mgcb_vscode.png diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_GameContent_Add.md b/articles/getting_to_know/howto/content_pipeline/HowTo_GameContent_Add.md new file mode 100644 index 00000000..29c0e12d --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_GameContent_Add.md @@ -0,0 +1,133 @@ +--- +title: How to add Content to your game +description: Learn how to add content such as images or sounds to your game. +requireMSLicense: true +--- + +> [!TIP] +> For help with creating a project, please look at the [Creating a New Project](~/articles/getting_started/index.md) section of the Getting Started guide. + +## MonoGame Content Builder Tool (MGCB Editor) + +The MGCB editor is installed as a "local" DotNet tool with your project, utilizing the `dotnet-tools.json` configuration file located in the `.config` folder of your project. + +![MGCB Editor](../../../getting_started/tools/images/mgcb_editor.png) + +You will need to install either the Visual Studio 2022 MonoGame extension or the `MonoGame Content Builder (editor)` extension for VSCode. If you are not using the extension, you will need to manually install and use the [MGCB Editor](~/articles/getting_started/tools/mgcb_editor.md). + +> [!NOTE] +> This is technically optional, since you can edit the .mgcb files manually if you wish, but the editor is highly recommended for ease of use. + +If you are unsure of your setup, please follow one of the "Setting up your development environment" articles in the "Getting Started" section of the [Documentation Site](~/index.md) + +## Adding content + +First, you will need some content for your game. For this tutorial, use the following image of a ball: + +![Open Content](~/articles/getting_started/images/ball.png) + +Copy the image to your machine by using **right-click > Save Image As** and save it somewhere locally with the name “ball.png”. + +### [VSCode](#tab/vscode) + +> [!NOTE] +> Requires the `MonoGame Content Builder (editor)` extension for VSCode. + +Now open up your game project and look at the Explorer window. Expand the **Content** folder and open up **Content.mgcb** file by right-clicking on it and selecting `Open in MGCB editor`. + +![Open Content](./images/open_mgcb_vscode.png) + +### [Visual Studio](#tab/visualstudio) + +> [!NOTE] +> Requires the `MonoGame extension for Visual Studio` to be installed. + +Now open up your game project and look at the Solution Explorer window. Expand the **Content** folder and open up **Content.mgcb** file by double-clicking on it. + +![Open Content](~/articles/getting_started/images/3_open_content.png) + +If a text file opens instead, then right-click on **Content.mgcb** and select **Open With**, then select **MGCB Editor** in the list, click **Set as Default** and then click **OK**, then try again. + +> [!NOTE] +> If you do not see the **MGCB Editor** option when you right-click and select **Open With**, then please review the [Tools documentation](~/articles/getting_started/tools/index.md) for installing the MGCB Editor tool for your operating system. + +--- + +You should now see the MGCB Editor window open up. + +![MGCB Editor](~/articles/getting_started/images/3_mgcb_editor_tool.png) + +Your game content is managed from this external tool. You can add content to your game in one of the following ways: + +- **Add Existing Item** toolbar button +- **Edit > Add > Existing Item...** menu button +- **right-click > Add > Existing Item...** context menu + +Make sure the `Content` MGCB file is selected to the left, then click the **Add Existing Item** toolbar button. + +![Add Content](~/articles/getting_started/images/3_add_content.png) + +You should now be prompted to select a file. Select the “**ball.png**” image that you downloaded a moment ago. Once you have confirmed your selection, you will be asked whether to copy the file, add a link to it, or skip it. Make sure "**Copy the file to the directory**" option is selected and click **Add**. + +![Copy Content](~/articles/getting_started/images/3_copy_content.png) + +Now click the **Save** toolbar button and close the MGCB Editor tool. + +![Save Content](~/articles/getting_started/images/3_save_content.png) + +--- + +## Adding the content in your game + +Now that you have added the asset to the Content project, it is time to load it into your game. First, open up the `Game1.cs` class file and declare a new `ballTexture` variable of type `Texture2D` in the `Game1` class, so you can store the ball image into memory. + +```csharp +public class Game1 : Game +{ + Texture2D ballTexture; + + private GraphicsDeviceManager _graphics; + private SpriteBatch _spriteBatch; +``` + +Next, find the `LoadContent` method. Here, use [Content.Load()](xref:Microsoft.Xna.Framework.Content.ContentManager#Microsoft_Xna_Framework_Content_ContentManager_Load__1_System_String_) function to load the "ball" sprite and store it in the `ballTexture` parameter. `Content.Load()` requires you to specify what type of content you are trying to load, in this case it is a `Texture2D`. + +```csharp +protected override void LoadContent() +{ + // Create a new SpriteBatch, which can be used to draw textures. + _spriteBatch = new SpriteBatch(GraphicsDevice); + + // TODO: use this.Content to load your game content here + ballTexture = Content.Load("ball"); +} +``` + +Finally, find the Draw method to draw the ball onto the screen. This is done by: + +- Opening a SpriteBatch (an image drawing collection function). +- Adding the images you want to draw and specifying where you want to draw them. +- Then finally closing the SpriteBatch to commit the textures you want drawn to the screen. + +> [!NOTE] +> If you add multiple images, they will be drawn in the order you place them from back to front (each drawn on top of each other). + +As shown below: + +```csharp +protected override void Draw(GameTime gameTime) +{ + graphics.GraphicsDevice.Clear(Color.CornflowerBlue); + + // TODO: Add your drawing code here + _spriteBatch.Begin(); + _spriteBatch.Draw(ballTexture, new Vector2(0, 0), Color.White); + _spriteBatch.End(); + + base.Draw(gameTime); +} +``` + +Now run the game. You should get the following: + +![Game](~/articles/getting_started/images/3_game.png) diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_LoadContentLibrary.md b/articles/getting_to_know/howto/content_pipeline/HowTo_LoadContentLibrary.md new file mode 100644 index 00000000..772731b0 --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_LoadContentLibrary.md @@ -0,0 +1,124 @@ +--- +title: How to load content within a Game Library? +description: It may be desirable in some designs to load and draw content within the methods of a Game Library. For example, you may wish to distribute code that displays textures, models, or fonts (such as a DrawableGameComponent) in a .DLL. +requireMSLicense: true +--- + +There are two advanced techniques to load content files from within a game library, one in which the binary content is separate from the .DLL, and one in which the content is embedded within the .DLL. + +- [Loading Content in a custom Game Library](#loading-content-in-a-custom-game-library) +- [Embedding Content in a Game Library](#embedding-content-in-a-game-library) + +> [!IMPORTANT] +> These approaches assume you are using Visual Studio as your IDE, it is not recommended to use VSCode due to the nature in which assets are added to the project, although you are free to find the .NET commands to achieve this. + +## Loading Content in a custom Game Library + +In this method, the compiled content used by the game library is distributed in its own .xnb files, separate from the .DLL. + +This is the preferred method in most cases, as it makes the most efficient use of memory, especially when binary content is large. + +### To create compiled content + +1. Create a new solution that contains a game library project. +2. [Add a game content project](HowTo_GameContent_Add.md) to the solution. +3. In a library project, choose **Add, New Item**, and select "Resources File." +4. Select the game library project, then right-click and choose **Add Content Reference...**, and select the game content project you just created. + +When the solution is built, the resources in the content project will be compiled into binary .xnb files. These files will reside in the "bin\\x86\\Debug\\Content" directory of the game library project (if built as an x86 debug project). + +### To load compiled content within the Game Library + +1. Create a new class that is the child of [DrawableGameComponent](xref:Microsoft.Xna.Framework.DrawableGameComponent). +2. Define a new [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager) and, within the constructor of the new class, set its **RootDirectory** to the path where the compiled content is to be stored. + + For flexibility, the path string may be a parameter passed to the constructor by the game client. + + ```csharp + public static ContentManager LibContent; + + public GameLibComponent(Game game, string contentdirectory) : base(game) + { + LibContent = new ContentManager(game.Services); + LibContent.RootDirectory = contentdirectory; + } + ``` + +3. In the [Game.LoadContent](xref:Microsoft.Xna.Framework.Game#Microsoft_Xna_Framework_Game_LoadContent) method, load your content normally using your [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager). + + ```csharp + SpriteBatch spriteBatch; + Texture2D LibTexture; + + protected override void LoadContent() + { + spriteBatch = new SpriteBatch(GraphicsDevice); + LibTexture = LibContent.Load("mytexture"); + } + ``` + +## Embedding Content in a Game Library + +You may also embed binary content resources directly in the Game Library and load them from within. This technique requires the declaration of those resources in a reference file. It allows you to distribute code that displays textures, models, or fonts (such as a [DrawableGameComponent](xref:Microsoft.Xna.Framework.DrawableGameComponent)) in a .DLL without distributing the .xnb files separately. + +> [!NOTE] +> Be aware that all embedded resources are loaded into memory with the .DLL, and cannot be unloaded from main memory. For this reason, this technique is not recommended for most applications, and should only be used when the content to embed is very small. + +### To add content to a Game Library as references + +1. Build an existing project containing the content you wish to add. +2. In a library project, choose **Add**, **New Item**, and select "Resources File". +3. If the **Resource Designer** is not opened automatically, double-click the .resx file in the **Solution Explorer**. +4. From the **Resource Designer**, choose **Add Resource**, **Add Existing File**. +5. Navigate to the "bin\\x86\\Debug\\Content" directory of the project that built the content you wish to add. + + > This assumes it was built as an x86 Debug project. + +6. Select the .xnb files for the content you wish to add to the library. + + > Ensure the dialog box is displaying "All Files". + +Once content has been added to the **Resource Designer**, any code running from within the Library can load the content with a special [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager). + +### To load embedded content within a Game Library + +1. Create a new class that is the child of [DrawableGameComponent](xref:Microsoft.Xna.Framework.DrawableGameComponent). +2. Define a new [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager). +3. Within the constructor of the new class, create a new instance of the [ResourceContentManager](xref:Microsoft.Xna.Framework.Content.ResourceContentManager) class and assign it to your [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager). + + The second parameter to the [ResourceContentManager](xref:Microsoft.Xna.Framework.Content.ResourceContentManager) constructor identifies the resource project that contains your embedded resources. + + ```csharp + public static ContentManager LibContent; + + public GameLibComponent(Game game) + : base(game) + { + ResourceContentManager resxContent; + resxContent = new ResourceContentManager(game.Services, ResourceFile.ResourceManager); + LibContent = resxContent; + } + ``` + +4. In the [Game.LoadContent](xref:Microsoft.Xna.Framework.Game#Microsoft_Xna_Framework_Game_LoadContent) method, load your content normally using your [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager). + + ```csharp + SpriteBatch spriteBatch; + Texture2D LibTexture; + + protected override void LoadContent() + { + spriteBatch = new SpriteBatch(GraphicsDevice); + LibTexture = LibContent.Load("mytexture"); + } + ``` + +## See Also + +### Reference + +- [Game Class](xref:Microsoft.Xna.Framework.Game) +- [Game.LoadContent](xref:Microsoft.Xna.Framework.Game#Microsoft_Xna_Framework_Game_LoadContent) +- [UnloadContent](xref:Microsoft.Xna.Framework.Game.UnloadContent) +- [Game Members](xref:Microsoft.Xna.Framework.Game) +- [Microsoft.Xna.Framework Namespace](xref:Microsoft.Xna.Framework) diff --git a/articles/getting_to_know/howto/content_pipeline/images/open_mgcb_vscode.png b/articles/getting_to_know/howto/content_pipeline/images/open_mgcb_vscode.png new file mode 100644 index 0000000000000000000000000000000000000000..98031bcd743255719ce64b9a6cfb18bfee3038a8 GIT binary patch literal 26218 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%0D~!d;qNe%_e$EL8U%S6hRekvINA$)|iZDIj zqsJT)5_c7~uvcTXhLvEaQhlYAv~<=(og#C=^k02_tb&5t zyKMrQL%{idOi3yD%^`i^Z8Mai2BR6bFLN3UQ23_{ze0Qho0j*bP z5z_NOH5I31NJKO?G7M2 zCWQBX6A}gPqQ6aU$0jA&PF9(OZZkk25Kd81+VY7PwzixAD|9K_5XKQ^9~l!Lf6U1K zwE*yQg{OAC+sr^ZU_-5_B?lC*9~mX+pr_80jEY)zE-ojAzsJV`{nqK_jG04vKn@9P zPD58Wb^47{jXqYi>&C64%eh?oRt~i^{=Bmv5%4F+0%)&Yz~TLvo~~kJQv|K3s@fBL zA$P#epMaxV{#h}O76|8{)}6hKETAhG&B4sX`>!KYQ!!t;oMsxF+m%b~g&DpZR!-Iu zQ*pdBF$qHYnY~Eg^_JpmIlhBLB7>t*C!|L?xSrL+ABmVi+U1~wa=RBp$<-b;HiCXi zrdA{OYq8a@g_QN>PP18a=5#{`cT_df>7JB#JaCdY5Upo>AN{>yOf9CGzP{q6rKJv> z*JFjMNSF_wH!K@a)87a)09lBCvb`|(=`X3dLiaK7R|TWbJw1hW%h9z*E>2FQ&_U3B zfhS1Ytsg2H8XRnFot)Vd4%VAvh1>?U&vth-o57_jUuzIIUo1X{|-=4_CU_v_iJhbb(NRY zo;s#GvoQUGxl!ET4O-WS7q6nc6_tosyZI?!nZGwVro?O(_^VT4=D46%|8!Mz>_<|` zM~44{r-RqHRbW@sGW?iD9IxmHW>m_6U+&#>s6EgjF|8^r|GjE+_xpO)bvz_>lQaNb6wXVX>4Q8aS zRakV+lmz?5OwUgYnI0}OW_>MoV|@O1?RPMk1^Q2_gBCJw&he=gJ<@fZxdPQm*!}}Y zlUu6}EC^fa6f?!3tt5lXZs$PuAwUco<-3!X(YEMY`3@obm;uYZZT~ol;f=@UpHHc> z8a_T^y$dh!Wfx;FS_>D^*NYQ+*CtAsl< zePwWJ+h4zL!$bC2%kRGlDX`U+@JsI1aq-SnjRH^HzoB<$x_;pC zKPvx#OLFe_GqMia2m!dd(?Tm#N?&B3l?51{AQJ$?8KN%%d+mGX`ya;BlyLqJIt2$zI-825HaAPta_cYF4O`T>?afM9SXzQ~J-?-e zDXwRkO5aJ8QKOiu^27pSYGJ_!&H&E(sfj4KCADIQOQU6tzMX@^*Zlmj@85-SenZSO zt*rriT?B{Qv^fpEJ$b=XM`__p%*J0oTGhT10!#Q#V}d*OjFK##m`y$4;!O2%X_Hl7 zUmr+>8&BTU8ybAK`)C+JOhI7_90~}{;Hv2CVQBe^T!2?8A z?txs@9S6jp<*c1kc@xv*4sUE5Ptp*;AcCHMMdw9k3GR$ zJb2UZ4Gr3_U-tmi^m7f3pAPk9XE(a)2KUTY4eo<-BwvTfYW+lQz`zF$4OU(qE30n{ z|J-I`0#6DWy8y8n*x=~mqNGKQ_5f~oI*V}8O+0*hI0fX2;4=n5<$>O_cW?liyN;Qe zrqfQ3o}oncWeBQy&INPpJ4i&7&w*{g*PuCJeKLk4o1W)n{DzgZAX+;)J=3CYaGvo- zs$2aV$^<|q3k2Bx77X)ZnF^4l@!&%U;IjLT?>Hly&ihK5Z@1gjx$jH~fJ74{B3o}k z`NP5C5fH`>lz+du0I1$>=xc5h=%=kM2az;TD`~^6Xk%bt01$oog3*%8mmoAID0NjB z)QvwS`=S664eJ3~(8*wYbau*v=CY|v2lplYY1XYo2oBfjH56AnibTuRBz~4-Cf=h{=?c@ zL6FXa)R>sHig{8kL%1}cgk+S(urhURZ7rWy2?B+|g03>NvIc7`J2Z*~gjbecvv$7( e6A Date: Tue, 6 Aug 2024 17:04:28 +0100 Subject: [PATCH 4/7] Content Pipeline update --- .../howto/content_pipeline/HowTo_Add_XML.md | 131 +++++++++++ .../HowTo_ExtendFontProcessor.md | 206 ++++++++++++++++++ .../HowTo_Extend_Processor.md | 135 ++++++++++++ .../HowTo_GenerateCustomXML.md | 116 ++++++++++ .../howto/content_pipeline/HowTo_Load_XML.md | 92 ++++++++ .../content_pipeline/HowTo_UseCustomXML.md | 41 ++++ .../images/HowTo_Load_XML_Final.png | Bin 0 -> 11621 bytes ..._content_properties_referencesSelected.png | Bin 0 -> 46019 bytes .../images/mgcb_editor_references_window.png | Bin 0 -> 21640 bytes .../images/mgcg_editor_new_spritefont.png | Bin 0 -> 18318 bytes .../howto/content_pipeline/index.md | 22 +- .../content_pipeline/CP_AddCustomProcImp.md | 28 +++ .../content_pipeline/CP_Architecture.md | 55 +++++ .../content_pipeline/CP_Class_Library.md | 42 ++++ .../content_pipeline/CP_Content_Advanced.md | 43 ++++ .../content_pipeline/CP_CustomParamProcs.md | 116 ++++++++++ .../whatis/content_pipeline/CP_Customizing.md | 58 +++++ .../whatis/content_pipeline/CP_DOM.md | 20 ++ .../whatis/content_pipeline/CP_Overview.md | 68 ++++++ .../content_pipeline/CP_SpriteFontSchema.md | 76 +++++++ .../content_pipeline/CP_StdImpsProcs.md | 47 ++++ .../content_pipeline/CP_StdParamProcs.md | 26 +++ .../CP_Tips_For_Developing.md | 81 +++++++ .../content_pipeline/CP_XML_Elements.md | 89 ++++++++ .../whatis/content_pipeline/index.md | 50 ++++- .../whatis/content_pipeline/toc.yml | 12 +- .../whatis/images/CP_CustomData.png | Bin 0 -> 28100 bytes .../whatis/images/CP_CustomImporter.png | Bin 0 -> 26735 bytes .../images/ContentPipelineTypes_small.png | Bin 0 -> 30789 bytes 29 files changed, 1550 insertions(+), 4 deletions(-) create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_Add_XML.md create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_ExtendFontProcessor.md create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_Extend_Processor.md create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_GenerateCustomXML.md create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_Load_XML.md create mode 100644 articles/getting_to_know/howto/content_pipeline/HowTo_UseCustomXML.md create mode 100644 articles/getting_to_know/howto/content_pipeline/images/HowTo_Load_XML_Final.png create mode 100644 articles/getting_to_know/howto/content_pipeline/images/mgcb_editor_content_properties_referencesSelected.png create mode 100644 articles/getting_to_know/howto/content_pipeline/images/mgcb_editor_references_window.png create mode 100644 articles/getting_to_know/howto/content_pipeline/images/mgcg_editor_new_spritefont.png create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_AddCustomProcImp.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_Architecture.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_Class_Library.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_Content_Advanced.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_CustomParamProcs.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_Customizing.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_DOM.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_Overview.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_SpriteFontSchema.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_StdImpsProcs.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_StdParamProcs.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_Tips_For_Developing.md create mode 100644 articles/getting_to_know/whatis/content_pipeline/CP_XML_Elements.md create mode 100644 articles/getting_to_know/whatis/images/CP_CustomData.png create mode 100644 articles/getting_to_know/whatis/images/CP_CustomImporter.png create mode 100644 articles/getting_to_know/whatis/images/ContentPipelineTypes_small.png diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_Add_XML.md b/articles/getting_to_know/howto/content_pipeline/HowTo_Add_XML.md new file mode 100644 index 00000000..ed7745a3 --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_Add_XML.md @@ -0,0 +1,131 @@ +--- +title: How to add a custom XML Content File to a Project? +description: Describes how to add custom game data as an XML file through the Content Pipeline. +requireMSLicense: true +--- + +Custom game data that is expressed in an XML format can be easily integrated into your game through the MonoGame Content Pipeline. + +This example demonstrates the procedure for integrating custom XML data into the content project of a simple game for the Windows platform. + +> [!IMPORTANT] +> This tutorial assumes you are using Visual Studio as your IDE, for VSCode, follow [this guide from Microsoft](https://learn.microsoft.com/en-us/dotnet/core/tutorials/library-with-visual-studio-code?pivots=dotnet-8-0) for creating multi-project solutions from the command-line utilizing the MonoGame Project and MonoGame Class library templates. + +### To define game data + +Within the MonoGame solution, you create a new Windows Game Library project. + +1. Right-click the solution node, point to `Add`, click `New Project`, and then select the `MonoGame Game Library` template. + + > [!TIP] + > A `MonoGame Game Library` project is created instead of a Content Pipeline Extension Library project so that the class we will define can be used by both the [Content Importer](https://docs.monogame.net/api/Microsoft.Xna.Framework.Content.Pipeline.ContentImporter-1.html) that runs at build-time and the [Content Loader](xref:Microsoft.Xna.Framework.Content.ContentManager#Microsoft_Xna_Framework_Content_ContentManager_Load__1_System_String_) at game runtime. + + > [!IMPORTANT] + > For MonoGame `3.8.2` and below, make sure to keep the project at `.NET 6` or below. The MGCB tool for 3.8.2 CANNOT understand or read .NET 8 libraries as it is compiled with .NET 6. The Game project can use a higher version, but library projects must stay at `.NET 6` else they cannot be read. + +2. In the `Name` box, type `MyDataTypes`, and then click `OK`. + +3. In the `Solution Explorer`, delete the existing `Game1.cs` as it is not needed. + +4. `right-click` and select `Add -> Add New Item` to add a new class, call it `PetData.cs` + +5. Double click on `PetData.cs` and replace its contents with the following code to define the `PetData` class. + + ```csharp + namespace MyDataTypes + { + public class PetData + { + public string Name; + public string Species; + public float Weight; + public int Age; + } + } + ``` + +6. Build the `MyDataTypes` project for debug or release to generate the library's dll file (note which profile you used). + +### Add an XML file using the custom data to the game content + +In this procedure, the `MyDataTypes` library is added as a reference in the content project. + +1. Build the `MyDataTypes` project, making not of whether it is a `Debug` or `Release` build. + + > [!NOTE] + > It is recommended to always use the same built type for building library projects, as they normally only contain "types" it is safe to just build them as `Release`. The reason being that you cannot CHANGE the reference once made in the MGCB editor and it is not affected by the `Build Type` used to generate the project. + +2. In the `Solution Explorer` or your game project, `right-click` the game content folder, point to `Add`, and then click `New Item`. + +3. In the `Add New Item` dialog box, type `pets.xml` as the file name, and then click `OK`. + +4. Replace the contents of the template file with the following XML code: + + ```xml + + + + + Fifi + Dog + 11 + 6 + + + Bruno + Dog + 21 + 12 + + + Chloe + Cat + 6 + 3 + + + Pickles + Hamster + 0.4 + 1 + + + + ``` + + > [!TIP] + > TO learn how to generate the custom MonoGame XML content from your own classes, refer to the [How to Generate custom XML](HowTo_GenerateCustomXML.md) guide. + +5. Open the `MGCB Editor` from the `Game` project (not the library) by selecting the `.mgcb` file and either Double-clicking it (Visual Studio) or Right-clicking it and selecting `Open` (VSCode). + + > [!TIP] + > If you have any issues opening the MGCB content project, please refer to the [How to load content](HowTo_GameContent_Add.md) guide. + +6. In the `MGCB Editor`, select the "Content" node and the top and then locate the `References` section in the `Properties` window, as shown below: + + [MGCB Editor window properties](./images/mgcb_editor_content_properties_referencesSelected.png) + +7. Click the `References` VALUE field and a new window should pop up allowing you to manage the references for the MGCB project: + + [MGCB editor references window](./images/mgcb_editor_references_window.png) + +8. In the `Reference Editor` window, click `Add`, and navigate to the `MyDataTypes` project and locate the built `dll`, normally under `bin/release/net8.0` (or DEBUG if you use the debug build). Once selected, click `Open` and the reference should be added to the Reference Editor as shown above. + + > [!TIP] + > If the folder is empty, return to visual studio and build the `MyDataTypes` project, if it is not built, there is no dll. + > And make sure to choose either the `debug` or `release` folder depending on how the class library is built. + +9. Click on `Add Existing` from the `Edit -> Add` options in the menu (or the `Add Existing` toolbar button) and select the `pets.xml` file. + +When you press `F6` to build the solution, it should build successfully, including the custom game content imported from the XML file. + +> [!IMPORTANT] +> Adding the the class library of the data types project to the Content project is critical, else the Content Pipeline does not know how to serialize or deserialize the custom data you have defined. +> Although it is possible to simply serialize `value types` or `value type arrays` without this step. + +To load the data at runtime, see the tutorial [Loading XML Content at Runtime](HowTo_Load_XML.md). + +## See Also + +- [Using an XML File to Specify Content](HowTo_UseCustomXML.md) +- [Adding Content to a Game](HowTo_GameContent_Add.md) diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_ExtendFontProcessor.md b/articles/getting_to_know/howto/content_pipeline/HowTo_ExtendFontProcessor.md new file mode 100644 index 00000000..c3870589 --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_ExtendFontProcessor.md @@ -0,0 +1,206 @@ +--- +title: How to Extend the Font Description Processor to Support Additional Characters? +description: Describes the process of developing a custom content processor needed to add additional characters to a FontDescription object based on the text that is required by the game. +requireMSLicense: true +--- + +In a font description (.spritefont) file, the `` area can be used to add additional characters to a font description. This enables you to use a [SpriteFont](xref:Microsoft.Xna.Framework.Graphics.SpriteFont) to render an additional range of characters. + +For some languages, this approach is not ideal. For example, Chinese and Japanese both have many thousands of characters. Adding the full range of characters to `` dramatically increases the size of the font asset and the time required to build the font asset. A better solution adds individual characters whenever the specific characters are needed. You can create a custom content processor to implement this solution. + +In this example, a file called _messages.txt_ contains all the text rendered by the game. The custom processor adds all the characters contained in the text in this file to a [FontDescription](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.FontDescription). Then it processes the object in the standard way using the base [FontDescriptionProcessor](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.FontDescriptionProcessor) functionality. All the characters in messages.txt will then be available to the [SpriteFont](xref:Microsoft.Xna.Framework.Graphics.SpriteFont) object at run time. + +> [!IMPORTANT] +> This tutorial assumes you are using Visual Studio as your IDE, for VSCode please adapt the IDE interactions appropriately. + +## Using the Font Description Processor + +1. create a new MonoGame project called `FontGame` using the MonoGame template of your choice (for simplicity choose a desktop template) + +2. Add a new `SpriteFont` called `DefaultFont` to a game project by opening the MGCB Editor, then right-click the `Content` node, click **Add**, and then click **New Item**. + +3. Add the new SpriteFont to the game by selecting the **SpriteFont Description (.spritefont)** template, name the font `DefaultFont` and then click **Add**. + + ![Adding the `DefaultFont` SpriteFont](./images/mgcg_editor_new_spritefont.png) + +4. Modify this file to use an existing font and any additional characteristics you prefer. + + For more information, see [Sprite Font XML Schema Reference](../../whatis/Content_Pipeline/CP_SpriteFontSchema.md). + +5. Add a new text file named `messages.txt` to the game project by right-clicking on the FontGame project node in Solution Explorer, click **Add**, and then click **New Item**. + +6. Select the **Text File** template, enter **messages.txt** for the file name, and then click **Add** to add the text file to the game. + +7. In the new text file, enter any messages that will be printed by the font described in the Sprite Font file. + + > We will use the method [File.ReadAllText](http://msdn.microsoft.com/en-us/library/ms143369.aspx) to read the text in this file. This method requires a carriage return ("\\r") or line feed ("\\n") after the last string, so be sure to follow the last line of text in the file with a carriage return or line feed. + +### To create the new content processor project + +The Content Pipeline is part of the build process and it is separate from your game code, therefore you need to create a new assembly that contains the code developed in this topic. Creating this new assembly project is the first step in developing a new processor. + +1. To add the new processor project to the game solution, go to Solution Explorer, right-click the **Solution** node, click **Add**, and then click **New Project**. + +2. In the dialog box, select the template **MonoGame Content Pipeline Extension (MonoGame Team))**, enter `MyFontProcessor` in the **Name** field, and then click **OK**. The new project automatically contains references to the MonoGame Framework run-time and design-time Content Pipeline assemblies. + +### To extend the font processor + +1. Open the `Processor1.cs` which is the template Content Processor example. + +1. Add the following lines of code, after the last `using` statement: + + ```csharp + using System.IO; + using System.ComponentModel; + ``` + +1. Remove the placeholder code that assigns the processor input (`TInput`) and output (`TOutput`) types, including the `Process" method. They will not be needed. + +1. Change the base class of `Processor1` from [ContentProcessor](xref:Microsoft.Xna.Framework.Content.Pipeline) to [FontDescriptionProcessor](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.FontDescriptionProcessor), which identifies it as a SpriteFont processor. + + > [NOTE] + > You can read all about the different types of built-in processors in the API docs [Content.Pipeline.Processors](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors) + +1. Change the class name and the Processor display name to something a little more unique, so that we know which processor we are selecting, from "Processor1" to "**MyFontProcessor**". + + ```csharp + [ContentProcessor(DisplayName = "MyFontProcessor")] + internal class MyFontProcessor : FontDescriptionProcessor + ``` + +1. Add a new processor parameter (property) to the the class declaration and adorn it with [C# Attributes](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-attributes/) to state additional processing parameters for the property. + + This parameter stores the name of the text file that stores the messages displayed by the game. + + ```csharp + [DefaultValue("messages.txt")] + [DisplayName("Message File")] + [Description("The characters in this file will be automatically added to the font.")] + public string MessageFile + { + get { return messageFile; } + set { messageFile = value; } + } + private string messageFile = @"../messages.txt"; + ``` + + > [!NOTE] + > As the "messages.txt" file is not in our content project, we need to supply a path "relative" to the "Content" project where the Content Processor is running from. + > Hence the path to the file is denoted as `@"../messages.txt"`. + +1. Add a new [Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.FontTextureProcessor) method override to match the following code: + + ```csharp + public override SpriteFontContent Process(FontDescription input, ContentProcessorContext context) + {} + ``` + + This modification replaces the template parameter and return types with the proper types needed for the extended font processor. + +1. Inside the `Process` method, register a Content Pipeline dependency on `messages.txt`, which will read specifically from a file by that name from the Game Project. + + This dependency tells the Content Pipeline that if messages.txt changes, the font must be rebuilt. + + ```csharp + string fullPath = Path.GetFullPath(MessageFile); + + context.AddDependency(fullPath); + ``` + +1. Next, we add functionality to read the contents of the file and add each letter to the input font one by one. Note that the [Characters](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.FontDescription) collection keeps track of duplicates automatically, it is not necessary for the user to make sure that each letter is added only once. The **Characters** collection will contain only one instance of each character, no matter how many times **Add** has been called. + + ```csharp + string letters = File.ReadAllText(fullPath, System.Text.Encoding.UTF8); + + foreach (char c in letters) + { + input.Characters.Add(c); + } + ``` + + > [!NOTE] + > In this example, messages.txt has been saved with Unicode UTF-8 encoding, which is the default encoding format that is specified in the call to [File.ReadAllText](http://msdn.microsoft.com/en-us/library/ms143369.aspx). However, the default file encoding format for text files that have been added to a Visual Studio project is `Western European (Windows) encoding`, corresponding to code page `1252`. If your text file uses a different encoding, specify the character encoding as follows: + + ```csharp + string letters = File.ReadAllText( fullPath, System.Text.Encoding.GetEncoding( 1252 ) ); + ``` + +1. Finally, call the `Base` **Process** method of the [FontDescriptionProcessor](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.FontDescriptionProcessor) to build the font with the newly requested characters. + + ```csharp + return base.Process(input, context); + ``` + +Done. If your txt file is located elsewhere, make sure to update the path to the file appropriately, FROM the content project folder in the Processor project. + +Final code: + +```csharp +using Microsoft.Xna.Framework.Content.Pipeline; +using Microsoft.Xna.Framework.Content.Pipeline.Graphics; +using Microsoft.Xna.Framework.Content.Pipeline.Processors; +using System.ComponentModel; +using System.IO; + +namespace FontProcessor +{ + [ContentProcessor(DisplayName = "MyFontProcessor")] + internal class MyFontProcessor : FontDescriptionProcessor + { + public override SpriteFontContent Process(FontDescription input, ContentProcessorContext context) + { + string fullPath = Path.GetFullPath(MessageFile); + + context.AddDependency(fullPath); + string letters = File.ReadAllText(fullPath, System.Text.Encoding.UTF8); + + foreach (char c in letters) + { + input.Characters.Add(c); + } + return base.Process(input, context); + } + + [DefaultValue("messages.txt")] + [DisplayName("Message File")] + [Description("The characters in this file will be automatically added to the font.")] + public string MessageFile + { + get { return messageFile; } + set { messageFile = value; } + } + private string messageFile = @"../messages.txt"; + } +} +``` + +### Associate the custom font processor with the sprite font in the MGCB tool + +1. Compile the solution to build **MyFontProcessor**, as you need to add your custom font processor as an available content processor for the content pipeline. + +1. Open the MGCB tool for your `Content` project, click (select) the **Content** node, navigate down to the properties section and then click in the **References** property. + + [MGCB Editor window properties](./images/mgcb_editor_content_properties_referencesSelected.png) + +1. Navigate to the `dll` for the built `MyFontProcessor` project (which by default is in `\FontGame\MyFontProcessor\bin\debug\net6.0`), select it and click "open". + + > [IMPORTANT] + > Ensure that the processor project is always up to date when the main game is built, you need to create a project dependency. Also use either "debug" or "Release" build types for the processor project, the MGCB tool will NOT dynamically select it when you change the Projects build type. + +1. Build the Content Project to ensure everything is connected as it should be. + +1. Select the `.spritefont` file, and then in the **Properties** window change the `processor` to`MyFontProcessor` in the drop-down list associated with the **ContentProcessor** field. + +When you build the solution, the new processor adds the characters in the messages.txt file to the list of characters available to the [SpriteFont](xref:Microsoft.Xna.Framework.Graphics.SpriteFont). + +> [!NOTE] +> To debug a Content Pipeline importer or processor, add the following line to the processor code to launch the debugger. +> +> ```csharp +> System.Diagnostics.Debugger.Launch(); +> ``` + +## See Also + +[Extending a Standard Content Processor](./HowTo_Extend_Processor.md) +[Adding New Content Types](../../whatis/Content_Pipeline/CP_Content_Advanced.md) diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_Extend_Processor.md b/articles/getting_to_know/howto/content_pipeline/HowTo_Extend_Processor.md new file mode 100644 index 00000000..6ac669f4 --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_Extend_Processor.md @@ -0,0 +1,135 @@ +--- +title: How To Extend a Standard Content Processor? +description: Describes how MonoGame lets you modify or extend the behavior of any standard Content Pipeline processor that ships with the product. +requireMSLicense: true +--- + +MonoGame lets you modify or extend the behavior of any standard Content Pipeline processor that ships with the product. See [Standard Content Importers and Content Processors](../../whatis/Content_Pipeline/CP_StdImpsProcs.md) for a description of standard processors. + +Because there are so many asset variants supported by different digital content creation (DCC) tools, it is often useful to be able to modify how one of the standard processors operates. The following examples illustrate some of the kinds of things you might want to do. + +> [!TIP] +> The following code samples are provided only for demonstration. Most of the functionality described is already available by using parameters on a standard processor. + +## Adding a Scaling Operation to a Processor + +There are many reasons why you might want to modify the existing functionality of a standard processor. Here is one example. If your source assets and your game are at different scales, you may want the processor to scale each model automatically at build time. You can implement such automatic scaling by overriding the [Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor) method of the [ModelProcessor](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor) class, which generates a [Model](xref:Microsoft.Xna.Framework.Graphics.Model). In the override, you first scale the entire scene, and then invoke the base class functionality to process as usual. + +The following code illustrates this technique: + +```csharp + [ContentProcessor] + class ScalingModelProcessor : ModelProcessor + { + public override ModelContent Process( + NodeContent input, ContentProcessorContext context ) + { + MeshHelper.TransformScene( input, Matrix.CreateScale( 10.0f ) ); + return base.Process( input, context ); + } + } +``` + +## Generating Additional Data + +In some cases, you may want to add information to a game asset that a standard processor would not. For example, if a custom effect you want to apply requires tangent or binormal data, you can extend the standard model processor to build this additional data into the asset. To do this, you override the [Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor) method of the [ModelProcessor](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor) class. In the override, navigate the [NodeContent](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.NodeContent) hierarchy of the game asset, and call [CalculateTangentFrames](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.MeshHelper) for each [MeshContent](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.MeshContent) object you find. + +The following code shows how to do this: + +```csharp + [ContentProcessor] + class ModelProcessorWithTangents : ModelProcessor + { + public override ModelContent Process( NodeContent input, ContentProcessorContext context ) + { + GenerateTangentFramesRecursive( input ); + return base.Process( input, context ); + } + + private void GenerateTangentFramesRecursive( NodeContent node ) + { + MeshContent mesh = node as MeshContent; + if (mesh != null) + { + MeshHelper.CalculateTangentFrames( mesh, VertexChannelNames.TextureCoordinate( 0 ), + VertexChannelNames.Tangent( 0 ), VertexChannelNames.Binormal( 0 ) ); + } + + foreach (NodeContent child in node.Children) + { + GenerateTangentFramesRecursive( child ); + } + } + } +``` + +## Changing the Processors Called for Child Objects + +Another useful technique is to override a standard processor, and to change how child objects are processed just by changing the processors they use. + +Consider, for example, the hierarchy of calls through which textures in a model are processed: + +- The standard [ModelProcessor.Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor) method is called to process a [NodeContent](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.NodeContent) object that represents the root of a scene. +- `ModelProcessor.Process` in turn calls the [ModelProcessor.ConvertMaterial](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor) method once for every [MaterialContent](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.MaterialContent) object used in the scene. +- `ModelProcessor.ConvertMaterial` in turn invokes the [MaterialProcessor.Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.MaterialProcessor) method on the [MaterialContent](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.MaterialContent) object passed to it. +- `MaterialProcessor.Process` in turn calls the [MaterialProcessor.BuildTexture](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.MaterialProcessor) method once for each texture in the [MaterialContent.Textures](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.MaterialContent.Textures) collection in the `MaterialContent` object passed to it. +- `MaterialProcessor.BuildTexture` in turn invokes the [ModelTextureProcessor.Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.TextureProcessor) method on the [TextureContent](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.TextureContent) object passed to it. + +One reason you may want to change how this works is that the `ModelTextureProcessor.Process` method applies texture compression to all textures it processes. This could be DXT1, DXT5, PVRTC, ETC1, RGBA4444 or ATITC depending on target your platform. If textures in your game assets are compressed already, you may want to avoid a second compression. + +> [!TIP] +> Not all platforms support all types of texture compression. For example DXT1/5 are generally only supported on Desktop graphics cards and some NVidia mobile graphics cards. PVRTC is only supported on iOS and some Android devices with PowerVR graphics cards, and ATITC is only supported on ATI graphics cards. Using the `Compressed` setting for `TextureCompression` for the Texture Processor will let the Pipeline pick the best compression for your target platform. + +### To prevent compression of textures during processing + +Here is how to prevent compression from being applied to model textures during processing. + +1. Create an override of the standard [MaterialProcessor.BuildTexture](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.MaterialProcessor) method, and invoke the [TextureProcessor.Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.TextureProcessor) method, which does no compression, instead of `ModelTextureProcessor.Process`. +2. Create an override of `ModelProcessor.ConvertMaterial` that invokes your override of `MaterialProcessor.BuildTexture` instead of the standard one. + +The first of these overrides could be coded as: + +```csharp + [ContentProcessor] + class NoCompressionMaterialProcessor : MaterialProcessor + { + protected override ExternalReference BuildTexture( + string textureName, ExternalReference texture, ContentProcessorContext context ) + { + return context.BuildAsset( texture, "TextureProcessor" ); + } + } +``` + +There are several things to note about this code. + +- An [ExternalReference](xref:Microsoft.Xna.Framework.Content.Pipeline) is an asset object that is shared between multiple classes, such as a diffuse texture used by more than one material. When such an asset is specified, the Content Manager loads only one copy of the `ExternalReference` at run time and builds it only once, no matter how many references there are to it. +- The [ContentProcessorContext](xref:Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext)[BuildAsset](xref:Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext) method lets you invoke a processor by name to build the content in an object. +- Although _textureName_, the first argument to `BuildTexture`, is ignored in the override above, you could use it if you wanted to process textures differently depending on normal maps or other criteria. + +Given the processor created by your first override above, you could code the second override: + +```csharp + [ContentProcessor] + class NoCompressionModelProcessor : ModelProcessor + { + protected override MaterialContent ConvertMaterial( + MaterialContent material, ContentProcessorContext context ) + { + return context.Convert( + material, "NoCompressionMaterialProcessor" ); + } + } +``` + +Because this override is processing `MaterialContent` objects in memory rather than `ExternalReference` objects, it uses the [ContentProcessorContext.Convert](xref:Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext) function instead of [BuildAsset](xref:Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext) to invoke the processor created by your first override. + +After building and installing your new `NoCompressionModelProcessor` (see [Adding a Custom Importer](../../whatis/Content_Pipeline/CP_AddCustomProcImp.md)), you can assign it to any models whose textures are already compressed and no further compression is applied. + +## See Also + +- [Adding New Content Types](../../whatis/Content_Pipeline/CP_Content_Advanced.md) +- [What Is Content?](../../whatis/Content_Pipeline/CP_Overview.md) +- [What is the Content Pipeline?](../../whatis/Content_Pipeline/CP_Architecture.md) +- [Standard Content Importers and Content Processors](../../whatis/Content_Pipeline/CP_StdImpsProcs.md) +- [Adding a Custom Importer](../../whatis/Content_Pipeline/CP_AddCustomProcImp.md) diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_GenerateCustomXML.md b/articles/getting_to_know/howto/content_pipeline/HowTo_GenerateCustomXML.md new file mode 100644 index 00000000..f5da9950 --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_GenerateCustomXML.md @@ -0,0 +1,116 @@ +--- +title: How to create a custom XML File? +description: For complex game data, using a software tool to create and maintain these assets may be useful. Level tables, for example, might be easier to develop through a custom-level editor tool. +requireMSLicense: true +--- + +The [IntermediateSerializer](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer) class of the MonoGame Framework can be employed by custom tools running under Windows to directly serialize game data to an XML file. + +An XML file generated in this way then can be included in the game project and imported by [XmlImporter Class](xref:Microsoft.Xna.Framework.Content.Pipeline.XmlImporter) as part of the Content Pipeline. Because [XmlImporter](xref:Microsoft.Xna.Framework.Content.Pipeline.XmlImporter) is actually a wrapper for [IntermediateSerializer](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer), it is certain that the XML file will be in the [correct format](../../whatis/Content_Pipeline/CP_XML_Elements.md) to be deserialized by the same facility. + +The [IntermediateSerializer](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer) class is controlled through the `XmlWriter` class of the .NET Framework defined in `System.Xml`. The properties of the `XmlWriterSettings` class can be used to specify its output properties. + +The serializer produces its output according to these rules: + +- All public fields and properties are serialized; a separate XML element is used for each. +- Protected, private, or internal data is skipped. +- Get-only or set-only properties are skipped. +- Properties come before fields. +- If there is more than one field or property, these are serialized in the order they are declared. +- Nested types are serialized using nested XML elements. +- When the class derives from another, members of the base class are serialized before data from the derived type. + +## XML Serialization Example + +The following steps create a simple program that demonstrates how a program can use the [IntermediateSerializer](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer) method [IntermediateSerializer.Serialize Generic Method](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer) to serialize program data to an XML file. + +### Step 1: Create a New Project + +You will create a new project in Visual Studio. + +1. On the `File` menu, click `New`, and then click `Project`. + +2. In the `New Project` dialog box, ensure `Windows` is selected in the `Project types` pane, and then click `Console Application` in the `Templates` pane. + +3. Open the new project, then in the `Solution Explorer`, right-click the `Dependencies` folder, and then click `Manage NuGet Packages..`. + +4. Click on the `Browse` tab (if not selected) and search for the `MonoGame.Framework.Content.Pipeline` package, and then click `Install`. + + Accept any dialog prompts for Licenses or dependencies as they appear. + +5. Additionally, install the `MonoGame.Framework.DesktopGL` NuGet package which is required by the `Content.Pipeline` package. + +## Step 2: Add XML Serialization + +1. In the `Solution Explorer`, double-click `Program.cs` to edit it. + +2. Add `using` declarations for the System.Xml and Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate namespaces. + + ```csharp + using System.Xml; + using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate; + ``` + +3. Define a class to serialize to XML with the properties you wish to store in your custom XML. + + ```csharp + namespace XMLSerializerTest + { + class MyData + { + public int elf = 23; + public string hello = "Hello World"; + } + } + ``` + +4. Within the function `Main` of the `Program` class, add the following code. This code employs [IntermediateSerializer.Serialize Generic Method](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer) to serialize the `MyData` class as an XML file. + +```csharp + class Program + { + static void Main(string[] args) + { + MyData ExampleData = new MyData(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + + using (XmlWriter writer = XmlWriter.Create("example.xml", settings)) + { + IntermediateSerializer.Serialize(writer, ExampleData, null); + } + } + } +``` + +## Step 3: Generate XML + +1. Press F5 to build and execute the program. +2. Examine the `example.xml` file in the project's `bin\\Debug\\netx.0` folder. + +> [!NOTE] +> `netx.0` relates to the version of .NET your project is building under, `net6.0` for `.NET 6` and `net8.0` for `.NET 8` + +You should have an output that looks like the following: + +```xml + + + + 23 + Hello World + + +``` + +## See Also + +### Concepts + +- [Using an XML File to Specify Content](HowTo_UseCustomXML.md) +- [Adding Content to a Game](HowTo_GameContent_Add.md) + +## Reference + +- [IntermediateSerializer Class](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer) diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_Load_XML.md b/articles/getting_to_know/howto/content_pipeline/HowTo_Load_XML.md new file mode 100644 index 00000000..8ed66ed9 --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_Load_XML.md @@ -0,0 +1,92 @@ +--- +title: How to load XML Content at Runtime? +description: Describes how to load custom game data at game runtime through the Content Pipeline. +requireMSLicense: true +--- + +This example concludes the procedure begun in the tutorial [Adding an XML Content File to a Visual Studio Project](HowTo_Add_XML.md). + +Once custom game data is integrated as game content from an XML file through the Content Pipeline, it exists within your game runtime package in binary format. The data can be [loaded at runtime](HowTo_GameContent_Add.md) through the [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager). + +> [!IMPORTANT] +> This tutorial assumes you are using Visual Studio as your IDE, for VSCode please adapt the IDE interactions appropriately. + +## Add a SpriteFont for testing + +While not essential for loading XML, in order to demonstrate the loaded data and write it to the screen, we will add a [SpriteFont](xref:Microsoft.Xna.Framework.Graphics.SpriteFont) to enable the drawing of strings. + +1. In the `Solution Explorer`, Double-click the `Content.mgcb` in the `Content` folder to open the MGCB editor. + + > [!TIP] + > If you have any issues opening the MGCB content project, please refer to the [How to load content](HowTo_GameContent_Add.md) guide. + +2. Click on `Edit -> New Item`, then name the file `Font` and select `SpriteFont Description` as the type. Click on `Create` to finish. + +3. Save the content project and then continue. + +## To load the custom data in the game + +1. To add the `MyDataTypes` library as a reference in the game project, In the `Solution Explorer`, right-click the game project, click `Add Reference`, click the `Projects` tab, select `MyDataTypes`, and then click `OK`. + +2. Then in the `Solution Explorer`, double-click `Game1.cs` to edit it. + +3. Add the `using` declaration for the MyDataTypes namespace at the top of the file beneath the existing `usings` statements. + + ```csharp + using MyDataTypes; + ``` + +4. Add a data declaration for an array of type `PetData` after the other `private` variables in the `Game1` class. + + ```csharp + private PetData[] pets; + ``` + +5. In the [Game.LoadContent](xref:Microsoft.Xna.Framework.Game#Microsoft_Xna_Framework_Game_LoadContent) override function, load the custom content. + + ```csharp + protected override void LoadContent() + { + // Load the pet data table + pets = Content.Load("pets"); + } + ``` + + The custom game data now resides in the array of `PetData` objects. + +6. The data loaded from the XML file into the `PetData` array can be accessed normally as c# code, for example, the following `Draw` code will render the data from the XML file to the screen if found, else it will write a warning. + + ```csharp + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(Color.CornflowerBlue); + + _spriteBatch.Begin(); + // Check if the XML data was loaded into the pets data array. + if (pets != null) + { + // For each pet, write its data to the screen. + for (int i = 0; i < pets.Length; i++) + { + var pet = pets[i]; + _spriteBatch.DrawString(Content.Load("Font"), $"{pet.Name} / {pet.Species}: Weight [{pet.Weight}] - Age: [{pet.Age}]", new Vector2(100, 100 + 20 * i), Color.White); + } + } + else + { + // If no data was loaded (or there was an error) write `No Pets found` to the screen. + _spriteBatch.DrawString(Content.Load("Font"), "No pets found", new Vector2(100, 100), Color.White); + } + _spriteBatch.End(); + base.Draw(gameTime); + } + ``` + +The result of loading the XML and rendering the data should display as follows: + +![XML data output](./images/HowTo_Load_XML_Final.png) + +## See Also + +- [Using an XML File to Specify Content](HowTo_UseCustomXML.md) +- [Adding Content to a Game](HowTo_GameContent_Add.md) diff --git a/articles/getting_to_know/howto/content_pipeline/HowTo_UseCustomXML.md b/articles/getting_to_know/howto/content_pipeline/HowTo_UseCustomXML.md new file mode 100644 index 00000000..6e818ce5 --- /dev/null +++ b/articles/getting_to_know/howto/content_pipeline/HowTo_UseCustomXML.md @@ -0,0 +1,41 @@ +--- +title: How to use an XML File to Specify Content? +description: Game assets managed through the Content Pipeline include graphic items such as textures, models and meshes; sound files such as dialogue or music; and custom data that governs the behavior of the game. +requireMSLicense: true +--- + +Data tables, for example, are custom data that might describe different characters’ attributes or the features of each level in the game. The content and format of this data is specific to the requirements of the game. Custom game data in the form of an XML file also can be loaded into your game through the standard features of the Content Pipeline. + +When the Content Pipeline is used, the game does not have to parse the XML format in which the game data is originally stored. Data loaded by the game through [ContentManager](xref:Microsoft.Xna.Framework.Content.ContentManager) is read in deserialized form directly into a managed code object. + +## In This Section + +- [How to Add custom game data as an XML file](HowTo_Add_XML.md) + + Describes how to add custom game data as an XML file through the Content Pipeline. + +- [Generating a custom XML File](HowTo_GenerateCustomXML.md) + + Describes how to use [IntermediateSerializer](xref:Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.IntermediateSerializer) from a Windows application to generate XML content to add to a MonoGame application. + +- [Adding an XML Content File to a MonoGame Project](HowTo_GameContent_Add.md) + + Describes how to add custom game data as an XML file through the Content Pipeline. + +- [Loading XML Content at Runtime](HowTo_Load_XML.md) + + Describes how to load custom game data at game runtime through the Content Pipeline. + +- [XML Elements for XMLImporter](../../whatis/Content_Pipeline/CP_XML_Elements.md) + + Describes the elements of an XML file that can be processed by the [XmlImporter Class](xref:Microsoft.Xna.Framework.Content.Pipeline.XmlImporter). + +- [Sprite Font XML Schema Reference](../../whatis/Content_Pipeline/CP_SpriteFontSchema.md) + + Describes the valid tags and values for Sprite-Font (.spritefont) XML files used by the Content Pipeline to create [SpriteFont](xref:Microsoft.Xna.Framework.Graphics.SpriteFont) textures. + +## See Also + +### Concepts + +[Adding Content to a Game](./HowTo_GameContent_Add.md) diff --git a/articles/getting_to_know/howto/content_pipeline/images/HowTo_Load_XML_Final.png b/articles/getting_to_know/howto/content_pipeline/images/HowTo_Load_XML_Final.png new file mode 100644 index 0000000000000000000000000000000000000000..dcaf0a31c70c35bd2165b1dfe40261f263704602 GIT binary patch literal 11621 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 literal 0 HcmV?d00001 diff --git a/articles/getting_to_know/howto/content_pipeline/images/mgcb_editor_references_window.png b/articles/getting_to_know/howto/content_pipeline/images/mgcb_editor_references_window.png new file mode 100644 index 0000000000000000000000000000000000000000..ea7c29de4b1357974b292722466d187b78f47727 GIT binary patch literal 21640 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 CcDphF literal 0 HcmV?d00001 diff --git a/articles/getting_to_know/howto/content_pipeline/images/mgcg_editor_new_spritefont.png b/articles/getting_to_know/howto/content_pipeline/images/mgcg_editor_new_spritefont.png new file mode 100644 index 0000000000000000000000000000000000000000..2d201b09aefd1705ebf8cc7815895521c442457e GIT binary patch literal 18318 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 literal 0 HcmV?d00001 diff --git a/articles/getting_to_know/howto/content_pipeline/index.md b/articles/getting_to_know/howto/content_pipeline/index.md index 0e5cd60f..0d5caefc 100644 --- a/articles/getting_to_know/howto/content_pipeline/index.md +++ b/articles/getting_to_know/howto/content_pipeline/index.md @@ -4,4 +4,24 @@ description: A series of articles to answer common questions related to Content requireMSLicense: true --- -## Coming soon \ No newline at end of file +## In This Section + +- [How to add content](HowTo_GameContent_Add.md) + + Demonstrates how to add content, such as images or sounds, to your game. + +- [How to use custom XML](HowTo_UseCustomXML.md) + + Demonstrates how to use a custom XML File to Specify Content. + +- [How to Load content from a library](HowTo_LoadContentLibrary.md) + + Demonstrates how to create A localized game using alternative sets of text that are appropriate to the language and culture of the gamer. + +- [How to extend a content processor](HowTo_Extend_Processor.md) + + Describes how MonoGame lets you modify or extend the behavior of any standard Content Pipeline processor. + +- [How to Extend the Font Description Processor to Support Additional Characters](HowTo_ExtendFontProcessor.md) + + Describes the process of developing a custom content processor needed to add additional characters to a [FontDescription](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.FontDescription) object based on the text that is required by the game. diff --git a/articles/getting_to_know/whatis/content_pipeline/CP_AddCustomProcImp.md b/articles/getting_to_know/whatis/content_pipeline/CP_AddCustomProcImp.md new file mode 100644 index 00000000..1e160c86 --- /dev/null +++ b/articles/getting_to_know/whatis/content_pipeline/CP_AddCustomProcImp.md @@ -0,0 +1,28 @@ +--- +title: What is a Custom Importer? +description: MonoGame provides standard importers and processors for a number of common file formats used to store such basic game assets as models, materials effects, sprites, textures, and so on. For a list of file formats supported by these standard importers and processors. +requireMSLicense: true +--- + +MonoGame provides standard importers and processors for a number of common file formats used to store such basic game assets as models, materials effects, sprites, textures, and so on. For a list of file formats supported by these standard importers and processors, see [Standard Content Importers and Content Processors](CP_StdImpsProcs.md). + +## To add a custom importer or processor to a game project + +This procedure assumes you have copied the new importer or processor to a local folder in the game project. + +1. Open the MonoGame Pipeline Tool. +2. Load the `.mgcb` file associated with your game. +3. Click on the `Content` node and in the `Properties panel` click on `References`. +4. Click the `Add` Button. +5. Navigate to the directory containing the assembly with the custom importer or processor, and then add it. +6. Save. + +> [!IMPORTANT] +> For MonoGame `3.8.2` and below, make sure to keep the project at `.NET 6` or below. The MGCB tool for 3.8.2 CANNOT understand or read .NET 8 libraries as it is compiled with .NET 6. The Game project can use a higher version, but library projects must stay at `.NET 6` else they cannot be read. + +The new importer or processor now appears as a choice in dialog properties for importing or processing a newly added game asset. + +## See Also + +- [Adding New Content Types](CP_Content_Advanced.md) +- [Standard Content Importers and Content Processors](CP_StdImpsProcs.md) diff --git a/articles/getting_to_know/whatis/content_pipeline/CP_Architecture.md b/articles/getting_to_know/whatis/content_pipeline/CP_Architecture.md new file mode 100644 index 00000000..45aa9f78 --- /dev/null +++ b/articles/getting_to_know/whatis/content_pipeline/CP_Architecture.md @@ -0,0 +1,55 @@ +--- +title: What is the Content Pipeline Architecture? +description: The Content Pipeline is designed to be extensible, so that it can easily support new input file formats and new types of conversion. +requireMSLicense: true +--- + +The MonoGame Content Pipeline is a set of processes applied when a game that includes art assets is built. The process starts with an art asset in its original form as a file, and continues to its transformation as data that can be retrieved and used within a MonoGame game through the MonoGame Framework Class Library. + +The Content Pipeline is designed to be extensible, so that it can easily support new input file formats and new types of conversion. + +Most MonoGame developers can ignore the inner workings of the Content Pipeline. The most commonly used types of game assets and formats are inherently supported by MonoGame. However, if you are a game developer who needs to support a new file format or game-engine capability, it is useful to understand the stages of the Content Pipeline that transform an asset from a digital-content creation (DCC) output file to part of the game binary. + +## Content Pipeline Components + +A game asset is made available to an MonoGame game after it is added to the Content project. Once the asset is part of the game solution, it is included in the Content Pipeline. + +Processes fall into two types depending on when they execute: design time components and runtime components. + +### Design-Time Components + +The design-time components of the MonoGame Content Pipeline that process your game assets execute when you build your MonoGame game as an executable file. These processes perform the initial transformation of an asset from its digital content creation (DCC) format to a managed code object that your game can use upon execution. + +Design-time components use the [Content Pipeline Class Library](CP_Class_Library.md), which can be used and extended to create custom Content Pipeline design-time components. + +#### Content Importer + +A `_Content Importer_` converts game assets from a particular DCC file format into objects in the MonoGame Content Document Object Model (DOM) that standard Content Processors can consume, or into some other custom form that a particular custom Content Processor can consume. + +An Content Importer typically converts content into managed objects based on the Content DOM, which includes strong typing for such assets as meshes, vertices, and materials. A custom Content Importer, however, may produce custom objects for a particular custom Content Processor to consume. + +#### Content Processor + +A `_Content Processor_` takes one specific type of an imported game asset and compiles it into a managed code object that can be loaded and used by MonoGame games. + +Each Content Processor acts upon a specific object type. For example, the [Effect Processor](CP_StdImpsProcs.md#standard-content-processors) accepts only [EffectContent](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.EffectContent) objects, representing a DirectX Effect asset. + +When you include a game asset file in your MonoGame `.mgcb` file, its **dialog properties** page specifies the appropriate Content Importer and Content Processor. Thereafter, when you build your game (by pressing F5), the assigned Content Importer and Content Processor for each asset is invoked automatically. The asset is built into your game in a form that can be loaded at run time by your game. + +The managed code objects created by the Content Processor are serialized into a compact binary format (also referred to as an intermediate format) file with an .XNB extension by the Content Pipeline Content Compiler. This .XNB file is used by the runtime components of the Content Pipeline that assist your game in retrieving the transformed game assets. + +The format of data in the .XNB file is tightly coupled to the MonoGame Framework. It is not designed for use by other runtime libraries. + +### Runtime Components + +Runtime components of the Content Pipeline support loading and using the transformed game asset by your MonoGame game. These components use the [MonoGame library](../WhatIs_MonoGame_Class_Library.md), which can be extended to create custom components. + +## Content Loader + +When the game needs the game asset's managed code object, it must call the [ContentManager.Load](xref:Microsoft.Xna.Framework.Content.ContentManager) method to invoke the Content Loader, specifying the object type it expects. The Content Loader then locates and loads the asset from the compact binary format (.XNB) file into the memory space of the game where it can be used. + +## See Also + +- [What Is Content?](CP_Overview.md) +- [Adding New Content Types](CP_Content_Advanced.md) +- [Loading Additional Content Types](CP_Customizing.md) diff --git a/articles/getting_to_know/whatis/content_pipeline/CP_Class_Library.md b/articles/getting_to_know/whatis/content_pipeline/CP_Class_Library.md new file mode 100644 index 00000000..d62c5486 --- /dev/null +++ b/articles/getting_to_know/whatis/content_pipeline/CP_Class_Library.md @@ -0,0 +1,42 @@ +--- +title: Content Pipeline Class Library +description: Content Pipeline Class Library Reference +requireMSLicense: true +--- + +The Content Pipeline class library is a library of classes, interfaces, and value types that are included in MonoGame. This library provides access to MonoGame Framework Content Pipeline functionality and is designed to be the foundation on which Content Pipeline–related applications, components, and controls are built. + +## Namespaces + +- [Microsoft.Xna.Framework.Content.Pipeline](https://monogame.net/api/Microsoft.Xna.Framework.Content.Pipeline.html) + + Provides classes representing base types and building block functionality for use by more specialized object models, such as the Graphics DOM. + +- [Microsoft.Xna.Framework.Content.Pipeline.Audio](https://monogame.net/api/Microsoft.Xna.Framework.Content.Pipeline.Audio.html) + + Provides intermediate classes and types for representing and manipulating graphics audio data. + +- [Microsoft.Xna.Framework.Content.Pipeline.Graphics](https://monogame.net/api/Microsoft.Xna.Framework.Content.Pipeline.Graphics.html) + + Provides intermediate classes and types for representing and manipulating graphics data. + +- [Microsoft.Xna.Framework.Content.Pipeline.Processors](https://monogame.net/api/Microsoft.Xna.Framework.Content.Pipeline.Processors.html) + + Provides base classes that represent processors used by the MonoGame Framework Content Pipeline when processing specific game asset types. + +- [Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler](https://monogame.net/api/Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler.html) + + Provides base classes that represent compilers and writers used by the MonoGame Framework Content Pipeline when processing specific game asset types. + +- [Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate](https://monogame.net/api/Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate.html) + + Provides base classes that represent the creation and writing of intermediate content for game asset types processed by the XNA Framework Content Pipeline. + +- Microsoft.Xna.Framework.Content.Pipeline.Tasks + + Provides support for importing and processing game assets into the binary format that is used by the Content Loader of a game project. + +## See Also + +- [Adding Content to a Game](../../howto/content_pipeline/HowTo_GameContent_Add.md) +- [What Is Content?](CP_Overview.md) diff --git a/articles/getting_to_know/whatis/content_pipeline/CP_Content_Advanced.md b/articles/getting_to_know/whatis/content_pipeline/CP_Content_Advanced.md new file mode 100644 index 00000000..fbb1e021 --- /dev/null +++ b/articles/getting_to_know/whatis/content_pipeline/CP_Content_Advanced.md @@ -0,0 +1,43 @@ +--- +title: What are custom content types? +description: Content Builders and Processors can be customized to handle almost any content type. +requireMSLicense: true +--- + +The game asset build process is controlled by Content Pipeline importers and content processors. When you press `F5` (or `dotnet build`) to build a game created with MonoGame, the appropriate Content Pipeline importer and processor for each asset is invoked. Each asset then is automatically built into your game. + +The flexibility of this process enables you to create and update game assets using a variety of digital content creation (DCC) tools. MonoGame supplies importers for several popular export formats supported by DCC tools, and also lets you develop custom importers for other formats. + +## In This Section + +- [What is the Content Pipeline Architecture?](CP_Architecture.md) + + Describes the components and execution flow of the MonoGame Content Pipeline. + +- [Content Pipeline Document Object Model](CP_DOM.md) + + Describes the built-in object support features for assets in the Content Pipeline. + +- [Loading Additional Content Types](CP_Customizing.md) + + Describes the options for customizing the Content Pipeline to support nonstandard game assets or special-purpose needs. + +- [Tips for Developing Custom Importers and Processors](CP_Tips_For_Developing.md) + + Provides useful information about how to design and debug a custom Content Pipeline importer or processor. + +- [Adding a Custom Importer](CP_AddCustomProcImp.md) + + Describes how to add a custom processor or importer to an existing game solution. + +- [Extending a Standard Content Processor](../../howto/Content_Pipeline/HowTo_Extend_Processor.md) + + Describes how MonoGame lets you modify or extend the behavior of any standard Content Pipeline processor that ships with the product. + +- [Developing with Parameterized Processors](CP_CustomParamProcs.md) + + Describes how to develop with parameterized processors, both standard and custom. + +- [How to: Extend the Font Description Processor to Support Additional Characters](../../howto/Content_Pipeline/HowTo_ExtendFontProcessor.md) + + Describes the process of developing a custom content processor needed to add additional characters to a [FontDescription](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.FontDescription) object based on the text that is required by the game. diff --git a/articles/getting_to_know/whatis/content_pipeline/CP_CustomParamProcs.md b/articles/getting_to_know/whatis/content_pipeline/CP_CustomParamProcs.md new file mode 100644 index 00000000..b4c8dfab --- /dev/null +++ b/articles/getting_to_know/whatis/content_pipeline/CP_CustomParamProcs.md @@ -0,0 +1,116 @@ +--- +title: What are Parameterized Processors? +description: This topic discusses a method for programmatically modifying existing parameter values, and for adding new parameters to your own processors. +requireMSLicense: true +--- + +## Programmatically Setting Parameter Values + +When you need to pass parameter values from one processor to another (also referred to as chaining), use the [BuildAsset](xref:Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext) and [BuildAndLoadAsset](xref:Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext) methods. Pass the parameter and its value using the _processorParameters_ argument of the respective method. For example, a custom model processor would invoke a second processor for model textures with a call to [BuildAsset](xref:Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext) and pass any parameter values in the _processorParameters_ argument. + +The following code example demonstrates this technique. First, add several parameters to a data dictionary: + +```csharp + //create a dictionary to hold the processor parameter + OpaqueDataDictionary parameters = new OpaqueDataDictionary(); + + //add several parameters to the dictionary + parameters.Add( "ColorKeyColor", Color.Magenta ); + parameters.Add( "ColorKeyEnabled", true ); + parameters.Add( "ResizeToPowerOfTwo", true ); +``` + +After adding the necessary parameters, pass the dictionary to the chained processor: + +```csharp + context.BuildAsset( + texture, typeof( TextureProcessor ).Name, + parameters, + null, + null ); +``` + +This call passes all parameters (stored in `parameters`) to a texture processor. + +Again, any parameters not recognized by the receiving processor are ignored. Therefore, if the parameter `ColorKeyCode` is entered into the dictionary as `_ColourKeyCode_`, it is ignored by the receiving processor. + +## Declaring Process Parameters + +Adding one or more parameters to your custom processor requires additional code in your processor's definition. Parameters support the following types: + +- bool +- byte +- sbyte +- char +- decimal +- double +- float +- int +- uint +- long +- ulong +- short +- ushort +- string +- enum +- [Vector2](xref:Microsoft.Xna.Framework.Vector2), [Vector3](xref:Microsoft.Xna.Framework.Vector3), and [Vector4](xref:Microsoft.Xna.Framework.Vector4) +- [Color](xref:Microsoft.Xna.Framework.Color) + +Parameters of other types are ignored by the processor. + +> [!TIP] +> Apply the Browsable attribute (with a value of **false**) to an individual parameter to prevent that parameter from being displayed in the Properties window. + +The following code example defines a simple custom processor that switches the coordinate system of a model using a single parameter (called switchCoordinateSystem): + +```csharp + public class SwitchCoordSystemProcessor : ModelProcessor + { + #region Processor Parameters + private bool switchCoordinateSystem = false; + + [DisplayName("Switch Coordinate System")] + [DefaultValue(false)] + [Description("Switches the coordinate system of a model.")] + public bool SwitchCoordinateSystem + { + get { return switchCoordinateSystem; } + set { switchCoordinateSystem = value; } + } + //additional class code follows... +``` + +In this code, the `SwitchCoordSystemProcessor` class is derived from [ModelProcessor](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor). This indicates that the processor accepts a model as input. The next few lines declare a single property called `SwitchCoordinateSystem` of type **bool**. Note that every parameter must have a **set** method. The property also has several attributes applied to it: + +|Attribute name|Usage| +|-|-| +|DisplayName|Name of the property when it appears in the Properties window of the MonoGame Pipeline tool. If not specified, the internal property name, declared in the source code, is used. For this example, "Switch Coordinate System" would be displayed.| +|DefaultValue|A user interface (UI) hint specifying the possible default value of the property. This value is used only as a UI hint; it will not be set on the property, nor will it override the default value declared in the code.| +|Description|Descriptive text displayed when you select the property in the Properties window of the MonoGame Pipeline Tool.| + +This completes the definition of the `SwitchCoordinateSystem` property. + +In the next code example, the class definition is continued with an override of the [Process](xref:Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor) method: + +```csharp + //additional class code precedes... + + public override ModelContent Process(NodeContent input, ContentProcessorContext context) + { + if (switchCoordinateSystem) + { + Matrix switchMatrix = Matrix.Identity; + switchMatrix.Forward = Vector3.Backward; + MeshHelper.TransformScene(input, switchMatrix); + } + + return base.Process(input, context); + } +``` + +This code passes the `SwitchCoordinateSystem` property (declared earlier) value to [TransformScene](xref:Microsoft.Xna.Framework.Content.Pipeline.Graphics.MeshHelper), which is a helper method that applies a transform to a scene hierarchy. + +## See Also + +- [Adding New Content Types](CP_Content_Advanced.md) +- [Parameterized Content Processors](CP_StdParamProcs.md) diff --git a/articles/getting_to_know/whatis/content_pipeline/CP_Customizing.md b/articles/getting_to_know/whatis/content_pipeline/CP_Customizing.md new file mode 100644 index 00000000..a752bba7 --- /dev/null +++ b/articles/getting_to_know/whatis/content_pipeline/CP_Customizing.md @@ -0,0 +1,58 @@ +--- +title: Loading Additional Content Types? +description: MonoGame provides the capability to extend Content Pipeline components that import and process almost any game asset file type. +requireMSLicense: true +--- + +MonoGame provides standard Content Pipeline components that import and process the most commonly used game asset file types. These file types include, for example, Content Importers for the Autodesk (.fbx) format and the DirectX (.x) format. A complete list of file types is available in the [Standard Content Importers and Content Processors](CP_StdImpsProcs.md) topic. Most digital Content creation (DCC) tools are capable of creating output in at least one of these standard formats. + +## Choosing to Customize + +A custom Content Importer is required for game assets available only in formats unsupported by MonoGame standard Content Importers. One may already be available from a third party. Custom Content Importers can be developed by DCC vendors, game-engine developers, and interested game hobbyists. Once a custom Content Importer is installed on your computer, you can associate art files with the Content Importer to invoke it when you build the art files (see [Adding a Custom Importer](CP_AddCustomProcImp.md)). + +You may need to write your own custom MonoGame Content Pipeline components to: + +- Support a new type of game asset or format from a DCC tool. +- Derive special-purpose content from another piece of content at the time the game is built. + +Here are some typical scenarios, and a summary of which Content Pipeline components require customization. + +|Component|When to customize|Output options| +|-|-|-| +|Content Importer|

|
  1. [Content Document Object Model](CP_DOM.md) (DOM) object that can be consumed by a standard Content Processor.
  2. A custom object that must be consumed by a custom Content Processor.
| +|Content Processor|
  • Must process the custom output of a custom Content Importer.
  • Needs to process the output of a standard Content Importer in a special-purpose way.
|
  1. A standard Content Processor output type.
  2. A custom Content Processor output type that must be consumed by a custom Content Loader at game run time.
| +|Content Loader|