We built a small UI system in Doodle Safari to help quickly build complex UI without coding. Below I’ll explain how the system works and how to use it to build your own UI.
The system supports 3 types of elements: UI Panel
, UI Button
and UI Label
. It uses a UI Controller
to manage these elements. You generally want an Entity with a UI Controller
and any number of elements to represent a part of your UI. For example a Player name display might be made up of a UI Controller
, a UI Panel
for the background and a UI Label
element for the name.
Important Note: Since
UI Controller
is an admin only script you cannot add it to Entities yourself. To work around this you should create a Template from an existing UI Entity in Doodle Safari and work from there.
Each UI element shares some general properties that allow you to position it on screen. They also have a smaller set of their own properties. Let's go over the shared properties first:
Element Id - This should be unique across a single Entity containing UI elements. It is used for a few purposes like parenting elements, referencing them in animation sequences or accessing them via script.
Parent Element Id - Set this to the “Element Id” of an element on the same Entity to parent it to that element. This will cause all of its positioning to switch to the space of that parent. For example if you set “Y Anchor” to “Bottom” it will anchor to the bottom of the parent instead of the screen. It will also move with the parent and inherit its scale.
Platform - This can be used to make an element only appear on a specific platform or all platforms. For example setting “Platform” to “Mobile” on a UI Label will cause it to not appear in the game when played on PC. It’s a good way to make your UI work on mobile and non-mobile platforms without building two separate UIs.
X Anchor - This can be used to attach an element to the left, center or right of the screen or parent element. Additional offsets can be applied using the “X” property.
Y Anchor - This can be used to attach an element to the top, middle or bottom of the screen or parent element. Additional offsets can be applied using the “Y” property.
X- The number of pixels or normalized units to move this element horizontally.
Y - The number of pixels or normalized units to move this element vertically.
Width - The width of the element. This will default to an actual width, but you can override the default width here if needed.
Height - The height of the element. This will default to an actual height, but you can override the default height here if needed.
Coordinate Space - “Pixels” will make the “X”, “Y”, “Width” and “Height” properties use screen space. “UI” will make them use normalized screen space (Between 0 -1).
Scale - The scale of the element.
Sort Order - A higher sort order will place the element on top of elements with a lower sort order. This only works within Voxel Object elements or Text elements on the same Entity. Text is always rendered on a higher layer so it can’t be placed behind Voxel Objects.
Disabled - If true, the element will not send events when interacted with and depending on the type of element may look different.
Event Tag - If set, any hover and press events will be sent to this tag. You can find script examples for those events below. Hover and press events are always sent to the Entity the element is on.
// Sent when an element is hovered over
onHoverBegin(elementId: string) {}
// Sent when an element is no longer hovered over
onHoverEnd(elementId: string) {}
// Sent when an element is pressed
onPressBegin(elementId: string) {}
// Sent when an element is no longer pressed
onPressEnd(elementId: string) {}
UI Panel elements can be used to display Voxel Objects or they can just act as a container for other elements.
Data Id - If set to a value that exists in a Database this will try to find an “Icon” in that data and display it as the Voxel Object.
Opacity - Determines how transparent the Voxel Object is. Unfortunately if a Voxel Object has any transparency it will be rendered after all of the opaque Voxel Objects so Sort Order will not work.
Voxel Object - If set, will display the Voxel Object on screen at the position and scale of the element.
UI Buttons are similar to UI Panels but offer additional Voxel Object fields so they can change appearance as they are hovered over and pressed.
Normal Voxel Object - The Voxel Object to display when the button is not being interacted with. This will also act as the default for any other states that are not set.
Hovered Voxel Object - The Voxel Object to display while the button is being hovered over but not pressed.
Pressed Voxel Object - The Voxel Object to display while the button is being pressed.
Disabled Voxel Object - The Voxel Object to display while the button is disabled.
Click Sound - the sound to play when the button is pressed and released.
Keybind - The shortcut key that can be pressed to press this button. This only works on non-mobile browsers.
UI Label displays text on screen and comes with some ways to dynamically inject data into that text. Labels can also react to hover and press events by changing color.
Font - A font must be set for a label to appear.
Text - The text to display on screen. Any text enclosed in curly braces will be replaced with the value of the matching key in Player save data. For example if I had a Collectable in the game with a “Data Id” of “coin” I could set my text to “Coins {coin:0}”. The label would display the number of coins I have collected or default to 0 if I haven’t collected any. This works for any key in the save data.
Autoscale - If true, this will scale long text down to fit the “Max Width”.
Max Width - The maximum width that text can be if “Autoscale” is true.
Normal Color - The color of the text when it is not being interacted with.
Hovered Color - The color of the text while it is being hovered over but not pressed.
Pressed Color - The color of the text while it is being pressed.
Disabled Color - The color of the text while it is disabled.
There are a variety of things you can do with your own scripts and UI. Here are some ways that UI can be manipulated through script on the same Entity:
// Disables an element
this.entity.sendEvent(“disableUIElement”, “myElementId”);
// Enables an element
this.entity.sendEvent(“enableUIElement”, “myElementId”);
// Checks to see if the game is on a mobile browser
this.entity.sendEvent(“isOnMobile”, (isOnMobile: boolean) => {
// Work with the result here
});
// Checks to see if the Player is touching the screen on a mobile browser
this.entity.sendEvent(“isTouchDown”, (isTouchDown: boolean) => {
// Work with the result here
});
// Gets the last position a Player touched the screen on a mobile browser
this.entity.sendEvent(“getLastTouchPosition”, (position: Vector2) => {
// Work with the result here
});
// Checks to see if the mouse or touch position is over any UI element on an Entity
this.entity.sendEvent(“isInputOverlapping”, (isOverlapping: boolean) => {
// Work with the result here
});
// Checks to see if the Player pressing the mouse button
this.entity.sendEvent(“isMousePressed”, (isMousePressed: boolean) => {
// Work with the result here
});
// Gets the UI Element Transform for a specific element
this.entity.sendEvent(“getUIElementTransform”, “myElementId”, (transform: any) => {
// Work with the result here (See the “UI Controller” script for more details)
});
// Gets all UI Element Transforms on the Entity
this.entity.sendEvent(“getAllUIElementTransforms”, (transforms: Array<any>) => {
// Work with the result here (See the “UI Controller” script for more details)
});
// Sets the parent of an element on this Entity to an element on a different Entity
this.entity.sendEvent(“setParentUITransform”, otherEntity, “targetElementId”, otherUITransform);