Skip to content

YuiScript

Quetzy edited this page Jul 13, 2022 · 21 revisions

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.

Using YuiScript In a .yui File

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.

Syntax

Keywords
Literal Values
Identifiers
Supported Operators
Calling Functions
Handling Events
Assigning Values
Directives (Advanced)

Keywords

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

Literal Values

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:

Numbers

YuiScript supports decimal and integer numbers:

  • 123
  • 4.56
  • 0.789 Note: Currently the leading 0 is required in the last case, .789 will not parse correctly. See #17.

Strings

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

Colors

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

Identifiers are how you refer to values that exist elsewhere: in game data (@), slot values ($), and resources (&).

@ Data Binding

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 the weapons variable on the inventory struct of the data context
  • @inventory.weapons[0] would refer to the first item in the weapons array on the inventory 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 the weapons 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 the inventory 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.

Finally, you can use @ on its own to refer to the current data context itself:

  • text: @ would set the text value to the current data context (as a string)

$ Slot Values

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.

& Resource Values

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.

Supported Operators

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.

String Concatenation

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

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()

Calling Methods

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 the attack() method on the @weapon struct ([] also works)
  • @current_target >> @weapon.attack - >> operator works with these as well

Important note about static methods

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.

Handling Events

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

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 as on_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

Assigning values in YuiScript works about the same as it does in GML, but there are limitations on what kinds of values are assignable.

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!)

Not Assignable:

  • @ = '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.

Directives (Advanced)

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:

final

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.

resolve

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!

gm_object

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

trace

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.

Precedence

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)

Top