This series of tutorials covers how to load a Gum project using all of the functionality provided by Gum for creating and maintaining MonoGame projects. This tutorial is intended for brand new users of MonoGame Gum. It covers the following topics:
Creating a new Gum project (.gumx)
Loading the Gum project (.gumx) in MonoGame
Displaying Screens
Interacting with Gum Forms UI
Accessing Gum instances in code
Working with Gum code generation for strongly-typed components
Managing multiple screens
This tutorial walks you through creating a brand new Gum project and adding it to an existing MonoGame project. The MonoGame project can be empty or it can be an existing game - the steps are the same either way.
This tutorial covers:
Adding Gum NuGet packages
Creating a new Gum project using the Gum tool
Modifying the game .csproj to include all Gum files
Loading the Gum project in your game
This tutorial presents the minimum amount of code necessary to work with Gum. You may need to adapt the code to fit in your game project.
Before writing any code, we must add the Gum nuget package. Add the Gum.MonoGame
package to your game. For more information see the Setup page.
Once you are finished, your game project should reference the Gum.MonoGam
project.
Next we'll create a project in the Gum UI tool. If you have not yet run the Gum tool, you can get setup instructions in the Gum Setup page.
Once you have the tool downloaded, run it. You should have an empty project.
We need to save our Gum project in the Content folder of our game. Gum projects include many files. it's best to keep a Gum project and all of its files in a dedicated folder.
Add a new folder to your Game's Content folder which will contain the Gum project, such as GumProject.
In the Gum tool click File -> Save Project.
Select the GumProject folder created earlier as the target location. Give your Gum project a name such as GumProject.
After your project is saved it should appear in Visual Studio.
We can add default Forms components to our project. Forms components are premade components for standard UI elements such as Button, TextBox, and ListBox. We'll use these components in later tutorials.
To add Gum Forms components in Gum, select Content -> Add Forms Components
This tutorial will not use the DemoScreenGum, so leave this option unchecked and press OK.
Forms components modify your default components (such as Text) for styling. Click OK to apply these changes.
Your project now includes Forms components.
Now that we have our Gum project created, we can load it in our game.
First, we'll set up our project so all Gum files are copied when the project is built. To do this:
Right-click on any Gum file in your project, such as GumProject.gumx
Select the Properties item
Set the file to Copy if Newer
Double click your game's csproj file to open it in the text editor and find the entry for the file that you marked as Copy if newer.
Modify the code to use a wildcard for all files in the Gum project. In other words, change Content\GumProject\GumProject.gumx
to Content\GumProject\**\*.*
Now all files in your Gum project will be copied to the output folder whenever your project is built, including any files added later as you continue working in Gum.
At the time of this writing, Gum does not use the MonoGame Content Builder to build XNBs for any of its files. This means that referenced image files (.png) will also be copied to the output folder.
Future versions may be expanded to support using either the .XNB file format or raw PNGs.
Now that we have a Gum project added to the .csproj, we can load the Gum project. We need to add code to Initialize, Update, and Draw. A simplified Game class with these calls would look like the following code:
The code above has the following three calls on GumService:
Initialize - this loads the argument Gum project and sets appropriate defaults. Note that we are loading a Gum project here, but the gum project is optional. Projects which are using Gum only in code would not pass the second parameter.
Update - this updates the internal keyboard, mouse, and gamepad instances and applies default behavior to any components which implement Forms. For example, if a Button is added to the Screen, this code is responsible for checking if the cursor is overlapping the Button and adjusting the highlight/pressed state appropriately.
Draw - this method draws all Gum objects to the screen. Currently this method does not perform any drawing, but in the next tutorial we'll be adding a Gum screen which is drawn in this method.
If you've followed along, your project is now a fully-functional Gum project. We haven't added any screens to the Gum project yet, so if you run the game you'll still see a blank (cornflower blue) screen.
The next tutorial adds our first screen.
Gum screens are top level items which can contain instances of Gum objects. We'll be creating our first Gum screen in this tutorial. We'll also load this screen in code and work with gum objects.
To add a new Screen:
Open the project in the Gum tool
Right-click on the Screens folder and select Add Screen
Name the screen TitleScreen and click OK
The newly created TitleScreen is now in the Screens folder.
We can add instances to Gum Screen by drag+dropping the files onto the Game screen.
Add a Text instance by dropping the Standard/Text onto TitleScreen.
Instances can also be created by selecting the TitleScreen, then drag+dropping the item in the editor window.
Add a ButtonStandard instance by dropping Components/Controls/ButtonStandard onto TitleScreen.
Be sure to select the TitleScreen first, then drag+drop. If you click the component instead, then it will be selected, so you must re-select the TitleScreen.
To show the screen in game, modify the Initialize method as shown in the following snippet:
The game now displays the Gum screen. Notice that if you attempt to interact with the button, it does not show highlighted/clicked states. This is because we haven't yet passed the Screen to the Update method where UI interaction is performed. We'll do that in the next section.
Our code now includes both the Initialize call and the ToGraphicalUiElement call.
The Initialize call is responsible for loading the .gumx file and all other Gum files into memory. This loads only the Gum files and it does not load any additional files such as .pngs or font files. This call only needs to happen one time in your game.
The ToGraphicalUiElement method is responsible for converting the Gum screen into a visual object. It loads all other files referenced by the Screen and its instances such as .png and font files. This method is called whenever you want to show a new GraphicalUiElement, and it may be called multiple times in a game. For example, ToGraphicalUiElement is called whenever transitioning between screens, which we will do in a future tutorial.
The addToManagers
parameter results in the screen being added to the Gum rendering system. By passing true
for this parameter, the Screen shows up automatically when Draw is called. Remember, Draw was added in the previous tutorial.
Games usually need to interact with Gum screens in code. By convention the Screen is stored in a variable named Root
. We can modify the Game project by:
Adding a Root member to Game
Assigning Root when calling ToGraphicalUiElement
Adding the Root to the Update call
The modified code is shown below in the following code snippet:
Notice that we've added the Root as a parameter to the Update call. By doing this, we get the built-in behavior for Forms control such as Button.
More complicated games may have multiple roots, such as situations where UI may exist on multiple layers for sorting or independent zooming. This tutorial does not cover this more-complex setup, but if your game needs multiple Roots spread out over multiple layers, you can pass a list of roots to Update as well.
This tutorial uses Game1 as the container for all Gum members and logic. You may want to move this code into other classes to fit the rest of your game's code structure.
Now that we have our screen stored in the Root object, we can access objects.
We can modify the displayed string by getting an instance of the Text and modifying its properties as shown in the following code:
The code above casts TextInstance to a TextRuntime. Each standard type in Gum (such as Text, Sprite, and Container) has a corresponding runtime type (such as TextRuntime, SpriteRuntime, and ContainerRuntime). Therefore, if we wanted to interact with a Sprite in code, we would cast it to a SpriteRuntime.
We can also interact with Forms objects in code. The base type for all Forms objects is FrameworkElement, so we can use the GetFrameworkElementByName extension method as shown in the following code:
Notice that the code above uses the GetFrameworkElementByName. This code returns an instance of a FrameworkElement (Forms instance). As we'll cover in the next tutorial, only some Components can be used as Forms instances.
This tutorial showed how to load a Gum screen in code and how to interact with objects. The next tutorial discusses Forms controls and explains the difference between GraphicalUielements and Forms controls.
So far we've been working with a single Screen. This tutorial covers how to work with multiple screens, including how to add and destroy screens in response to UI events.
Before we write any code, we'll create two screens. A real game might have screens like a TitleScreen, OptionsScreen, GameScreen (which includes hud and a pause menu), and a GameOverScreen. For this tutorial we'll create two simple screens, each with a single button and a Text.
Next we'll make the following modifications to Game1:
Change the first screen to Screen1
Make the Root property public static
so that it can be accessed by the Screens
A full game may keep the Root in a dedicated object which provides access to the Screens, but we're making it public static
to keep the tutorial simple.
Now we can modify both Screen1Runtime.cs and Screen2Runtime.cs to include code that links from one to the other as shown in the following code:
Each screen removes itself from managers when its button is clicked, then creates and adds the next screen to managers.
This tutorial showed how to switch between two screens by removing the old screen with RemoveFromManagers and creating a new screen with ToGraphicalUiElement.
Gum Forms provides a collection of standardized, fully functional UI elements. MonoGame Gum includes the following types:
Button
CheckBox
ComboBox
ListBox
ListBoxItem (used by ListBox)
PasswordBox
RadioButton
ScrollView
Slider
TextBox
We can use all of the types above by adding instances of components which map to these controls.
The previous tutorial showed how to add a Button instance to our screen. We can add other functional controls by drag+dropping instances into the TitleScreen.
Our forms controls already have some functionality even before we write any code in our game.
We can interact with any of the Forms instances by using GetFrameworkElementByName
. For example, the following code can be used to add items to the ListBoxInstance:
Forms types such as Button are associated with Gum components based on their category. For example, the following components can be used to create Button instances.
Although the prefix "Button" suggests that these controls are Forms Buttons, the name can change and these would still create buttons. At runtime the type of Forms control associated with a component is determined by the state categories defined in the component.
For example, each of these components has a state category named ButtonCategory.
Although we won't cover the details in this tutorial, you can customize the existing components or create new components which will map to the Forms types so long as they have the appropriate category.
The Forms types and properties are based on the WPF syntax. Developers familiar with WPF may find that many of the same members exist in Gum Forms. However, keep in mind that Gum Forms are still using Gum for the layout engine, so any properties related to position or size follow the Gum rules rather than WPF rules.
This tutorial showed how to create Forms instances in a screen, interact with them in code, and how to work with the different forms types.
The next tutorial covers how to generate code for custom components.
The Gum tool includes lots of functionality for creating and customizing UI. For a more complete tutorial covering the Gum tool, see the . Feel free to spend some time creating your TitleScreen.
Forms component instances can be added and modified just like any other instance, but at runtime these types provide common properties and methods. To learn more about working with Forms in code, see the .
The types used in the previous tutorials fall into two categories:
Standard runtime types like TextRuntime
Forms types like Button
Games often need to interact with custom components which do not fall into either of these two categories. This tutorial shows how to create custom classes, which we refer to as runtime types for custom components.
For this tutorial we'll create a component which can be used to display score. It has three parts:
A NineSlice for the background
A text for the "Score:" label
A text for the score value
If we add an instance of this component to our screen, we can interact with it as shown in code. First, we need to drag+drop an instance of the ScoreComponent into our Screen.
We can interact with ScoreComponentInstance by using the following code:
Although this code is functional, it can be difficult to maintain in a larger project. We are relying on values like "ScoreValue"
to find the text object. If we spell this wrong, or if we change the name of our Text in Gum, this code breaks. Also, this code is quite verbose and it can be difficult to write from memory.
We can use strongly-typed classes to solve these problemsl
The Gum tool supports code generation which allows us to interact with Gum components without needing to cast or use string names. We can enable code gen in Gum by checking the check boxes in the Code tab. Also, be sure to switch the Output Library to MonoGame.
This produces a fully-generated class named ScoreComponentRuntime. In this case, the code is using FullyInCode instantiation type, which means the generated code shows the code necessary to create this component without loading the gum project. If you would like to use generated code without loading a Gum file, then this approach might be useful. Usually this approach is useful if you would like to avoid file IO or if you cannot read from disk (such as on an embedded device).
Since our project loads from disk, we'll switch to using FindByName as our Object Instantiation Type. By switching to this Object Instantiation Type, the generated code is modified to look for instances by name.
The code should look similar to the following:
We can enable automatic saving of generated code in Gum by specifying the location of our .csproj file. To do this, set Code Project Root to the full path of folder containing your .csproj:
We can force code generation once by clicking the Generate Code button.
This creates two code files - a generated code file and a custom code file:
The generated code file contains the same code as is shown in the code generation preview.
The custom code file can be used to customize the runtime class
This component can be used in code as shown in the following snippet:
Note that all screen and components default to a Generation Behavior of GenerateAutomaticallyOnPropertyChange. This means that any changes result in re-generation of the .Generated.cs file.
We can also create runtimes for screens. Once properties have been set up, adding additional runtimes is much easier. In fact, clicking Generate Code button the produces generated code for the screen.
Once we have a runtime class for our Screen, we can delete code from Game1 and write it in the CustomInitialize method as shown in the following snippet:
This tutorial shows how to generate types for screens and components which are used automatically when loading a Gum project.
The next tutorial shows how to work with multiple screens.