Skip to content

Commit e95e535

Browse files
authored
Updated controls to new V3 (#226)
Removed `.Visual` which is no longer needed to position or size controls. Updated link to the code section in Gum. Updated to latest Gum NuGet package. Clarified state creation, which is done differently in the AnimatedButton vs OptionsSlider
1 parent 85cb304 commit e95e535

File tree

12 files changed

+69
-74
lines changed

12 files changed

+69
-74
lines changed

articles/tutorials/building_2d_games/20_implementing_ui_with_gum/index.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Gum addresses these challenges with ready-made solutions, allowing us to focus o
3939
>
4040
> Keep in mind that while it is possible to build a full UI system without any external dependencies, creating a layout engine is complicated and beyond the scope of this tutorial. Instead, we will be taking advantage of the Gum NuGet package.
4141
>
42-
> Gum is a powerful system enabling the creation of virtually any game UI, and we will be covering some of the basics of its use in this tutorial. The full Gum documentation can be found here: [https://docs.flatredball.com/gum/code/monogame](https://docs.flatredball.com/gum/code/monogame)
42+
> Gum is a powerful system enabling the creation of virtually any game UI, and we will be covering some of the basics of its use in this tutorial. The full Gum documentation can be found here: [https://docs.flatredball.com/gum/code/about](https://docs.flatredball.com/gum/code/about)
4343
4444
## Gum Concepts
4545

@@ -218,8 +218,8 @@ startButton.Anchor(Gum.Wireframe.Anchor.BottomLeft);
218218

219219
// Set the X and Y position so it is 20px from the left edge
220220
// and 20px from the bottom edge.
221-
startButton.Visual.X = 20;
222-
startButton.Visual.Y = -20;
221+
startButton.X = 20;
222+
startButton.Y = -20;
223223
```
224224

225225
The `Click` event is raised whenever the button is activated and provides a standard way to respond regardless of input device:
@@ -291,7 +291,7 @@ Gum allows you to customize visuals in two ways:
291291
With simple property changes, you can directly assign values in code. For example, the following code example changes the width of a button:
292292

293293
```cs
294-
startButton.Visual.Width = 100;
294+
startButton.Width = 100;
295295
```
296296

297297
Direct property assignment works well for initial setup, such as positioning elements or setting their dimensions when first creating your UI. However, when you need visual elements to respond to user interactions (like highlighting a button when it is focused), a different approach is required.
@@ -321,7 +321,7 @@ To add the Gum NuGet package in Visual Studio Code:
321321
2. Choose `Add NuGet Package` from the context menu.
322322
3. Enter `Gum.MonoGame` in the `Add NuGet Package` search prompt and press Enter.
323323
4. When the search finishes, select the `Gum.MonoGame` package in the results
324-
5. When prompted for a version choose version `2025.8.3.3`.
324+
5. When prompted for a version choose version `2025.12.9.1`.
325325

326326
#### [Visual Studio 2022](#tab/vs2022)
327327

@@ -332,7 +332,7 @@ To Add the Gum NuGet package in Visual Studio 2022:
332332
3. In the NuGet Package Manager window, select the `Browse` tab if it is not already selected.
333333
4. In the search box, enter `Gum.MonoGame`.
334334
5. Select the "Gum.MonoGame" package from the search results.
335-
6. On the right, in the version dropdown, select version `2025.8.3.3` and click the "Install" button.
335+
6. On the right, in the version dropdown, select version `2025.12.9.1` and click the "Install" button.
336336

337337
#### [dotnet CLI](#tab/dotnetcli)
338338

@@ -342,7 +342,7 @@ To add the Gum NuGet package using the dotnet CLI:
342342
2. Enter the following command:
343343

344344
```sh
345-
dotnet add DungeonSlime.csproj package Gum.MonoGame --version 2025.8.3.3
345+
dotnet add DungeonSlime.csproj package Gum.MonoGame --version 2025.12.9.1
346346
```
347347

348348
---
@@ -351,11 +351,11 @@ To add the Gum NuGet package using the dotnet CLI:
351351
> You can verify the package was successfully added by examining your `DungeonSlime.csproj` file, which should now contain a reference like:
352352
>
353353
> ```xml
354-
> <PackageReference Include="Gum.MonoGame" Version="2025.8.3.3" />
354+
> <PackageReference Include="Gum.MonoGame" Version="2025.12.9.1" />
355355
> ```
356356

357357
> [!IMPORTANT]
358-
> This tutorial uses version `2025.8.3.3` of Gum, which is the latest version of Gum as of this writing. That exact version is specified to use in the section above when installing the NuGet package to ensure compatibility throughout this tutorial. If there are newer versions of Gum available, please consult the [Gum documentation](https://docs.flatredball.com/gum/gum-tool/upgrading) before updating in case there are any breaking changes from the code that is presented in this tutorial.
358+
> This tutorial uses version `2025.12.9.1` of Gum, which is the latest version of Gum as of this writing. That exact version is specified to use in the section above when installing the NuGet package to ensure compatibility throughout this tutorial. If there are newer versions of Gum available, please consult the [Gum documentation](https://docs.flatredball.com/gum/gum-tool/upgrading) before updating in case there are any breaking changes from the code that is presented in this tutorial.
359359

360360
### Adding UI Sound Effect
361361

@@ -397,7 +397,7 @@ Finally, update the [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initializ
397397
398398
The following is a breakdown of this initialization process:
399399
400-
1. **Basic Initialization**: `GumService.Default.Initialize(this, DefaultVisualsVersion.V2)` sets up the Gum system with our game instance. This is required for any gum project. The second parameter specifies the default visual styling. V2 is the latest version which makes it easy to style the default controls.
400+
1. **Basic Initialization**: `GumService.Default.Initialize(this, DefaultVisualsVersion.V3)` sets up the Gum system with our game instance. This is required for any gum project. The second parameter specifies the default visual styling. V3 is the latest version which makes it easy to style the default controls.
401401
402402
> [!NOTE]
403403
> We only need to pass our [**Game**](xref:Microsoft.Xna.Framework.Game) instance and the visuals version since we are using Gum as a code-first approach. Gum also offers a visual editor that creates Gum project files. When using the editor, you will need to also pass the Gum Project file to `Initialize`. For more information on how to use the Gum visual editor, see the [Gum Project Forms Tutorial](https://docs.flatredball.com/gum/code/monogame/tutorials/gum-project-forms-tutorial).
@@ -668,7 +668,7 @@ While this UI is now functional, you may have noticed that it uses Gum's default
668668
:::question-answer
669669
The two ways to customize Gum UI elements are:
670670
671-
1. **Direct property assignment**: Setting properties directly in code (like `MyButton.Visual.Width = 100`). This works well for initial setup and static properties.
671+
1. **Direct property assignment**: Setting properties directly in code (like `MyButton.Width = 100`). This works well for initial setup and static properties.
672672
2. **States**: Using Gum's state system (`StateSave` objects) to define different visual states that can be applied in response to specific conditions or events. States are automatically applied by Forms controls in response to user interactions (like focus or highlighting).
673673

674674
States are useful for dynamic changes that occur during gameplay, as they separate visual response logic from game logic.

articles/tutorials/building_2d_games/20_implementing_ui_with_gum/snippets/game1/initializegum.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ private void InitializeGum()
33
// Initialize the Gum service. The second parameter specifies
44
// the version of the default visuals to use. V2 is the latest
55
// version.
6-
GumService.Default.Initialize(this, DefaultVisualsVersion.V2);
6+
GumService.Default.Initialize(this, DefaultVisualsVersion.V3);
77

88
// Tell the Gum service which content manager to use. We will tell it to
99
// use the global content manager from our Core.

articles/tutorials/building_2d_games/20_implementing_ui_with_gum/snippets/gamescene/createpausepanel.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ private void CreatePausePanel()
22
{
33
_pausePanel = new Panel();
44
_pausePanel.Anchor(Anchor.Center);
5-
_pausePanel.Visual.WidthUnits = DimensionUnitType.Absolute;
6-
_pausePanel.Visual.HeightUnits = DimensionUnitType.Absolute;
7-
_pausePanel.Visual.Height = 70;
8-
_pausePanel.Visual.Width = 264;
5+
_pausePanel.WidthUnits = DimensionUnitType.Absolute;
6+
_pausePanel.HeightUnits = DimensionUnitType.Absolute;
7+
_pausePanel.Height = 70;
8+
_pausePanel.Width = 264;
99
_pausePanel.IsVisible = false;
1010
_pausePanel.AddToRoot();
1111

@@ -23,17 +23,17 @@ private void CreatePausePanel()
2323
_resumeButton = new Button();
2424
_resumeButton.Text = "RESUME";
2525
_resumeButton.Anchor(Anchor.BottomLeft);
26-
_resumeButton.Visual.X = 9f;
27-
_resumeButton.Visual.Y = -9f;
28-
_resumeButton.Visual.Width = 80;
26+
_resumeButton.X = 9f;
27+
_resumeButton.Y = -9f;
28+
_resumeButton.Width = 80;
2929
_resumeButton.Click += HandleResumeButtonClicked;
3030
_pausePanel.AddChild(_resumeButton);
3131

3232
var quitButton = new Button();
3333
quitButton.Text = "QUIT";
3434
quitButton.Anchor(Anchor.BottomRight);
35-
quitButton.Visual.X = -9f;
36-
quitButton.Visual.Y = -9f;
35+
quitButton.X = -9f;
36+
quitButton.Y = -9f;
3737
quitButton.Width = 80;
3838
quitButton.Click += HandleQuitButtonClicked;
3939

articles/tutorials/building_2d_games/20_implementing_ui_with_gum/snippets/titlescene/createoptionspanel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ private void CreateOptionsPanel()
1313

1414
var musicSlider = new Slider();
1515
musicSlider.Anchor(Gum.Wireframe.Anchor.Top);
16-
musicSlider.Visual.Y = 30f;
16+
musicSlider.Y = 30f;
1717
musicSlider.Minimum = 0;
1818
musicSlider.Maximum = 1;
1919
musicSlider.Value = Core.Audio.SongVolume;
@@ -25,7 +25,7 @@ private void CreateOptionsPanel()
2525

2626
var sfxSlider = new Slider();
2727
sfxSlider.Anchor(Gum.Wireframe.Anchor.Top);
28-
sfxSlider.Visual.Y = 93;
28+
sfxSlider.Y = 93;
2929
sfxSlider.Minimum = 0;
3030
sfxSlider.Maximum = 1;
3131
sfxSlider.Value = Core.Audio.SoundEffectVolume;

articles/tutorials/building_2d_games/20_implementing_ui_with_gum/snippets/titlescene/createtitlepanel.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ private void CreateTitlePanel()
77

88
var startButton = new Button();
99
startButton.Anchor(Gum.Wireframe.Anchor.BottomLeft);
10-
startButton.Visual.X = 50;
11-
startButton.Visual.Y = -12;
12-
startButton.Visual.Width = 70;
10+
startButton.X = 50;
11+
startButton.Y = -12;
12+
startButton.Width = 70;
1313
startButton.Text = "Start";
1414
startButton.Click += HandleStartClicked;
1515
_titleScreenButtonsPanel.AddChild(startButton);
1616

1717
_optionsButton = new Button();
1818
_optionsButton.Anchor(Gum.Wireframe.Anchor.BottomRight);
19-
_optionsButton.Visual.X = -50;
20-
_optionsButton.Visual.Y = -12;
21-
_optionsButton.Visual.Width = 70;
19+
_optionsButton.X = -50;
20+
_optionsButton.Y = -12;
21+
_optionsButton.Width = 70;
2222
_optionsButton.Text = "Options";
2323
_optionsButton.Click += HandleOptionsClicked;
2424
_titleScreenButtonsPanel.AddChild(_optionsButton);

articles/tutorials/building_2d_games/21_customizing_gum_ui/index.md

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,12 @@ To convert from pixel coordinates to normalized values, you divide the pixel pos
8484

8585
#### Visual States
8686

87-
Rather than directly modifying properties when UI elements change state (like when a button is focused), Gum uses a state-based system. Each control type has a specific category name that identifies its collection of states:
87+
When UI elements change state (like when a button is focused), Gum uses a state-based system. A control's visual can be casted to a type specific to that control to access its states. Each control-specific visual has a type name matching its control, with the word Visual appended. For example, a button's `Visual` can be casted to type `ButtonVisual` to access button-specific properties and states. Once casted, the visual provides a `States` property containing all available states for that control type. Most control visuals, including `ButtonVisual`, provide the following States properties:
8888

89-
- Buttons use `Button.ButtonCategoryName`.
90-
- Sliders use `Slider.SliderCategoryName`.
91-
- Other control types have their own category names.
92-
93-
Within each category, you define named states that correspond to the control's possible conditions:
94-
95-
- "Enabled" (the normal, unfocused state).
96-
- "Focused" (when the control has focus).
97-
- "Highlighted" (when the mouse hovers over the control).
98-
- "Disabled" (when the control cannot be interacted with).
89+
- States.Enabled (the normal, unfocused state).
90+
- States.Focused (when the control has focus).
91+
- States.Highlighted (when the mouse hovers over the control).
92+
- States.Disabled (when the control cannot be interacted with).
9993

10094
Each state contains an `Apply` action that defines what visual changes occur when that state becomes active. For example, when a button becomes focused, its state might change the background color or switch to an animated version.
10195

@@ -301,6 +295,8 @@ The slider uses color changes to provide visual feedback:
301295

302296
When the slider is focused, all its elements change from gray to white, making it clear to the player which UI element currently has focus.
303297

298+
Notice that unlike the button which used existing states, we create the slider's states from scratch. These states must use specific names so that Gum knows to use them when the slider gains or loses focus. These names can be obtained through the `FrameworkElement` class. Our slider only needs to handle focused and unfocused (which returns to the `enabled` state), bug Gum provides additional states if needed.
299+
304300
#### Fill Visualization
305301

306302
One of the most important aspects of a slider is the visual representation of its value. We achieve this by updating the width of the `_fillRectangle` element:
@@ -397,7 +393,7 @@ The principles you have learned in this chapter extend beyond the specific compo
397393
:::question-answer
398394
The two main approaches are:
399395

400-
- **Direct property assignment**: Setting properties directly in code (like `button.Visual.Width = 100`). This approach is best for initial setup of UI elements and static properties that do not change during gameplay.
396+
- **Direct property assignment**: Setting properties directly in code (like `button.Width = 100`). This approach is best for initial setup of UI elements and static properties that do not change during gameplay.
401397
- **States (StateSave objects)**: Defining different visual states that are applied automatically in response to interactions. This approach is best for dynamic changes that happen during gameplay, like highlighting a button when it is focused or changing colors when a slider is adjusted.
402398

403399
:::
@@ -434,8 +430,7 @@ The principles you have learned in this chapter extend beyond the specific compo
434430
:::question-answer
435431
Gum's state system links with Forms controls through specifically named categories and states:
436432

437-
- Each Forms control type has a reserved category name (e.g., Button.ButtonCategoryName)
438-
- Within that category, the control looks for states with specific names (Enabled, Focused, Highlighted, etc.)
433+
- Each Forms control type has states specific to that type which are accessed through a casted visual (e.g., `buttonVisual.States.Enabled`)
439434
- When the control's state changes (like gaining focus), it automatically applies the corresponding visual state
440435

441436
This relationship is important because it:

articles/tutorials/building_2d_games/21_customizing_gum_ui/snippets/animatedbutton.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using Gum.DataTypes;
33
using Gum.DataTypes.Variables;
44
using Gum.Forms.Controls;
5-
using Gum.Forms.DefaultVisuals;
5+
using Gum.Forms.DefaultVisuals.V3;
66
using Gum.Graphics.Animation;
77
using Gum.Managers;
88
using Microsoft.Xna.Framework.Input;

articles/tutorials/building_2d_games/21_customizing_gum_ui/snippets/gamescene/createpausepanel.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ private void CreatePausePanel()
22
{
33
_pausePanel = new Panel();
44
_pausePanel.Anchor(Anchor.Center);
5-
_pausePanel.Visual.WidthUnits = DimensionUnitType.Absolute;
6-
_pausePanel.Visual.HeightUnits = DimensionUnitType.Absolute;
7-
_pausePanel.Visual.Height = 70;
8-
_pausePanel.Visual.Width = 264;
5+
_pausePanel.WidthUnits = DimensionUnitType.Absolute;
6+
_pausePanel.HeightUnits = DimensionUnitType.Absolute;
7+
_pausePanel.Height = 70;
8+
_pausePanel.Width = 264;
99
_pausePanel.IsVisible = false;
1010
_pausePanel.AddToRoot();
1111

@@ -33,16 +33,16 @@ private void CreatePausePanel()
3333
_resumeButton = new AnimatedButton(_atlas);
3434
_resumeButton.Text = "RESUME";
3535
_resumeButton.Anchor(Anchor.BottomLeft);
36-
_resumeButton.Visual.X = 9f;
37-
_resumeButton.Visual.Y = -9f;
36+
_resumeButton.X = 9f;
37+
_resumeButton.Y = -9f;
3838
_resumeButton.Click += HandleResumeButtonClicked;
3939
_pausePanel.AddChild(_resumeButton);
4040

4141
AnimatedButton quitButton = new AnimatedButton(_atlas);
4242
quitButton.Text = "QUIT";
4343
quitButton.Anchor(Anchor.BottomRight);
44-
quitButton.Visual.X = -9f;
45-
quitButton.Visual.Y = -9f;
44+
quitButton.X = -9f;
45+
quitButton.Y = -9f;
4646
quitButton.Click += HandleQuitButtonClicked;
4747

4848
_pausePanel.AddChild(quitButton);

articles/tutorials/building_2d_games/21_customizing_gum_ui/snippets/titlescene/createoptionspanel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ private void CreateOptionsPanel()
1818
musicSlider.Name = "MusicSlider";
1919
musicSlider.Text = "MUSIC";
2020
musicSlider.Anchor(Gum.Wireframe.Anchor.Top);
21-
musicSlider.Visual.Y = 30f;
21+
musicSlider.Y = 30f;
2222
musicSlider.Minimum = 0;
2323
musicSlider.Maximum = 1;
2424
musicSlider.Value = Core.Audio.SongVolume;
@@ -32,7 +32,7 @@ private void CreateOptionsPanel()
3232
sfxSlider.Name = "SfxSlider";
3333
sfxSlider.Text = "SFX";
3434
sfxSlider.Anchor(Gum.Wireframe.Anchor.Top);
35-
sfxSlider.Visual.Y = 93;
35+
sfxSlider.Y = 93;
3636
sfxSlider.Minimum = 0;
3737
sfxSlider.Maximum = 1;
3838
sfxSlider.Value = Core.Audio.SoundEffectVolume;

articles/tutorials/building_2d_games/21_customizing_gum_ui/snippets/titlescene/createtitlepanel.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ private void CreateTitlePanel()
77

88
AnimatedButton startButton = new AnimatedButton(_atlas);
99
startButton.Anchor(Gum.Wireframe.Anchor.BottomLeft);
10-
startButton.Visual.X = 50;
11-
startButton.Visual.Y = -12;
10+
startButton.X = 50;
11+
startButton.Y = -12;
1212
startButton.Text = "Start";
1313
startButton.Click += HandleStartClicked;
1414
_titleScreenButtonsPanel.AddChild(startButton);
1515

1616
_optionsButton = new AnimatedButton(_atlas);
1717
_optionsButton.Anchor(Gum.Wireframe.Anchor.BottomRight);
18-
_optionsButton.Visual.X = -50;
19-
_optionsButton.Visual.Y = -12;
18+
_optionsButton.X = -50;
19+
_optionsButton.Y = -12;
2020
_optionsButton.Text = "Options";
2121
_optionsButton.Click += HandleOptionsClicked;
2222
_titleScreenButtonsPanel.AddChild(_optionsButton);

0 commit comments

Comments
 (0)