Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Gum is a general-purpose, open source game UI and layout tool. FlatRedBall integrates with Gum to create UI for your games.
To downlaod Gum, click here: http://files.flatredball.com/content/Tools/Gum/Gum.zip
For information about using the Gum tool: https://docs.flatredball.com/gum/
For information about using Gum in a FlatRedBall project: Gum Tutorials
Gum is an open source, general purpose, platform-agnostic UI layout tool. Although Gum itself is not built to be used with any game engine in particular, FlatRedBall provides full support for the Gum tool. In fact, Gum is so well integrated into FlatRedBall that it is used to perform layouts of FlatRedBall's built-in UI system: FlatRedBall.Forms. For information on using Gum as a standalone UI tool for any type of project, see the Gum documentation.
The first step in using Gum is to download the project. If you have downloaded the FRBDK.zip file from the main downloads page on this site, you already have Gum in the <unzipped FRBDK location>/Gum/Data/Gum.exe. You can also get Gum as a standalone downloaded here. You will also need to install the XNA 4.0 Redistributable on your machine to run Gum. Remember the location for Gum.exe, we'll need it later.
For this tutorial we will be adding a Gum project through the FlatRedBall Editor.
If you have created your project using one of the project types in the wizard, then your project is already including Gum.
You can verify that you have a Gum project by looking for GumProject.gumx in Global Files.
If you do not want to run the Glue wizard, or if your project already has Screens or Entities (the Glue wizard only appears for completely empty projects), you can add Gum through the toolbar button
Click the Gum toolbar button or the Add Gum Project quick action. Notice that the Gum icon in the toolbar has a + icon to indicate that this button adds a new project.
When asked, select the option to Include Forms Controls (Recommended)
Troubleshooting Missing Gum Options: If you do not see any of the options shown above, you can verify that the plugin has installed correctly and that it is running through the Manage Plugins Window.
Also, you will see a button in the toolbar for opening the Gum project.
The .gumx project (which means Gum XML) is the root project. It can be opened in Gum. If you have the file association set up for the .gumx file with Gum, you can click the Gum icon or double-click the .gumx file to open Gum. This is the same icon which was previously used to add a new Gum project.
Setting up file associations is recommended since it makes opening Gum much faster.
Don't associate Launcher.exe with your files: Instead, associate Gum.exe to gum file formats (gumx, gucx, gusx) Launcher.exe exists when running Gum manually to check for prerequisites.
Once you open up the .gumx file in Gum, you should see a screen like this:
Gum is a very powerful tool with lots of functionality. This tutorial assumes that you are familiar with using the tool. If you're not, then you will want to take a look at the usage guide for Gum, which can be found here. You should take some time to familiarize yourself with the tool, as the tutorials in this series will focus specifically on how to use Gum with Glue and FlatRedBall.
If you've gotten this far then you have a Gum project which is ready to be used with Glue. Next, we'll cover some of the basics of working with Gum in Glue.
So far we've covered how to create a new Gum project in your FlatRedBall project and how to add Gum screens to your FlatRedBall screens. This gets us a considerable amount of functionality already, but it's very likely that you'll need to interact with Gum objects in code. For example, you may use code to dynamically set the visibility of certain UI elements according to the game's state. This tutorial will explain how to work with the GraphicalUiElement in code - the class that is at the heart of Gum.
If you've been following along the previous tutorials, then you should have a Screen that has two Gum objects: a Text and a ColoredRectangle. For this tutorial will make some modifications to the ColoredRectangle in our custom code in MainMenu. To make it easy to access Gum objects, the every Gum screen is loaded into a strongly-typed object which can be accessed in your FlatRedBall Screen's custom code using the GumScreen property. Keep in mind that GumScreen is a generated property which standardizes access to the Gum screen. You can also access the Gum screen through the name of the file - MainMenuGum in this case. For example, we can change the Y value of the ColoredRectangleInstance and move it along the X axis in CustomActivity by modifying MainMenu.cs as shown in the following snippet:
Open your Visual Studio project
Navigate to MainMenu.cs (which will be in your Screens folder in Visual Studio)
Modify CustomInitialize so it looks like this:
Similarly, objects from Gum can be modified in CustomActivity. You can modify your MainMenu's CustomActivity so it looks like this:
You can also add a reference to the ColoredRectangleInstance in the FlatRedBall Editor. This is not necessary since you already have a strongly-typed reference in code through GumScreen. However, it is still possible if you prefer to have access to the object in the FlatRedBall Editor.
Open or bring Glue into focus
Expand Screens
Expand MainMenu
Expand Files
Drag+drop the .gusx file onto the Objects item
Use the dropdown next to Source Name: to select ColoredRectangleInstance
Click OK
Note that the Source Name: drop down contains all instances in your Gum project. We selected the ColoredRectangleInstance for this example, but you could select any instance. Note that the ColoredRectangle in the FlatRedBall Editor is not a new ColoredRectangle instance - it is a reference to the ColoredRectangle inside the MainMenuGum screen.
Alternatively, you can access an object purely in code through its name. Doing so requires interacting with the instance in its base type (GraphicalUiElement) or casting the object. While this introduces some inconvenience (and can potentially cause crashes if you perform casting incorrectly), it can be useful for UI which is dynamic. For example, the following shows how to acciess the ColoredRectangleInstance purely in code. This code could be added to MainMenu.cs to get a reference to the ColoredRectangleInstance from the MainMenuGum screen:
Notice that we use the name ColoredRectangleInstance. This needs to match the name of the instance in the Gum screen exactly, including capitalization:
Also, notice that we use the type ColoredRectangleRuntime. We did this because the type in code should match the type in your Gum project. FlatRedBall automatically generates classes for every type in your Gum project, but it will always append Runtime to the end of the name. Therefore, the type ColoredRectangle in Gum becomes ColoredRectangleRuntime in code.
The example above shows how to move the rectangle. For this example we will modify the Text object to display score. We will increment the score whenever the player presses the space bar. This is just a simple example to show how to show a score. A real game may increase the score on an event such as when a bullet hits an enemy. We will access the TextInstance purely in code - but if you are more comfortable using the drag+drop approach to access the Text object, feel free to do that instead. To display a score, modify the MainMenu code:
Notice that the TextIntance property matches the exact name in Gum:
I've you've spent some time in Gum you may notice that the coordinate system in Gum behaves differently than the coordinate system in FlatRedBall. By default (0,0) is located at the center of the screen in FlatRedBall, but (0,0) represents the top-left corner of the screen in Gum. Also, increasing the Y value of an object will move it up in FlatRedBall, but increasing the Y value of a Gum object will move it down. You can try this by modifying your code to change the Y of the ColoredRectangleInstance in CustomActivity. When used with FlatRedBall, Gum objects will behave identically to how they behave in the Gum tool. This not only applies to Y positioning but as we will see in later tutorials with position and width unit types as well.
This tutorial has shown how to access objects from Gum in code. FlatRedBall generates objects which we can edit in code with full compile-time protection and Visual Studio IntelliSense. The next tutorial will show you how to use UI events (such as Click) on Gum components.
This tutorial continues our look at working with events on Gum objects. The previous tutorial covered the most basic situation - a clickable button in a screen. We'll expand upon the previous tutorial by creating a more complicated example - handling events on a popup container which displays a message and allows the user to click OK or Cancel. This tutorial covers the second property for controlling events: ExposeChildrenEvents.
HasEvents and ExposeChildrenEvents decide whether a component and its children are recognized as UI objects by the FlatRedBall engine. More specifically, they decide whether instances of these components are added to the GuiManager's windows list, and whether the GuiManager.Cursor can interact with these instances. Since each value can be independently set, then there are four possible combinations of values.
[row] [column md="3"] HasEvents / ExposeChildrenEvents [/column] [column md="4"] Description [/column] [column md="5"] Example [/column] [/row] [row] [column md="3"] True/False [/column] [column md="4"] The entire component is clickable, but individual parts of the component are not independently clickable. [/column] [column md="5"] A button made up of multiple sub-components (such as a NineSlice background, Text instance, and icon Sprite) which does not need each sub-component to be clickable. [/column] [/row] [row] [column md="3"] True/True [/column] [column md="4"] The entire component is clickable, as are individual sub-components. [/column] [column md="5"] A pop-up which should block clicks and also contains buttons which can be clicked. [/column] [/row] [row] [column md="3"] False/True [/column] [column md="4"] Only individual sub-components of the component will receive clicks. [/column] [column md="5"] A component which is used to perform layout on a collection of buttons, but the component as a whole has no visual component and should not receive clicks (or block clicks from receiving underlying UI). Only the individual buttons will receive clicks. [/column] [/row] [row] [column md="3"] False/False [/column] [column md="4"] The UI is completely ignored in the FlatRedBall UI system. [/column] [column md="5"] A decorative collection of UI which may overlap underlying UI elements but should not interfere with their click events. [/column] [/row]
This tutorial uses three components:
Button - a clickable component
Label - a non-clickable component used to display a message to the user
Popup - a component containing a label and two buttons
The Button component will need some kind of visual (such as a ColoredRectangle). We will include a Text object for the button, but it is not necessary for this tutorial. When finished your Button should look similar to the following image:
The only important detail for this component is to make sure the HasEvents value is set to true. We will look at ExposeChildrenEvents later.
The label component is only a container with a single Text object. Functionally we could skip creating a Label component and add a Text object directly to the Popup component. We will be creating a Label component to show how to include some components in events (Buttons) and exclude others (Label). When finished your Label should look similar to the following image:
For this tutorial we want to make sure Label instances are not clickable, so make sure the HasEvents property is set to false.
Finally, we'll create our Popup Component. It should contain the following instances:
Label
Button OkButton
Button CancelButton
ColoredRectangle (or other visual objects) for the background
When finished, the popup will appear as shown in the following image:
Let's consider the event behavior we want for our popup object:
We want each of the buttons in the popup to be clickable
We do not want the label to be clickable
We do not want the entire popup to raise events when it is clicked - only its buttons
This can be summarised as - the popup itself should not raise events, but its children should. We can get this combination of behaviour by setting the HasEvents value to false and the ExposeChildrenEvents value to true.
Now that we have a fully-functional Popup instance, we can add it to our Gum screen (such as MainMenuGum).
Now that our popup is in our MainMenuGum, we can access it in our MainMenu.cs file in Visual Studio. For example, we can respond to click events on the OK and Cancel buttons by hiding the popup and displaying some debug text.
The previous tutorial showed how to control whether Gum objects are considered objects which the cursor can interact with. Displaying the Cursor.WindowOver property can give clues about why events are not firing. As a reminder, the following code can be added to your Glue Screen's CustomActivity method:
This code produces the following behavior in our game:
Now that we've covered events related to contained objects (a Button inside of a Popup), we can look at other ways to diagnose event problems.
One of the most common event-related bugs is when the container of a set of instances receives input events instead of the contained instances. If the parent of an object does not have its ExposeChildrenEvents set to true, then children will not raise their events. For screens with deep hierarchies, any object in the parent/child, then any object in the chain can break events by having this set to false. For example, consider a button which is part of a standard Container:
If the Container Standard object does not have its ExposeChildrenEvents value checked, then the Button will not raise events.
This can be fixed by checking the ExposeChildrenEvents value.
If a particular component acts purely as a container but should never receive events itself, then its HasEvents should be set to false.
By default a parent will only check events on its children if the children are contained within the bounds of the parent. This restriction exists primarily for performance reasons - it allows parents to perform an initial bounding-box check against the cursor and if the test fails, the parent will not perform "deep" collision (testing every contained child). However, sometimes children are not contained within their parents' bounds, and if those children have events (such as click events), they will not raise these events by default. This can be corrected in one of two ways:
Expand the bounds of the parent to contain the child - this may make sense conceptually and it's the easiest and most efficient solution to the problem.
Change the parent instance's RaiseChildrenEventsOutsideOfBounds value to true. This will enable "deep" checking against all contained children. Of course, be careful when setting this on a large number of parents as it can increase the amount of time that cursor checks take.
This tutorial has shown how to work with events on Gum component instances which are contained in other instances.
So far we've discussed how to use Gum to create screens which can used and interacted with in your FlatRedBall game. This page discusses how to use events to attach custom code to common UI actions such as clicking.
Save the file to disk somewhere - remember where you saved it.
Open Gum
Right-click on the Components folder
Select "Import Component"
Browse for and select the NineSliceButton.gucx file that you saved
Click OK, and click OK if told that the file will be copied.
The Component should now appear in Gum:
Gum asks to copy your file so that your entire Gum project remains portable. If you would like to save your component, or import future components from the default Components folder, you can right-click on the Components folder and select View in explorer.
Next, we need to mark the Button as being clickable. To do this in Gum:
Verify NineSliceButton is selected
Check the HasEvents check box
Now that you have a NineSliceButton component, you can add it to your Gum screen:
Select the MainMenuGum screen
Drag+drop the NineSliceButton into the edit window of Gum, or onto the MainMenuScreen in the tree view
Once you have an object in a Gum screen, you can access it in code by using the GumScreen property which is part of every FlatRedBall screen. GumScreen is automatically generated to provide access to all Gum objects in the related screen. In this case, the screen is MainMenuGum. By typing GumScreen and a period, auto complete provides options for accessing Gum objects in your screen as shown in the following screenshot:
You can subscribe to the Click event by creating a method such as HandleClick, as shown in the following code snippet.
If you run the game now and click on the button you will notice that it gets wider every time it is clicked. Of course, this is not a very practical example. In a real game, you could move to the next screen or switch the logical or visual state of your game elements as appropriate.
The most common reason for events not firing is that the cursor is not actually detecting being over the Gum object with events. To check, you can add the following code in your Screen's CustomActivity:
Displaying the WindowOver may help you figure out why clicks are not occurring.
As shown above, events such as Click are only available on Components and Containers. This means that a free-floating Sprite, ColoredRectangle, or Text object in a Gum screen will not have Gum events raised on it. Note that non-components (such as Containers) can contain instances of components (such as a list of stacked buttons), and those contained components can raise events. The next tutorial will cover events on children components in more depth. Furthermore, note that although a non-component will never raise events (and will not be set as the GuiManager.Cursor.WindowOver ), the HasCursorOver method will still properly check the cursor against the bounds of the calling object.
Now that you know how to add events to Gum objects you can create a fully functional UI system by combining the visual editing power of Gum with the code generation and project structure features of Glue.
Gum objects can be used as UI objects. The most common way to build interactive UI is to use FlatRedBall.Forms - a UI system which mimics WPF in syntax but which uses Gum for the visuals. This tutorial (Events on Gum Objects) was created before FlatRedBall.Forms was available, so it provides instructions on how to build your own interactive Button which responds to clicks. For modern games we recommend using FlatRedBall.Forms instead of building new UI systems from the ground-up using Gum. For information on FlatRedBall.Forms, see the . If you are interested in how the underlying event system works (which is not necessary for most games), feel free to continue reading this tutorial and the next tutorial. If you would like to skip the two events tutorials, you can click here to go to .
Although this tutorial is focused on Gum objects, the steps necessary for adding events to Gum objects is identical to the steps necessary for adding events to Entities which implement the IWindow interface. In fact, the GraphicalUiElement class implements the IWindow interface, so you can even add events to it in custom code. For more information on IWindow in Glue, see
This tutorial will use a NineSliceButton, which can be imported from this file (right-click and save the file): To import this file:
Where is my mouse cursor? FlatRedBall projects have the mouse cursor invisible by default. To make the mouse cursor visible, see this page:
This tutorial outlines the most basic situation - a single component "floating" in an empty screen. A common setup is to have components which are part of other components (such as a button which is part of a menu). For information on working with events and parent/child components, see the .
Gum Type | Has Events | Expose Children Events |
Circle |
Colored Rectangle |
Component | X | X |
Container | X | X |
Nine Slice |
Polygon |
Rectangle |
Sprite |
Text |
Now that we have an empty Gum project set up, let's look at how we use screens. Screens in Gum are very similar to Screens in FlatRedBall. By default every FlatRedBall screen has an associated Gum screen. The naming convention for Gum screens is to match the FlatRedBall screen and to append the word "Gum" at the end. For example, let's consider a simple game which has the following screens:
GameScreen
Level1
Level2
CreditsScreen
MainMenuScreen
In this situation the following Gum screens would be created automatically:
GameScreenGum
Level1Gum
Level2Gum
CreditsScreenGum
MainMenuScreenGum
Why do Gum screens have the word "Gum" at the end?: The reason for this is because of a C# limitation where objects with a certain name cannot contain objects with the same name. In other words, when you create a screen in C# called MainMenu, FlatRedBall creates a class also called "MainMenu". If the Gum plugin were to create a Gum screen also called "MainMenu", the project would no longer compile. The Gum plugin avoids this problem by appending the word "Gum" when creating a new Gum screen.
We can control how the Gum plugin behaves by selecting the .gumx file which is located under Global Content Files.
The Show Mouse option automatically turns on the mouse cursor so it is visible on any screen with Gum UI. This is turned on by default. You may want to turn this off if you do not intend to use the mouse cursor in your game. The Automatically Create Gum Screens for Glue Screens option can speed up development since it automatically creates new Gum screens and adds them to your FlatRedBall project. If this option is checked, you may not need to create any Gum screens.
First, we'll create a MainMenu FlatRedBall screen. To do this:
Open or bring FlatRedBall to focus
Select the Quick Actions tab
Click the Add Screen/Level button
Check the Empty Screen option since we aren't making a new level
Enter the name Main Menu
Click OK
You should now have a screen in FlatRedBall called MainMenu and a Gum screen called MainMenuGum (assuming you left the Automatically Create Gum Screens for Glue Screens option checked).
Next, let's add some objects to the Gum screen:
Double-click the MainMenuGum file to open it in Gum (assuming you have set up file associations)
Expand the Standard folder
Drag+drop a ColoredRectangle from the Standards folder onto the MainMenuGum item, or into the editing area if the MainMenuGum screen is selected
Move the colored rectangle away from the top-left of the screen
Drag+drop a Text from the Standards into MainMenuGum
You should now have a Text and a ColoredRectangle in your Screen in Gum.
Gum auto-saves your changes (just like the FlatRedBall Editor) so once you've made these changes, you do not need to manually save the Screen. Since the Gum screen was automatically added, you can run your game and you will see the Gum screen showing up in your FlatRedBall game automatically - no code necessary.
At this point, you have the basics working for laying out Screens. In the next tutorial, we'll look at how to interact with Gum objects in code.
The example above showed how to add a new FlatRedBall Screen which automatically adds a Gum screen to your project. If you have already created a Screen (such as a GameScreen) before you added Gum to your project, then you must manually add the Gum screen. To do so:
Choose which FlatRedBall Screen should have a Gum screen. For example GameScreen
Open Gum
Add a new Screen to Gum called GameScreenGum
After adding the Screen in Gum, return to the FlatRedBall Editor
Right-click on GameScreen's Files folder
Select Add Gum Screen -> GameScreenGum
To review, if you add a new FlatRedBall Screen *after *having added Gum to your project, you do not need to manually create Gum screens. However, if you have FlatRedBall Screens which were created before having added Gum, then manual steps above are necessary for those Screens.
Exposed variables provide a way to modify Gum objects contained within other Gum objects through your code. For this tutorial, we'll be creating a scoring HUD object.
First, we'll create a ScoreHud component in Gum. To do this:
Right-click on Components and select Add Component
Enter the name ScoreHud and click OK
Verify ScoreHud is selected
Set Width Units to RelativeToContainer
Set Width to 0
Set Height Units to RelativeToContainer
Set Height to 0
Next, we'll create two Text objects:
Drag+drop a Text object into the ScoreHud object.
Rename the Text to Player1ScoreText
Position the Player1ScoreText object near the top-left of the Screen
Change the Player1ScoreText's "X Units" to "PercentageWidth"
Repeat the steps above to create a Player2ScoreText which is positioned on the top-right of the Screen
Now we can add a ScoreHud instance to the MainMenuGum by drag+dropping the ScoreHud into MainMenuGum:
If you run the game at this time you'll notice that the score hud appears, but the score HUD says "Hello" for Player 1 and Player 2's scores. We can get access to the ScoreHud object in Glue as follows:
Switch to Glue
Expand the "MainMenu" screen
Right-click on Objects
Select "Add Object"
Select "From File"
Select the file "MainMenuGum.gusx"
Select ScoreHudInstance as the Source Name
Enter the name ScoreHudInstance
Click OK
Unfortunately, if you try to access the Player objects in Visual Studio you'll see that there is no way to change the Text on the ScoreHud:
To access the score text we first need to expose the variables in Gum. To do this:
Switch to Gum
Select the Player1ScoreText object in Gum
Right-click on the Text variable
Select "Expose Variable" \
Enter the name "Score1" and click OK
Repeat the same process to expose "Score2" for Player2ScoreText
You should now have 2 exposed variables. You can see this by selecting the ScoreHud object in Gum:
Now you can go to Visual Studio and add the following code to CustomInitialize in your MainMenu screen:
Notice that the variables appear in Visual Studio with the same names as exposed in Gum. Running the game will show the score values showing 200 and 450 respectively.
Although Gum provides extensive layout control, many games require Gum components with custom logic. For example, a button may need to play a sound effect when clicked - logic which should be centralised in the button component code rather than added as events on every button instance. This tutorial shows how to use partial classes to add custom logic to a button. Although we use partial classes for the specific functionality of adding sound effects, partial classes can be used for any other logic.
Partial classes, which use the partial keyword, allow the definition of a single class to be spread out across multiple files. Glue uses partial classes to separate custom code from generated code (so that generated code does not overwrite custom code). In fact, all screens and entities in a Glue project already use partial classes. You can see this by expanding any screen or entity in your project in Visual Studio. The following image shows a GameScreen's custom code:
The following image shows a GameScreen's generated code:
Similarly, your Visual Studio project will have two files for each Gum screen and component: One for generated code and one for custom code. For example, if you have been following this tutorial, you will have a
MainMenuGumRuntime.cs
MainMenuGumRuntime.Generated.cs
We can handle the Click event by modifying the ButtonRuntime code as follows:
Now we can add code in our HandleClick method to perform any custom logic when the user clicks the button. This code will be executed on every instance of ButtonRuntime across our entire project.
This is usually caused by having a mismatched namespace in your partial compared to the partial of the generated code.
Gum supports the concept of states, which are very similar to states in Glue. This tutorial will discuss how to use States which are created in Gum when integrating Gum objects in Glue. This tutorial will cover both regular (uncategorized) as well as categorised states. If you'd like to see how to work with states in Gum, see this tutorial. If you'd like to see how to work with categorized states in Gum, see this tutorial.
Note that this tutorial uses a component titled Button. The purpose of this tutorial is to provide an end-to-end example of how to create states and work with them in code. The purpose of this is not to create a usable button. Although buttons may seem simple in concept, a real world button requires lots of logic that can take a long time to implement. For actual UI that uses buttons, we recommend using FlatRedBall.Forms and the Button class/components that it includes. In other words, you should follow this tutorial to learn about states, but we do not recommend actually using the resulting component in an actual game.
Before we look at how to work with States in code, we'll do a brief overview of states. We'll look at how states are created, why you might want to use states, and state ownership. States can be though of as "groups of variables". For example, consider the state of a button being disabled. A disabled button may have the following variables set:
Background.Alpha = 127 (make the background of the button half-transparent)
Text.Red = 100 (set the Red, Green, and Blue values to make it gray instead of black)
Text.Green = 100
Text.Blue = 100
A disabled button could change these variables in code when it became disabled, but it's far more expressive and maintainable to create a state in Gum that assigns all of these values. This allows the code to simply set the state and not worry about the visual details. States can be added to Screens, Components, and Standard Elements in Gum. States should almost always be categorized, so for the remainder of this tutorial we will be working with categorized states. Categorized states can help avoid some of the most common pitfalls of working with states, such as unset variables when switching between states. The following screen shot shows a typical Button object with states for controlling the button in response to various UI interactions and enabled values:
The screenshot shows a Button component with a category called UiStates. This category contains the following states:
Normal
Pressed
Highlighted
Disabled
These states can change any property on the Button or its contained instances. Important: Note that all of the states shown belong to the Button component. These states can change values on contained objects (such as ColoredRectangleInstance and TextInstance), but the states belong to the Button component. For example, if the Pressed state is selected and a variable is changed on ColoredRectangleInstance...
This change is applied to the Pressed state which belongs to the Button component. As another example, consider a screen called MenuScreenGum with three buttons:
FullGameButton
DemoButton
ExitButton
If this screen were used as a main menu in a game which can be played in demo mode, it could control the visibility of its contained buttons using a Full and Demo state, as shown in the following image:
The Full state may hide the DemoButton (set its Visible to false), while the Demo state may hide the FullGameButton (set its Visible to false). Even though these states may change variables on the button instances, the state is contained in the MainScreenGum.
Whenever you create a new State in Gum, Glue will automatically generate code for you to use this state. States are useful for creating standard UI behavior like reacting visually to a push. Typically UI elements are created as components (such as a general Button component). These components contain states to control visual behavior, and these states are set in the custom code for the component. For users of FlatRedBall.Forms, this section provides some insight into how states can be used in code. If you are using FlatRedBall.Forms, you will likely not need to write code like this because the Button class already has built-in state changing logic. For this example, consider a component named Button. For this component we will have the following states:
Normal
Pressed
Highlighted
Note that in the image below the three states are categorized in a category called UiStates. We recommend categorizing states whenever possible.
All Gum components create two code files for you: a file for custom code and a file for generated code.
The generated file (in this case ButtonRuntime.Generated.cs) will contain entries for each of the states. Note that each category will create its own enumeration and associated property in generated code: The following screenshot shows the enumerations defined for the states:
The following screenshot shows the properties for the states:
Note that categorized states are nullable, allowing the category to not be set at all (which is the default when the Button is created at runtime). These states can be used in code. For example, a simple, functional button could be created by adding events to the button in its CustomInitialize function.
Gum Screens and Components can be added to Layers in Glue. This page discusses some common scenarios for layering Gum objects in Glue. For information on moving Gum objects to layers in code, see the MoveToFrbLayer page.
Entire Gum Screens can be added to a Glue Layer. The following steps assume:
A Glue Screen called GameScreen
A Gum screen called MainScreen
The Gum screen added as a file under the Glue screen
Right-click on Objects
Select "Add Object"
Select the "From File" option
Select the MainScreen.gusx file
Select "this" option (the first option in the drop-down
Enter the name GumScreenInstance
Click OK
Right-click on Objects
Select "Add Object"
Make sure "FlatRedBall or Custom Type" is selected
Select Layer
Click OK
Drag+drop the GumScreenInstance onto the newly-created Layer
Gum screens can also be added to Glue Layers in code. For this example we'll assume your project already has:
A Glue screen (GameScreen)
A Gum screen in the Glue screen (GameScreenGum)
A Layer (LayerInstance)
To add the Gum screen to the Layer in code:
Open the project in Visual Studio
Open GameScreen.cs
Add the following code to CustomInitialize:
Note that the call requires two layers: LayerInstance and LayerInstanceGum. This is required because the GameScreenGum is a Gum object, so ultimately it must exist on a Gum layer. However, Glue simplifies this process by creating a Gum layer for every FlatRedBall layer automatically, so you do not have to worry about creating the Gum layer yourself if you added a Layer through Glue.
Individual objects within a Gum screen can be added to a Layer. To add an object to a layer:
Verify that your Glue screen has a Gum screen.
Right-click on the Objects folder
Add a new Layer
Right-click on the Objects folder
Select "From File"
Select the Gum screen (.gusx)
Select the object within the Gum screen which you'd like to move to a new layer
Drag+drop the object onto the Glue layer
Gum screens which are part of a FlatRedBall project generate a custom class. This custom class provides access to all contained objects within the screen. While it is possible to also interact with the elements in a general way (by name), using the typed properties in a screen is usually safer and more convenient.
To write code against a Gum screen, you must first have:
A Glue project
A Gum project inside the Glue project
A Glue screen (we'll call it GameScreen)
A Gum screen (we'll call it GameScreenGum)
The Gum screen file referenced by the Glue screen
Note: Once you add a Gum project to your Glue project, Glue will automatically add new Gum screens for each Glue screen. This functionality can be controlled by selecting the .gumx file in Glue:
The Gum screen object can be accessed in code just like any other object. All contained objects in a Gum screen are accessible through properties on the Gum screen. For example, consider the following Gum screen:
These can be accessed in code using the names of the objects from Gum:
Gum animations can be defined in the Gum tool or manually in code. This page covers the details of creating a Gum animation purely in code.
Animations are a convenient way to play, and stop instructions. Instructions are a FlatRedBall concept which encapsulate performing a particular action. For example, in instruction may set a variable, play a sound effect, or even create other instructions. The only requirements for creating an animation (which is of type GumAnimation ) are to define the instructions which will play when the animation executes and to define its length.
For this example we will create an animation which moves a component to three different positions. Animations can be defined in the component partial class itself (in which case this might be used to reference the component) or in code that has access to the component (in which case the code references the component by its variable name). For this example, we'll reference the component by its variable name ButtonInstance. Assuming that ButtonInstance is in scope, the following code could be used to define an animation:
The actions performed in the animation are defined in the GetMoveInstructions method, which returns an IEnumerable<Instruction> . Each instruction defines the logic to perform and the time to perform it. First, we should note that the method returns the enumerable using yield return . While this isn't necessary, this approach can simplify your code. It's also important to note that the GetMoveInstructions constructs and returns new instructions every time it is called, so that the time can be set appropriately, as is explained below. In this code example we are using the DelegateInstruction class, which can be used to perform any action as specified in its constructor. Notice that the actions performed can be simple or can be more complex C#. The first instruction in the code above assigns two variables, so the action is wrapped in brackets, as shown in the following snippet:
Instructions in an animation do not execute immediately, so their time must be set relative to the current time, which is obtained through TimeManager.CurrentTime . For example, the following code for the second instruction sets its time to be one second later than the current time:
If the instructions are created once and reused, rather than created every time the GetMoveInstruction method is called, then the same TimeToExecute would be used. Creating new instructions allows for setting the TimeToExecute relative to the current time.
Many games include a Gum screen for every FRB screen. Gum screens can be used for UI such as buttons and textboxes (usually using FlatRedBall.Forms) or can be used for read-only UI such as score and health display. Screens can be added in a variety of ways, depending on the state of your project.
If you have an existing project which already has a Gum project, then by default your project will receive a new Gum screen any time you add a new FRB screen.
This behavior is default, and it can be controlled by selecting the .gumx file.
If you have a FRB screen which does not have an associated Gum screen (for example, if the Screen was created before adding Gum to your project), then you can tell FRB to add a new Gum screen. To do this, select and right-click on the screen, and select the option to create a new Gum screen.
This option will create a new Gum screen and add it both to your Gum project and to the current FRB screen. FRB follows the standard naming convention by appending the word "Gum" to your FRB screen name.
If your project already has a Gum screen and a FRB screen and you would like to add the Gum Screen to the FRB screen, you can right-click on the Files and select the option to add the screen.
Gum components can be instantiated either by adding them to Screens in Gum, or in code. This guide discusses how to fully create a component in code.
The following steps can be used to instantiate a component in code:
Call the constructor
Call AddToManagers, optionally passing a layer for the component
For example, a component named Button in Gum would generate a class called ButtonRuntime. The following code would instantiate a ButtonRuntime:
Of course, if adding a new object in code, it must be cleaned up. Therefore you will need to keep track of the object and remove it from managers in the Glue screen's CustomDestroy method. For example, more complete code might look like the following snippet:
If adding a new instance as a child of another component, you do not need to call AddToManagers . See the section below for more information.
The following code can be used to create a new item and add it to a parent as a child. Notice that the child (listItem ) does not have its AddToManagers method called. It does not need to be called if the parent is already added to managers.:
Gum uses a different coordinate system compared to FlatRedBall. The separate Gum coordinate system exists for two reasons:
Gum objects are often positioned relative to the screen bounds rather than world coordinates. For example, a HUD should always appear at the top-left of the screen.
Gum objects may use different coordinate units. For example, a "Join Game" callout may be positioned 25% from the left edge of the screen rather than using pixel coordinates.
At times games may need to position FlatRedBall objects (such as entities) relative to the position of a Gum object. This document explains how to convert Gum to FlatRedBall coordinates.
The high level steps for converting from Gum to FlatRedBall coordinates are:
Convert the Gum coordinates to screen coordinates - coordinates relative to the top-left of the screen
Convert the screen coordinates to world coordinates - the coordinates used by FlatRedBall objects
(Optional) position the FlatRedBall object using the world coordinates.
For this example, consider a Gum screen with a single colored rectangle named ColoredRectangleInstance:
Note that the rectangle is positioned according to its center. This example will position a FlatRedBall Circle named CircleInstance. Note that this code can be used to position any FlatRedBall positioned object (such as entities or other collision shapes).
The following code can be used to convert the rectangle's position (which in this case is the center) to screen coordinates:
Notice that the example above uses the position of the ColoredRectangleInstance as defined by its XOrigin and YOrigin. If the rectangle's origin is changed to top-right in Gum...
...then the FlatRedBall CircleInstance will also be positioned on the Gum object's top-right corner.
The high level steps for converting from FlatRedBall to Gum coordinates are:
Convert the FlatRedBall coordinates to Screen pixel coordinates
Convert the screen pixel coordinates to Gum screen coordinates (considering zooming)
(Optional) Position a Gum object using the Gum screen coordinates
The following code creates colored rectangle instances on clicks. Notice that the Cursor's world coordinates are used to get the Gum coordinates:
FlatRedBall provides built-in logic which can be used for games that need cursor or touch-based UI. The three classes/interfaces providing this functionality are:
Cursor - an object providing cursor interaction like push, click, and hit testing with UI objects
GuiManager - a FlatRedBall manager which stores all live windows and drives every-frame logic like raising UI events
IWindow - an interface which allows an object to be managed by the GuiManager
Gum components implement the IWindow interface, and all component instances are automatically added to the GuiManager. In other words, if you have a component in your FlatRedBall screen, it can be used as an IWindow with no additional code.
Note that the usual method for interacting with Gum objects with a Cursor is to use FlatRedBall.Forms. This document explains how to manually interact with Gum objects if the extra flexibility is needed.
The following code shows how to grab and drag a Gum component instance with the mouse. Note that the following requirements must be met:
On Windows the mouse must be visible
The Gum object must be a component. Standard elements (such as ColoredRectangle instances) can also be moved with the mouse, but for simplicity this guide uses a component
The component must have its Has Events value set to true. This is true by default for all components.
Gum component instances which are added to a Gum screen in the Gum UI tool will automatically be added to the FlatRedBall GuiManager. Components which are created manually in code must be manually added to the GuiManager. Fortunately, calling AddToManagers is all that is needed. The following code is similar to the code above, except it manually creates a user control instance in code rather than using one created in a Gum screen.
Gum components can be added to FlatRedBall entities without any code or additional setup. A common example of needing a Gum component on a FRB entity is a health bar in a RTS game. Gum components can be thought of as containers which are "Gum on the inside, FlatRedBall on the outside". In other words, internally the Gum component uses the Gum coordinate and rendering system, but the component is attached to an entity which can be positioned like any other FlatRedBall object.
Before adding a Gum component to an entity, your project must have a Gum project added. If not, you can add one by clicking the Gum icon.
For this example we will add a component called HealthBar to an entity named Soldier. It assumes that your project has a Gum component named HealthBar.
To add an instance of the HealthBar component to a FlatRedBall entity:
Expand the Soldier entity in FlatRedBall
Right-click on the Objects folder
Select Add Object
Verify the FlatRedBall or Custom Type category is selected
Select the type HealthBarRuntime. FRB provides options for every component in your Gum project, with the word Runtime appended to the name. Keep in mind that the search bar can help narrow down the options.
Click OK
An instance of the HealthBar component will now be a part of the entity and will be attached (moves with) the entity automatically.
Component variables can be changed in multiple locations depending on the needs of the project. If a component should be the same across the entire project, then its variables should be changed in Gum.
If the component requires changes specific to each entity, then the variables can be changed in FRB.
If a component requires instance-by-instance modifications, it can be modified in code just like any other component Gum object.
As mentioned above, Gum objects which are added to FRB entities will automatically be attached. This can be disabled by detaching the Gum object from its parent. For example, to detach the HealthBarRuntimeInstance from its parent, the following code could be added to CustomInitialize :
Gum components which are part of an entity are attached to the entity itself (which is a PositionedObject ). Gum component attachment can be changed at runtime. The following code changes the HealthBarRuntimeInstance so it is attached to the SpriteInstance instead of the entire entity.
Gum provides a powerful layout system for positioning objects. Attached Gum objects can take full advantage of this coordinate system. To use Gum layout effectively we must first understand how Gum objects attach to FlatRedBall objects. For starters, consider a simple FRB entity with a single Sprite:
By default the origin of the object is its center. This can be visualized as shown in the following image:
We can therefore visualize the behavior of a Gum object that is added to the entity. By default the component will be positioned at 0,0, and the default position values will make its top-left corner align with the center of the entity.
We can change this by adjusting the origin values:
XOrigin = Center
YOrigin = Center
Keep in mind that FlatRedBall entities do not have size values (width and height), so adjusting the width of the Gum object to be based on its parent width will result in the Gum object behaving as if its parent has Width and Height values of 0:
Width = 0
WidthUnits = RelativeToContainer
YOrigin = Center
Even though the Gum object is attached to a FlatRedBall object, the Gum object is still drawn using the Gum coordinate system (relative to the FlatRedBall object position). For example, by default Gum objects move down when their Y value is increased (assuming YUnits is not set to inverted).
XUnits = PixelsFromLeft
YUnits = PixelsFromTop
X = 20
Y = 40
As shown above, Gum objects can be attached to Sprites within an entity. This provides the added benefit of being able to use the Sprite's size for layout. If the Gum object is attached to the sprite (using the code above), then the Gum object will (by default) position itself according to the top-left of the Sprite:
XUnits = PixelsFromLeft
YUnits = PixelsFromTop
X = 0
Y = 0
We can also reference the Sprite's dimensions by changing the WidthUnits and HeightUnits:
XUnits = PixelsFromLeft
YUnits = PixelsFromTop
X = 0
Y = 0
WidthUnits = RelativeToContainer
Width = 0
HeightUnits = RelativeToContainer
Height = 0
Keep in mind that the relationships are updated constantly, so if the Sprite changes size due to an animation, the Gum object will adjust automatically too.
Although entities do not have a size by default, entities can implement the IReadOnlyScalable interface to define their size. Implementing IReadOnlyScalable serves as an alternative to attaching to a FlatRedBall Sprite.
IReadOnlyScalable can be implemented directly on the entity in custom code as shown in the following code:
The code above returns a Scale of 8 and ScaleY of 16. Keep in mind that Scale values are measured from the center to the edge, so the values above would result in a width of 16 and a height of 32.
Note that FlatRedBall Sprites implement the IReadOnlyScalable interface. If your entity contains a FlatRedBall Sprite, you can use the Scale values from the Sprite in your IReadOnlyScalable implementation. The following code shows how to implement the IReadOnlyScalable interface on an entity called player which has a SpriteInstance:
The example above shows a simple implementation. An actual implementation can have more features, such as having setters on the scale values, or creating width and height values which are not tied to the SpriteInstance.
FlatRedBall.Forms (Gum) components can be added to FlatRedBall Entities just like any other component. If a FlatRedBall.Forms component should be positioned in world space, it can be added to an otherwise-empty entity type, then that entity can be used just like a normal entity.
For example, a DialogBox instance can be positioned in world space by creating an entity in FRB which contains a DialogBox component:
Create a new Entity called DialogBoxContainer in FRB
Add a DialogBoxRuntime instance to the DialogBoxContainer entity
For this demo, all of the code to show the dialog will exist in the Player code, so we need to access the internal DialogBoxRuntime (Gum component). Therefore, it should be marked as public. Select the DialogBoxRuntimeInstance object and change its scope to public.
The DialogBoxRuntime can now be used to show a dialog box on the player. For example, the following code can be added to the Player.cs in a standard platformer project:
Whenever the enter key is pressed, a DialogBox instance is created and appears above the player.
The first step is to add the entire Gum screen as an object in the Glue screen:
Now the Screen can be added to the Glue Layer:
Now the entire GumScreenInstance (that is the entire MainScreen from Gum) will be drawn on LayerInstance.
If you have already created Glue screens and Gum screens, but the two are not associated, you can right-click on the Files of any Screen to add an existing Gum screen: Regardless of which method is used, the result is a Gum screen that is using a specific, generated class, as can be seen in Glue:
Adding an instance to a Layer For information on adding a Gum runtime instance to a layer, see the .
Note that it is possible to add Gum components to FlatRedBall entities, and doing so results in the coponents being drawn in world coordinates (FRB coordinates). Adding a Gum component to a FlatRedBall Entity is the easiest way to position a Gum object in world space. For more information see the .
The Gum Properties Tab provides access to common properties and actions for the entire Gum project. It can be accessed by selecting the Gum project (usually GumProject.gumx in Global Content Files), then selecting the Gum Properties tab.
Note that if you do not have a GumProject.gumx in Global Content Files, you need to add a Gum project first. The easiest way to do this is to click the Gum icon in the toolbar.
If checked, this property will automatically create a new Gum screen whenever a Glue screen is created. The newly-created Gum screen will be part of the Gum project and will be automatically added to the Glue screen. In other words, once created the screen is fully integrated into the project.
The Gum screen will be named the same as the Glue screen with the word "Gum" appended. For example, if GameScreen is added, then it will contain GameScreenGum. Gum Screens and Inheritance Although both Glue and Gum screens support inheritance, Gum screens in derived Glue screens will not inherit from the base Gum screen. The reason for this is because the base screen will always load its Gum screen. If the derived Glue screen contained a derived Gum screen, then objects in the base Gum screen would be loaded twice. To understand this, we can consider an example with two Glue screens:
GameScreen (the base Glue screen)
Level1 (the derived Glue screen)
In this case, GameScreen will have a Gum screen called GameScreenGum, and it will always load this screen even if the actual screen created is the derived Level1. Therefore, if Level1 is loaded, then both of the Gum screens are loaded: GameScreenGum and Level1Gum. If Level1Gum inherited from GameScreenGum, then all of the contents in GameScreenGum would get created twice at runtime.
Gum and the Gum plugin provide a number of options for dealing with various game resolutions. This tutorial will walk you through the different ways to work with resolutions.
When you work with a Gum project, you can specify the resolutions that you're targeting. This can be done as follows:
Go to Edit->Project Properties
Change the "DefaultCanvasHeight" or "DefaultCanvasWidth" to the desired values.
Notice the blue dotted line changing in response to the changed values.
Gum allows you to zoom your entire UI in or out at runtime. This can be useful in a number of situations:
If you are creating a game that targets a lower-resolution device (such as a non-retina iOS device), but you want the game to also run on retina devices. In this case you may want to zoom your UI when running on retina displays.
If you are intentionally creating a low-resolution game to give it a "retro feel".
If you are setting the resolution of your game through Glue, then the Gum canvas size will automatically adjust to the starting value as set by Glue in generated code. In other words, even though our Gum project was defined as a 400x300 project and our game is 800x600, the Gum canvas will expand to 800x600 automatically. Note that this requires that the game's resolution and orthogonal values are equal, as shown in the following image:
For situations where Orthogonal Values do not equal the game's resolution, see the section below. In the previous example the NineSlice still took up 1/4 of the screen even though the Gum canvas expanded. The reason for this is because the NineSlice was using "absolute" width and height values which were set to 400x300. If we change the width and height values to be relative to the container of the NineSlice (which is the entire screen) then the NineSlice will automatically expand to fill the entire Screen. To do this, set the NineSlice's width and height values to be relative to its container (which is the entire screen):
Select the NineSlice instance
Set the "Width Units" to "RelativeToContainer"
Set the "Height Units" to "RelativeToContainer"
Although the generated code automatically adjusts the canvas width and height according to the Gum settings, your game may require adjustment of the resolution after launching. Common examples of this may include docking a Windows 8 app, or resizing a Windows desktop app. For this example my Screen will set the resolution to 500x500, and then I'll adjust the Gum canvas to match:
So far we've looked at situations where the orthogonal value equals the resolution of the game. Games which use an orthogonal camera and target mobile devices may use fixed Orthogonal Width and Orthogonal Height values in Glue, but the resolution depends on the device. We can simulate this on PC by setting the values as shown in the following Glue Camera window (we'll set the resolution to be extra wide to help show the problem):
In this case, the Gum rendering engine will expand itself to fill the entire rendering area, but it will maintain its aspect ratio. In other words, our canvas will be 800 units tall (to match the game's resolution), but the width will be limited to the 4:3 aspect ratio as set in the Orthogonal Values.
In this case we will update the canvas width to match the game's aspect ratio as shown in the following code:
This adjustment will result in the canvas widening to the appropriate aspect ratio. Since the nine slice automatically fills up the screen (when UpdateLayout is called), this will expand the nine slice to the full display width:
If your game is going to run at a fixed resolution, then you can set the resolution of your Gum project and everything will match. For information on how to set the resolution of your project in Glue, see this page.
For this example, I will set my entire game resolution (in Glue) to be 800x600 (the default), but I will make my Gum project use a resolution of 400x300. I will add a single NineSlice in Gum which will take up the entire (400x300) view: If the game is run now you'll notice that the NineSlice only takes up a quarter of the screen: You can tell the Gum project to zoom everything 2x, which results in the the NineSlice being twice as wide and twice as tall - resulting in it taking up the entire screen. More generally speaking, this makes the bounds of the Gum screen twice as tall and twice as wide, so it will match the resolution of the game at runtime. You can add the following code to your screen's CustomInitialize:
For this example, remove the code to set the Camera.Zoom value. If we run the game now, the NineSlice will automatically fill up the entire screen:
The CanvasWidth and CanvasHeight values can be adjusted in an event reacting to the resizing of a FRB window as follows:
Gum screens support animations which can be played through code. This article shows how to access a Gum animation and play it in your FlatRedBall project.
To access a Gum screen in code you must first create an object in Glue for the Gum screen. For information on how to do this, see this article. Animations can be defined in Gum or in code. To define an animation in Gum, see the Gum Usage Guide.
Similar to screens, components can also contain animations which can be played at runtime. To play an animation on a component at runtime:
Add an animation to a Gum component
Obtain a reference to the component at runtime. For example, get a reference from a Gum screen in a Glue screen's Objects.
Call the Play method on the desired animation within the component instance in code. For example, if the ButtonRuntime has an animation called FadeOutAnimation, the following code could be used to play the animation:
All animations are instances of the GumAnimation class, which provides useful variables, methods, and events.
The EndReached event is raised by the animation after the animation reaches its end (which is defined by its Length property). To use the EndReached event, you can add an Action to it, as follows:
Gum objects support playing .achx animations. This enables the use of the familiar AnimationEditor to create texture-based animations for Gum objects. Note that AnimationChain animations are used to change texture or texture coordinates. These are typically used for animated characters in 2D games. AnimationChain animations cannot change the size, orientation, or other common Gum properties.
Glue Version 12 introduces automatic calling of AnimateSelf on the top-level Gum screen. If your Glue project is using an earlier version than version 12, you will need to explicitly add the following to your CustomActivity:
For more information on Glue versions, see the gluj/glux page.
For this example we will assume a Sprite named SpriteInstance.
This Sprite is contained in a page named MainMenuGum which is assumed to be inside a FlatRedBall Screen named MainMenu.
We can set the Sprite to be animated using the following code in CustomInitialize:
The Gum Sprite now animates when running the game.
Named events can be used to perform custom logic at certain times in an animation. Since named events are created in Gum, the animator can align the performance of logic visually. For example, a Gum animation may be used to move components of a title screen into view. While this animation is playing the game may need to play sound effects and music at certain times. Named events can be used to play music and sound effects at certain times.
Named events can be added to animations in Gum in the animation window. To add a named event to an existing animation:
Select the animation
Click the Add Named Event button
Enter a name for the event
Select the event
Enter a time for the event
Gum animations have an AddAction method which can be used to add custom actions at certain times: For example, if the screen were accessible in code as GameScreenGumRuntime in a Glue screen, the MakeYellow named event could be reacted to as shown in the following code:
Animations added to Gum screens can be accessed in code. The Glue code generator will append the word "Animation" to animation names. For example, consider a Gum screen containing an animation called MoveToRight: Then this animation can be accessed in code: