Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The default behavior for DataUiGrid is to reflect properties from the assigned Instance. When the Instance is assigned, the Grid automatically populates its Categories with members based on reflection.
Properties which have the System.ComponentModel.CategoryAttribute
will be categorized according to the category assigned. Otherwise, properties will be put in the Uncategorized category.
By default all public fields and properties are displayed in a DataUiGrid. To control which properties are shown, the categories can be cleared, and individual InstanceMembers can be added for the desired properties.
For example, the following code clears the properties, and shows only X, Y, and Z:
This page discusses all of the classes in Gum at a high level to give you an understanding of how they relate to each other.
The following hierarchy shows a typical (although small) Gum project:
Gum Project Save
ScreenSave
InstanceSave
InstanceSave
StateSave
VariableSave
VariableSave
VariableSave
ScreenSave (etc...)
ComponentSave
InstanceSave
StateSave
VariableSave
VariableSave
VariableSave
ComponentSave (etc...)
StandardElementSave
StateSave
VariableSave
VariableSave
Notice that GumProjects contain ScreenSaves, ComponentSaves, and StandardElementSaves. ScreenSaves, ComponentSaves and StandardElementSaves all share the same base class: ElementSave. Therefore, a lot of Glue code will work with ElementSaves rather than the specific derived type.
ElementSaves can contain instances, and each instance is of type InstanceSave. Therefore, if you wanted to print out information about all of the instances in a given screen you might do something like this:
ElementSave is the base class for ScreenSave, ComponentSave, and StandardElementSave. ElementSave is often used when working with instances of the derived classes due to their similarities, such as referencing instances, states, or converting the ElementSave to a GraphicalUiElement at runtime.
The Categories member provides more detailed control over a DataUiGrid. The easiest way to work with a DataUiGrid is to assign its Instance and let it automatically create UI for all public members. Working with Categories requires more code but gives the most flexibility in setting up a grid.
The following makes the grid only show a single value called "Some Value" which always has a value of 10. An actual implementation may modify some backing variable.
The GraphicalUiElement class represents any visual element in the Gum runtime. An instance of a GraphicalUiElement exists for every standard instance as well as instances of components. When loading a .gumx file in a project (such as a MonoGame or FlatRedBall project), each Screen is also a GraphicalUiElement.
ApplyState can be used to apply a state (StateSave) to a GraphicalUiElement. States can be set by direct StateSave reference, or by unqualified name. Direct StateSave assignment supports states defined in Gum or dynamically created states.
Gum is an open-source tool which enables you to pull code for any other project, or to make modifications so that the tool fits your needs.
Gum uses the following technologies:
.NET 4.6 (included with Visual Studio)
Windows Forms (included with .NET 4.0)
XNA Game Studio 4.0 https://www.microsoft.com/en-us/download/details.aspx?id=20914
For information on running from source, see Running from Source.
For information on how to write Gum plugins, visit this page: Plugins
The AddToMangers methods adds the calling GraphicalUiElement to the SystemManagers. This should be called if the GraphicalUiElement needs to be treated as a root-most object. This is automatically called if an ElementSave's ToGraphicalUiElement passes true
for its addToManagers
parameter.
ToGraphicalUiElement creates a new GraphicalUiElement (visual Gum object) from the calling ElementSave. This method is typically used to create new Screen or Component instances from a Gum project.
The conversion of a GraphicalUiElement can optionally add the resulting GraphicalUiElement to managers. This value should be true if the object is not going to be added as a child of any other GraphicalUiElement.
The following code can be used to convert a Screen named "MainMenu" to a GraphicalUiElement. In this case, the MainMenu is obtained from a loaded Gum project:
The addToManagers
parameter determined whether the GraphicalUiElement is automatically added to managers so that it is directly drawn. This can be called after the element is created, so the following code shows two ways of adding the element to managers:
All GraphicalUiElements need to either be added to managers directly or indirectly. Elements added directly to managers are drawn by GumService.Default.Draw();
. Any item that is added to managers automatically draws its children, so only root-most objects should be added to managers.
Let's look at common scenarios and and discuss when to set addToManagers
to true.
Elements should have addToManagers
set to true
if any of the following are true:
The GraphicalUiElement serves as the root for all other elements. Typically this would be when a Gum project is loaded, the Screen is obtained from the Gum project's Screens
property and its ToGraphicalUiElement
method is called.
You are performing simple tests such as adding a Gum component to screen for testing, you may want to set addToManagers to true so that it shows up on screen. Note that if this is an object with events (such as a Gum Forms object), it must either be passed to the GumService.Default.Update
call or it will not respond to the mouse, keyboard, or gamepads.
The most common case for setting AddToManagers to true is if you are creating a Gum screen from a loaded Gum project.
Elements should have addToManagers
set to false
if any of the following are true:
If the GraphicalUiElement is being added as a child of another element which has already been added to managers. For example, if a new element is being created and added to an existing Screen or one of its children.
If the GraphicalUiElement is being added as a child to existing root containers, such as FrameworkElement.PopupRoot
or FrameworkElement.ModalRoot
.
If the GraphicalUiElement is being added to managers on a dedicated layer. For example, a Screen could be created and added to a dedicated UI layer to control sorting of all elements in the Screen relative to other manually-created GraphicalUiElements.
Absolute values can be obtained from a GraphicalUiElement by using the appropriate absolute values. these values return the value of the GraphicalUiElement relative to the screen.
The following absolute values are available:
AbsoluteLeft - Returns the absolute left edge of the GraphicalUiElement
AbsoluteTop - Returns the absolute top edge of the GraphicalUiElement
AbsoluteRight - Returns the absolute right edge of the GraphicalUiElement
AbsoluteBottom - Returns the absolute bottom edge of the GraphicalUiElement
AbsoluteX - Returns the absolute X of the GraphicalUiElement considering its XOrigin
AbsoluteY - Returns the absolute Y of the GraphicalUiElement considering its YOrigin
The following assumes that container is a valid GraphicalUiElement. Its absolute position is used to draw a Sprite using SpriteBatch.
The DataUiGrid is similar to Winforms PropertyGrid - a reflection-based UI object which can be used to display the properties on objects in real-time. It is used for the properties on Gum objects, but is written to be general purpose to be used in any applications.
It is included in the WpfDataUi.dll file which is part of Gum, so this library can be pulled out and used in any other application.
The DataUiGrid can be used with reflection or its Categories can be manually populated. Using reflection is easier to set up, but does not provide as much flexibility. Manually building up Categories takes more work, but provide the most flexibility.
The following references are needed for displaying the DataUiGrid:
PresentationCore
PresentationFramework
System.Xaml
WindowsBase
To add a grid to your XAML you'll need to:
If using .NET 6+, add the following using:
If using .NET 4.7, add the following using:
Add the following inside a layout container (like a Grid):
You can construct a grid in code just like any other WPF control.
To use the grid in code you simply need to set its Instance member to an instance object you want to view. For example:
This will produce a grid which looks like this:
Alternatively the Instance property can be data bound as shown in the following XAML:
The BlendState property allows a GraphicalUiElement to blend with what is drawn before it. BlendStates can be used to control both color and alpha blending. Most games do not need to modify BlendState, and coverage of BlendStates is beyond the scope of this documentation.
The Gum BlendStates type mimics the XNA (MonoGame) BlendState. For more information see on BlendState, see the MonoGame BlendState API reference:
For examples of custom BlendStates, see this post:
By default Gum objects render directly to the back buffer, so the alpha value of the back buffer does not matter.
If you are rendering to a render target, then you may want to modify the blend state so that the object being renders adds to the alpha. The following code shows how to do this:
The Camera object is used to adjust the position of all objects in a game and provide zooming capabilities. By default the camera is positioned at 0,0, so all objects are drawn in screen space. Games which need to render objects in world space can do so by applying camera offsets.
By default the Camera's position is at X=0 and Y=0, with the top-left of the Camera defining its position. If the camera moves, then all Gum objects appear to move in the opposite direction. For example, increasing the Camera's X value results in the camera "moving" to the right, resulting in objects in screen moving to the left.
The following code shows how to move objects in response to the MouseState:
By default the Camera's Zoom value is set to 1. This Zoom value applies a global zoom to all drawn UI. This can be adjusted to make all UI bigger.
The following code shows how to zoom in and out.
Note that the text that is positioned along the right and bottom does not update its position in response to the zoom. The reason for this is because the canvas size has not been adjusted.
This can be fixed by also adjusting the canvas as shown in the following code. Note the following code assumes a default width of 800 and height of 600:
The AnimateSelf method performs .achx animation logic, advancing the displayed frame on the calling GraphicalUiElement and its children. This call is recursive, so it is typically only called on the root GraphicalUiElement (such as the current Screen).
The following shows how to call AnimateSelf on a GraphicalUiElement. Typically this is called every frame in activity. For example, this code shows how to animate a GraphicalUiElement in a MonoGame project:
AnimateSelf is called automatically in FlatRedBall projects in generated code.
CanvasHeight is a static property which tells the Gum layout engine the size of the canvas. This is used for layouts on GraphicalUiElements which have no parent.
CanvasHeight and CanvasWidth are typically assigned to the height and width of your canvas. This can depend on the development environment. These values are used for layouts, especially layouts which depend on the canvas (or window) width and height.
Setting these values does not automaticaly cause all GraphicalUiElements to perform their layout calls. If these values are changed then any GraphicalUiElements which have no parents should have their Layout method called. Setting properties on a GraphicalUiElement (such as changing WidthUnits) may also perform a layout.
If your game is not zoomed, then the CanvasWidth and CanvasHeight should match the graphicsDevice width and height as shown in the following code:
If responding to a window resize or zoom change you may need to also call UpdateLayout on any objects which do not have parents. Usually this is a single root object, as shown in the following code:
The Children collection contains the direct descend children of the GraphicalUiElement. An instance's children will report the instance as their parent.
Note that Screen GraphicalUiElements have null
for Children. The reason is because Screen GraphicalUiElements do not have a position or size - they are merely containers for children without providing any layout information. Therefore, to access the items that a Screen contains, see the ContainedElements
property.
If a GraphicalUiElement is loaded in a game project, its Children property contains the direct children as established in Gum.
For example, consider a component with six children named ColoredRectangleInstance, ColoredRectangleInstance1, ... , ColoredRectangleInstance5:
If using code generation, these could be accessed by their names. If not using code generation, or if you need to access each item by index, then the component's Children property provides access.
For example, the following code could be used to set the children width:
Of course, if you know the type of the children, you can cast the child to its specific type. Be careful doing this, as you may end up with an invalid cast exception.
For example, the following code could be used to edit the children as ColoredRectangleRuntimes:
UpdateLayout performs a layout call resulting in the GraphicalUiElement's renderable object updating its X, Y, Width, and Height values. UpdateLayout may recursively call UpdateLayout the caller's parent and children.
Usually UpdateLayout does not need to be called explicitly since it is automatically called in response to Values on the GraphicalUiElement changing. The most common situation for calling UpdateLayout is when the GraphicalUiElement CanvasWidth or CanvasHeight properties change, such as in response to a game window resizing. For more information, see the CanvasHeight page.
Most GraphicalUiElements wrap a renderable component such as a Text, Sprite, or Rectangle. Although applications treat GraphicalUiElements as if they are renderable themselves, that is technically not the case. Rather, GraphicalUiElements are simply wrappers around renderable components. The GraphicalUiElement is responsible for performing layouts and ultimately setting the internal Renderable's Parent, X, Y, Width, and Height. This is done in the UpdateLayout call.
If a GraphicalUiElement has not changed, then it does not call UpdateLayout for performance reasons. For performance reasons Gum attempts to minimize the number of UpdateLayout calls performed. Of course, it is impossible to eliminate all UpdateLayout calls, especially when a Screen or Component is first created.
Gum attempts to always keep renderable components up-to-date. Therefore, Gum automatically calls UpdateLayout if a GraphicalUiElement's property changes which may cascading impacts on the contained renderable, children of the GraphicalUiElement, or the parent of the GraphicalUiElement.
RemoveFromManagers removes the calling GraphicalUiElement from the SystemManagers. This method should be called on GraphicalUiElements which need to be destroyed but do not have parents. To remove a GraphicalUiElement which has parents, remove it from its Parent's Children.
The following code shows how to add and remove a GraphicalUiElement.
Keep in mind that if a GraphicalUiElement is not added to managers, it does not need to be removed from managers. For more info see the Parent page.
This property returns the elements that this contains. This should be used if the GraphicalUiElement represents a Gum screen. Note that the ContainedElements does not respect hierarchy - all contained instances are contained here whether they have a parent or not. Therefore, to obtain only the top-level instances, the Parent property should be checked.
GetChildByNameRecursively returns a GraphicalUiElement by the argument name. This method is recursive so it searches the entire child hierarchy to find a child. If no match is found, null is returned.
Note that this performs a depth-first search, returning the first instance found. Therefore, if multiple items exist with the same name, this may result in confusion.
The following code can be used to find a Text by the name TextInstance and set its Text property to "You found me":
The Parent property get or sets the calling GraphicalUiElement's parent. If Parent is set to a non-null value, then the newly-set Parent's Children gets modified too. Therefore, it is possible to either add a child to its Parent's Children or set its Parent directly. You do not need to do both.
The following code shows how to add a SpriteRuntime to a parent ContainerInstance
. It assumes that ContainerInstance
is a valid GraphicalUiElement:
Later, the sprite could be removed by setting its Parent to null:
Gum Forms objects can be added to or removed from parents by setting Parent. For example, the following code could be used to show a button in a popup and remove it when it has been clicked:
The static IsAllLayoutSuspended property controls whether GraphicalUiElement layout calls are skipped. If this value is true, all GraphicalUiElements return immediately when calling Layout rather than performing a full layout. This includes explicit calls to Layout as well as calls performed as a result of changing properties such as Width.
This property is false by default, which mean layout is not suspended.
By default changes to Gum immediately result in a layout. For example, consider the following code:
The parent in the code above may be sized according to its children. If so, any change to the child (X, Y, Width, and Height) cause the parent to update its layout. In addition, adding the child to the parent also results in a layout call. Furthermore, the parent may contain other children which would be updated in response to the parent. This code could result in hundreds or even thousands of layout calls.
However, typically a layout is not needed until all objects have been added and after all of their variables have been modified. Therefore, the following modifications could be made:
The UpdateLayoutCallCount property can be used to measure the number of UpdateLayout calls. It can be used to compare the number of layout calls reduced by using IsAllLayoutSuspended.
For example, the followign code can be used to display layout calls:
This produes a large number of layouts:
We can add additional code to suppress layouts until we are finished adding all children:
This results in far fewer layout calls:
The GumProjectSave object is a serializable object representing a Gum project. Specifically, the GumProjectSave object can directly serialize to the .gumx file format. The GumProjectSave provides access to all Screens, Components, and Standard Elements. Typically the GumProjectSave is accessed through the ProjectState singleton object.
The following code loops through all screens, components, and standard elements in the current project and adds their names to a list of strings:
The InstanceMember class is used in the DataUiGrid's Categories to control which properties are displayed and how the user can interact with those properties.
InstanceMembers can be created manually or automatically. For more information on using InstanceMembers in a DataUiGrid, see the and its subpages.
The SinglePixelTexture is a reference to a Texture2D which is used to draw solid-colored Gum objects such as ColoredRectangle (renderable type SolidRectangle).
By default the SinglePixelTexture is constructed at runtime in-memory, but this can introduce a large number of rendering state changes when rendering multiple objects.
For performance reasons, the single pixel texture should be a PNG which also has other art in your Gum project. Ideally your project should have just one large PNG which includes a single pixel texture, all fonts, and all other sprites used in Sprite and NineSlice instances.
To assign the SinglePixelTexture, you must access the Renderer. This can typically be accessed through the default SystemManagers as shown in the following code:
RaiseChildrenEventsOutsideOfBounds determines whether InteractiveGue instances check all children for events even if the children fall outside of the bounds of the instance. This is false by default for performance reasons, but is needed if a parent has children which are intentionally placed outside of its bounds.
An InstanceSave represents an instance in Gum. InstanceSave only stores two pieces of information:
The name of the instance
The base type of the instance
All InstanceSaves in Gum will be contained in an ElementSave. Information about the InstanceSave's variables (such as its position or size) is not stored directly on the InstanceSave. The reason for this is because InstanceSaves have variables which depend on States. In other words, an InstanceSave may be at X=0 in one state, and at X=100 in another state.
Therefore, all variables values that define the InstanceSave are stored in states within the InstanceSave's container ElementSave.
In this pic the Screen1 ScreenSave has 3 InstanceSaves: 1. TextInstance 1. ColoredRectangleInstance 1. ColoredRectangleInstance1
The following code shows how to access the X value of an ElementSave as defined in the default state of its container.
Layers provide the ability to sort renderable objects, to independently control zoom, and to keep objects drawn in screen space even if the Camera's X or Y has changed.
The following code shows how to create a Layer:
This code creates a new Layer which draws on top of unlayered objects, and on top of all previously-added layers.
To add a renderable (such as a GraphicalUiElement) to a layer, the AddToManagers method takes a second parameter for the layer. The following code shows how to add a newly-created GraphicalUiElement to a Layer:
All children within a layered renderable also draw on the same layer.
LayerCameraSettings can be used to override default behavior. If no LayerCameraSettings instance is created, then a Layer's zoom and screen-space behavior matches all unlayered objects.
The following code creates LayerCameraSettings which keep all objects on the layer in screen space regardless of the Camera's position:
LayerCameraSettings can be used to offset all renderables on a layer independently. For example, the following LayerCameraSettings results in every object offset by 100 pixels to the right:
The example above results in 100 pixel offsets added to the camera's position. If a layer has its IsInSceenSpace property set to true, then the offset is absolute and ignores the camera's position. For example, the following code offsets all objects by 50 pixels on the Y axis (down) relative to screen space (ignoring Camera position);
LastFrameDrawStates is an IEnumerable for the draw states used in the previous draw call. This can be used to find performance problems and isolate what may be causing state changes.
The following code can be used to output state changes to the Output window in Visual Studio
The SpriteRenderer class is responsible for performing MonoGame/FNA-based rendering. It exposes an interface similar to SpriteBatch, but it provides additional functionality including storing a stack of states and providing information about state changes which can be useful for diagnostics.
The SpriteRenderer lives as an object in the Renderer class and can be accessed through the Renderer instance. For example, you can access the SpriteRenderer as shown in the following code:
A VariableSave represents a single variable on an object, such as its X or Y value.
VariableSaves can be of any type, can contain any value, and can hold values either on the Component itself or on a contained instance.
By default an instance does not own its variables. Rather, variables exist inside of states which can affect the instance. For example, consider the following image:
In this case, the GameScreenGum is the owner of the variable, not TextInstance. The GameScreenGum has the following:
GameScreenGum (ScreenSave)
Default State (StateSave)
Variables (List<VariableSave>)
TextInstance.Text = "Hello from Gum" (VariableSave)
This relationship may seem complicated, but it is requires because the Screen can create multiple StateSaves (including StateSaves inside of other StateCategorySaves). The actual value of the variable depends on which StateSave is being considered.
Furthermore, the effective value of a variable (such as "Hello from Gum") can be the result of variables being assgined at multiple levels.
For example, consider that the TextInstance in the example above is setting its text to "Hello from Gum" at the ScreenSave level. This value overrides the Text value which is assigned on the standard Text object.
The following lists the levels where variables can be assigned:
Base standard (such as on the Text under the Standard folder)
On an instance in a component (such as a Text instance in a Button component)
On an instance of a component through an exposed variable (such as a Button instance setting its text to "Quit" in a GameScreen)
In a derived class (such as a derived ExitButton which inherits from Button, but sets the TextInstance.Text to "Exit")
In a categorized state (such as a DarkMode state changing the color of a Button's background)
The hierarchy of which variables take precedence over other variables can be complicated which is why Gum provides a way to get the effective value for a variable.
The GetValueRecursively method provides a way to get the effective value for an object given a state. For example:
The SelectedState class gives you information about what the user has selected in the app. This includes which ElementSave (that is ScreenSave, ComponentSave, or StandardElementSave)/InstanceSave/StateSave is selected.
To get the current ScreenSave:
To get the current InstanceSave: