-
Notifications
You must be signed in to change notification settings - Fork 4
YuiScript
YuiScript is a mini scripting language embedded in YUI, which enables you to create advanced data binding and template behavior. At a basic level it's very similar to the GML you use in the rest of GameMaker, with some small differences either for convenience or due to limitations.
When you want to use YuiScript to define a value in a .yui file, you need to use an expression that begins with one of the three signifier characters, @
$
and &
, that tell YUI to treat the value as a YuiScript expression, or use the @@
escape syntax to use an expression that doesn't start with one of the signifier characters. For example:
type: button
content: @label // data binding
border_color: $button_border_color // slot value
background: &menu_button_background // resource value
tooltip: @@ 'Tip: ' + @tip_text // escape syntax
Most of the time you will naturally use one of the signifier characters at the start of an expression, but there are a few cases (such as event handlers) where you will need to use the @@
escape syntax.
Keywords
Literal Values
Identifiers
Supported Operators
Calling Functions
Handling Events
Assigning Values
Directives (Advanced)
YUI supports the following keywords:
Keyword(s) | Description |
---|---|
equals |
equivalent to == operator |
not |
equivalent to ! prefix operator |
and or
|
use instead of && and || (which are not supported) |
then else
|
use instead of ?: operator (which is not currently supported) |
true false undefined
|
equivalent to the same in GML |
In the Keywords section above you can see that using true
false
and undefined
works the same in YuiScript as it does in GML, but we'll often need to use other kinds of literal values: Numbers, Strings, and Colors:
YuiScript supports decimal and integer numbers:
123
4.56
-
0.789
Note: Currently the leading0
is required in the last case,.789
will not parse correctly. See #17.
You can write string literals in a few different ways:
'using single quotes'
`using backticks`
-
hello
- you can use an unquoted string without spaces if it doesn't match the name of a Keyword or Arrow Function parameter
You can use standard #RRGGBB
hex format to use colors in YuiScript expressions, in the same way supported in GML (as of GameMaker 2022.2):
background: #FF0000
- will set the background
property to a red color (in this case it's the same as the c_red
GML constant)
Some element properties also support including the alpha in the color value, in #AARRGGBB
format:
border_color: #880000FF
- will set the border_color
to the same blue as c_blue
with alpha at 0.5.
Check the element's property section to see if alpha is supported for a particular property.
Identifiers are how you refer to values that exist elsewhere: in game data (@
), slot values ($
), and resources (&
).
The @
character lets you bind to data on the current Data Context. For example, if the current data context is your player object, you might use @health
to bind to the health
variable on the player object.
You can use .
and []
to access struct values or array indexes, for example:
-
@inventory.weapons
would refer to theweapons
variable on theinventory
struct of the data context -
@inventory.weapons[0]
would refer to the first item in theweapons
array on theinventory
struct
You can even use other identifiers with []
:
-
@inventory.weapons[@selected_weapon_index]
would resolve@selected_weapon_index
to its value, and then use that value to access theweapons
array
You can also use []
to access struct variables dynamically:
-
@inventory[@category]
would resolve@category
to a value, and then access the variable with that name on theinventory
struct.-
Note: Unlike GML which requires you to use the
[$]
accessor to access struct variables dynamically, YuiScript can automatically detect whether a value is a struct or array and use the correct accessor for you.
-
Note: Unlike GML which requires you to use the
Finally, you can use @
on its own to refer to the current data context itself:
-
text: @
would set thetext
value to the current data context (as a string)
The $
character lets you access slot values in the definition of a Template. A Slot is essentially a custom property defined by the template, and the $
syntax let's you use that slot value within the definition.
Here's the very simple definition of the built-in meter_bar widget:
meter_bar:
type: template
slots:
min: 0
max: 100
value: 0
meter_sprite: yui_white_pixel // default to a white pixel
meter_blend_color: null
template:
type: border
content:
type: image
sprite: $meter_sprite
blend_color: $meter_blend_color
size:
w: $value / ($max - $min),
w_type: proportional
h: stretch
Here you can see how $value
and so on are used to access the slot values for the slots defined in the slots
section.
You can use .
and []
with slot values in the same way as with @
data bindings.
The &
character lets you access resource values for the current screen. For example, you might want to access a resource when setting an elememt theme value in a theme file:
// declare theme resources
resources:
control_bg: #252525
control_border: #1b1b1b
// theme example (partial)
button:
background: &control_bg
border_color: &control_border
...
text_input:
background: &control_bg
border_color: &control_border
...
Resources are a simple and powerful way to declare values or data in one place and refer to it in many other places.
You can use .
and []
with resource values in the same way as with @
data bindings.
YUI Supports the following operators (from tightest precedence to loosest):
Prec | Operator | Description | Associates |
---|---|---|---|
1 |
() [] . >>
|
Grouping, Subscript, Method Call | Left |
2 |
! not
|
Not | Right |
3 |
* /
|
Multiple, Divide | Left |
4 |
+ -
|
Add, Subtract | Left |
5 | $+ |
String Concatenation | Left |
6 |
< <= > >=
|
Comparison | Left |
7 |
== !=
|
Equality | Left |
8 | and |
Logical And | Left |
9 | or |
Logical Or | Left |
10 |
?? then +else
|
Nullish, Conditional (equivalent to ?: in GML) |
Right |
11 | = |
Assignment | Right |
12 | => |
Arrow Functions | Left |
13 | | |
Directives | Left |
Note: The GML bitwise operators are not supported.
When using the standard +
operator, if the value on either the left or right side of the operator is a string, YuiScript will automatically convert the other to a string and concatenate the result.
However, sometimes neither side is a string, but you want to concatenate the result as a string anyway. For that, you can use the $+
string concatenation operator:
-
10 $+ 200
will result in the string"10200"
-
@left $+ @right
will convert both@left
and@right
to strings and then concatenate them
The second example illustrates a more common case where you might use this, because you might not even know whether either @left
or @right
will be a string.
Calling functions in YuiScript is fairly simple, but first we should be clear about what kind of functions we can call:
- Runtime Functions - the functions built into GML, such as
string_format
- Script Functions - the functions you add via Script Assets, such as
yui_log
(added by YUI, among others) - Methods - these are functions defined on objects and structs within the game, such as in a struct declaration or constructor function. See the GMS Manual on Method Variables if you're unsure what this means.
- Arrow Functions - these are functions you define (and call) directly within YUI (see Handling Events)
The first two kinds of functions are the simplest to use, as you do it the same way you would in GML:
type: button
content: @@ string_format(@health, 0, 0)
on_click: @@ yui_log('Clicked!')
So far this is pretty simple, it looks just like normal GML! But note that we have to use @@
escape syntax since string_format
doesn't start with a signifier character. This is a little ugly, so YUI also provides the infix call operator >>
:
type: button
content: @health >> string_format(0, 0)
What >>
means is to take the value on the left and insert it as the first parameter to the method call on the right. string_format(@health, 0, 0)
and @health >> string_format(0, 0)
function exactly the same. But conveniently, now the first character of the expression is a signifier character, so we no longer have to use @@
escape syntax to call the function on our @health
value.
You can also easily chain function calls with >>
:
on_click: @health >> string_format(0, 0) >> yui_log()
Now that we've seen how to call runtime functions and script functions, calling methods works just about how you'd expect:
type: button
content: Attack!
on_click: @attack(@current_target)
The button defined above will call the attack()
method on the current Data Context, and pass the value of @current_target
as the first parameter. You can use call syntax on anything that evaluates to a method:
-
$attack(@current_target)
- slot values work also -
@weapon.attack(@current_target)
- using.
(subscript) to access theattack()
method on the@weapon
struct ([]
also works) -
@current_target >> @weapon.attack
->>
operator works with these as well
There is one important thing to be careful about when calling methods: if the method you want to call is static
, the function won't be called with the correct scope, which means accessing variables inside that function is likely to fail. If your method doesn't access local variables, then this is fine, but if your method does access local variables, don't use the static
keyword with it.
The examples so far have shown how to run code when a button is clicked using the on_click
property of the button
element. In fact, on_click
is an Event, and it's just one of several that elements can support.
Events are a bit special when it comes to handling them in YuiScript, because events have an event parameter which contains data about the event that just occurred. Consider how to use the text_input
element:
- type: text_input
text: @editable_text
events:
on_text_changed: text => @editable_text = text
The text_input
element has an on_text_changed
event, and when that event occurs you want to be able to access the new text! To do that, we have to define an Arrow Function.
One last thing to know about event handlers is that you can set multiple handler expressions for a single event by using YAML array syntax:
on_text_changed:
- text => @editable_text = text
- text => yui_log('text updated to: ' + text)
Arrow Functions in YuiScript are very similar to Arrow Functions in JavaScript. You define an arrow function using the =>
arrow operator:
parameter_name => some_code_here(parameter_name)
What this does is define a custom function that accepts one parameter, which is named on the left side of the =>
, and when the function is called you can access the value of that parameter by using its name wherever you want to access the value.
Looking again at the event handler in example above:
on_text_changed: text => @editable_text = text
We can now see that this defines an arrow function and names the first parameter text
, which is then accessed in the code on the right side of the =>
simply by using text
again. In this case the code uses =
to assign the value to a variable on the data context.
Some important things to remember about arrow functions in YuiScript:
- You can currently only define one parameter, unlike arrow functions in JavaScript.
- You don't have to use
@@
escape syntax when handling an event built in to YUI (such ason_text_changed
in this example). This is because YUI knows that the value of an event must be a YuiScript expression - it wouldn't make sense to treat the value as a literal string. - You do have to use
@@
escape syntax with events defined as slots on custom widgets. YUI doesn't know whether a given slot is a normal literal value or an event handler, so you have to tell it. See the Widget Gallery code as an example.
Assigning values in YuiScript works about the same as it does in GML, but there are limitations on what kinds of values are assignable.
-
@health = 100
- assigning a value to a variable on the data context -
@inventory.potion_count = 2
- assigning a value to a nested variable on the data context -
@equipped_items[0] = @weapon
- assigning a value to an index in an array -
$scroll_state.y = 0
- assigning a value to a nested variable on a slot (note: you cannot set a slot directly!)
-
@ = 'this is invalid'
- you cannot set the data context itself to a new value -
$weapon = 'this is invalid'
- you cannot set a slot variable directly (only nested variables on slots, see above) -
text => text = "this is invalid"
- you cannot assign to an arrow function parameter
If you try to perform an invalid assignment, YUI will give you an error (usually when loading the screen file).
An important thing to keep in mind about YuiScript is that there are no local variables. You cannot do var name = 'Alice'
, for example.
There are a handful of advanced YUI features available via the |
directive operator.
The syntax for using the |
directive operator is:
@some_yui_expression | directive1, directive2, etc
Essentially, a YUI expression goes on the left, and then to the right of |
you can put a comma separated list of directive names. Directives are evaluated left-to-right in the order that they are written.
The supported directives are:
The final
directive causes the expression on the left side to be evaluated once, and thereafter it will simply return the value of that expression, rather than evaluating it every time. This can be useful when setting live properties on elements or widgets, so that an expression doesn't need to be evaluated every frame if the value won't change after the first time. Using final
can improve performance in these situations.
The resolve
directive can be useful in Templates. When declaring the slots for a template in the slots
section, the default values are treated as Literal Values, and will not be evaluated as expressions, due to certain constraints of the system. If you want a slot's default value to be treated as a YuiScript expression, than you need to use resolve
in the expression where you access the slot.
Consider this partial example from the scrollbox widget definition:
// in slots section
hbar_visible: $scroll_state.info.x_max > 0
vbar_visible: $scroll_state.info.y_max > 0
// in template definition
- type: viewport
canvas:
right: $vbar_visible|resolve then $bar_thickness else 0
bottom: $hbar_visible|resolve then $bar_thickness else 0
The hbar_visible
and vbar_visible
slots control the visibility of the horizontal and vertical scrollbars. We want the default visibility to be true
when the scroll_state's x or y maximum value is greater than zero (don't worry about the logic of this if it doesn't make sense).
In order for those default values to be evaluated as expressions we need to use the resolve
directive when accessing those slots, as seen in the second section of the example.
Warning: The resolve
directive can be used outside of this situation, but be careful as any string that evaluates to a YuiScript expression will be resolved, so don't use it if the expression being resolved could be manipulated by the player!
The gm_object
directive can be used to refer to an object instance in GameMaker by its object name. It's a fairly common pattern in GameMaker to have some 'global objects' where only a single (usually persistent) instance of it exists, usually created in the loading room for a game. In some cases you may be able to set that object as the Data Context for a yui_document
, but sometimes you'll want to access a global object from within a specific subsection of a UI, such as an obj_music
or obj_audio_system
object within an Audio Settings screen. For that you can use the gm_object
directive:
type: text
data_source: @@ obj_music|gm_object // sets the data context to the first instance of the `obj_music` game object in the current room
text: @current_track_name // displays the name of the current track
The trace
directive sets the trace
property to true
on the resulting expression object created in GML. Generally this is only useful for the YUI developer(s) contributing to the project.
The |
directive operator has the lowest precedence of all operators, which means it will evaluate everything to the left of the |
before evaluating the directives themselves. You can target a directive more precisely by placing the full directive expression in parentheses:
(obj_player|gm_object).health
- accesses the health
variable on the obj_player
instance by its object name (equivalent to obj_player.health
in GML)
Home - Elements - Built-in Widgets - YuiScript - Themes -- Custom Widgets