Skip to content

Bindings and Pre/Post Render Callbacks

Binding a widget to an instance variable

It's great that you can create widgets and set their value once; but many times you need to have their values in sync with variables in the game. For example, you might have a progessbar that shows the player's health. When you create it, you can set it to 100% (as, when the game is created, you typically start with full health), but what if the player gets hurt or gets extra max health? We need ways of syncing the value so it is always updated.

If you have a widget that accepts a value, you can bind that widget to an instance variable. Once you do this, its value will be automatically updated each frame.

As an example, let's check out this very simple interactive example1. You can move the player with W A S D or Up Left Down Right keys. You have 5 hp and if you touch a monster you lose one hp. In the top left corner, you can see the player's healthbar, which was greated with gooey:

Not working?

If interaction is not working, click on the black bar just above the game canvas, to activate the iframe element - otherwise keys are processed by the site itself.

In the example, we can see that, each time we lose 1 hp, the progressbar automatically reflects this. To achieve this, we are not manually setting the value every time a monster hurts the player - we did that by binding the player's hit points (i.e. the instance variable hp of the obj_Rogue object) to the value of the progressbar. The code looks as follows (only the progressbar code is shown here):

    var _prog = new UIProgressBar("Test_ProgressBar", 0, 0, undefined, spr_Heart, 0, 0, 5);
    _prog.setRenderProgressBehavior(UI_PROGRESSBAR_RENDER_BEHAVIOR.REPEAT);
    _prog.setSpriteRemainingProgress(spr_Heart);
    _prog.setImageRemainingProgress(1);
    _prog.setBinding(obj_Rogue, "hp");

Binding a widget to a method variable

If you bind a widget to a variable, the widget will have its value set to exactly that value. Ths can be inconvenient for certain use cases, because you might want to convert or somehow transform the raw value of the variable before binding it. However, we can solve this by binding a method variable to the widget. This gives you a lot of flexibility over how to construct the final value that ultimately gets bound to the widget.

For example, let's create a text element that shows a message telling the user where the mouse is:

1
2
3
4
5
6
7
8
    var _panel = new UIPanel("Test_Panel", 0, 0, 400, 300, blue_panel, UI_RELATIVE_TO.MIDDLE_CENTER);
    var _text = new UIText("Test_Text", 0, 0, "", UI_RELATIVE_TO.MIDDLE_CENTER);
    _panel.add(_text);

    self.my_method = function() {
        return string($"The mouse is at {device_mouse_x(0)},{device_mouse_y(0)}");
    }
    _text.setBinding(obj_Game, "my_method");

In the above code, we are creating a method on the object we're creating this (obj_Game) and then binding the value of the UIText widget to the result of the method. You can see it in action here:

Binding support

Starting with gooey 2025.4, the following widgets have two-way binding support (this means they work both ways: setting the value of a widget automatically from an instance/struct variable, and modifying said instance/struct variable when the widget value is modified):

  • UIButton (the button text)
  • UIText (the text)
  • UITextbox (the textbox text)
  • UICheckbox (the boolean value)
  • UIDropdown (the selected option)
  • UIOptionGroup (the selected option)
  • UISpinner (the selected option)
  • UIProgressBar (the value)
  • UISlider (the value)

Before gooey 2025.4, only the following widgets had binding support, and the support was one-way only (making the value of the widget update according to an instance/struct variable):

  • UIButton (the button text)
  • UIText (the text)
  • UIProgressBar (the value)

Pre-render and post-render callbacks

As we saw above, some widgets do not directly support variable binding. Additionally, many times, you need to do more complex processing before or after a widget is rendered each step. For example, you might need to enable or disable a widget based on a game condition, or you might want to modify a widget's size or position every frame to match another. In these cases, you can set a callback that is called every frame for that widget, either before its rendering (pre-render callback) or after it (post-render callback).

In the following example, we will use a pre-render callback to set whether buy buttons in a shop menu of a game prototype are enabled. In particular, we will make it so each button represents an item from a shop, and will be enabled only if the player has enough gold to buy it.

Use W A S D or Up Left Down Right keys to walk over coins to pick them up. Your total gold is displayed in the top left. Watch the shop menu and buttons (made with gooey) at the lower right closely - they start out disabled (note that nothing happens if you click them) but change to enabled when the player has enough money to buy them. Prices also change from red to black:

Not working?

If interaction is not working, click on the black bar just above the game canvas, to activate the iframe element - otherwise keys are processed by the site itself.

In the above example, we are using the setPreRenderCallback() method of the button. We have a _cost local variable, which represents the item's cost in gold, and we are using the setEnabled method to decide whether the button should be enabled. In this case, the condition is that obj_Player.total_gold must be equal or greater than the cost of the item. Since this code is ran every frame (before rendering), the enabled property of the button gets modified accordingly to what the player is doing in the game.

1
2
3
    _button.setPreRenderCallback(method({_i, _cost}, function() {
        ui_get(string($"Button{_i}")).setEnabled(obj_Player.total_gold >= _cost);
    }));

(note we also have an _i local variable because I used a for loop to create three buttons, with unique ID Button1 through Button3, and I'm running the below code three times as well, in the same loop. For simplicity I'm not presenting the complete loop here).

I'm also running a similar pre-render callback for the text, setting the color to red if the player does not have enough gold or black if it does:

1
2
3
4
    _text.setPreRenderCallback(method({_i, _cost}, function() {
        var _color = obj_Player.total_gold >= _cost ? "c_black" : "c_red";
        ui_get(string($"Cost{_i}")).setText(string($"[spr_Items,141][{_color}]{_cost}"));
    }));

Note that I'm using Scribble notation in the text, since that's what gooey uses for rendering text.

Wrapping up

Pre and post-render callbacks are super useful for handling more complex behavior and making the UI react to game events.


  1. Sprites by Seth, freely available at their itch.io page