Skip to content

Property Binding

Grisgram edited this page Mar 17, 2024 · 23 revisions

You may know the concept of data-binding, or property-binding, from other environments, like .net, where it is widely used, especially in UI applications with WPF or MAUI. Mobile Apps normally also use UIs, which are data bound.

raptor3 offers Property Binding too.

What is it used for?

There are many situations, where property binding reduces boilerplate code and keeps things nice and tidy.

Example: Bind an object's position

One example could be, let's say in a Jump'n Run game, where your character has a temporary shield, preventing damage, where this shield shall follow the player.

You could create an object for the shield, set the player object as parent, define a step-event, where you make the x-position of the shield equal the x of the player, and so on. This needs functions, events, code.

Instead, in the Create event of your Shield object, you could simply write:

binder.bind("x", player_instance, "x");
binder.bind("y", player_instance, "y");

...and you're done! This object's x/y coordinates will be bound to the player_instances' x/y and the object will follow the player! No step-event, no draw, nothing special to do, just 2 lines of code.

Example: Bind the sell-value of an item at a merchant in an RPG

Assuming you do an RPG, and the player visits a merchant to sell some of the collected items. You could create that shop-item, get a parent, copy all the values, ... again... code, code, code.

Or, you could do (in the shop item):

// I assume here, that your shop-item is made of several objects, 
// like the icon of the item and the sell value
// and "icon" and "value" (TextBox) are the instances of these sub-items
icon.binder.bind("sprite_index", inventory_item, "sprite_index");
value.binder.bind("text", inventory_item, "sell_value", NUMBER_TO_STRING_CONVERTER);

This is just two of many thinkable scenarios, where you can keep your code simple and clean, without boilerplate copy-blocks of values. As a "binding" is a "live" value, you even have the guarantee, that everything on screen gets updated correctly, if something changes!

Tip

You can bind any property of your current object to any property of a source instance or struct. There's no restriction!
In the example above, a "text" property of a TextBox is bound to a sell_value of an item, and above that the position of one item is mirrored to another one. You can even bind struct-members (or entire structs!) freely.

What about performance of these things?

Of course, everything comes with a performance penalty. If you create thousands of bindings, you might run into performance issues, but I don't think, that's a standard case. There's no problem in binding a couple of objects to each other, or to bind a value like the Score or Game Time to some UI Element. It just reduces code.

The "binder" variable

Each child of _raptorBase holds a variable called binder.
With this, you can bind any value of the current object to any value from a source object (your current object is always the receiver of a bound value, not the sender).

This binder is of type PropertyBinder and offers these functions:

bind

/// @function bind(_my_property, _source_instance, _source_property, _converter = undefined)
/// @description Bind any of your properties to any source and property of the source.
/// @param {string} _my_property	The name of your local property to bind
/// @param {instance/struct} _source_instance	The object or struct, that holds the value, you want to receive
/// @param {string}	     _source_property	The name of the property, you want to receive
/// @param {function}	     _converter		A function receiving 1 argument: the value from the source.
///						Must return a converted value, that the object can use.
/// @param {function}	     _on_value_changed	A callback function receiving 2 arguments: (new_val, old_val)
///						This callback is only invoked, if the bound value changed.

The converter function (if set), is invoked every time, the value shall be compared to the previous value.
The on_value_changed callback only runs, if a difference between the old and the new value is found.

unbind

/// @function unbind = function(_my_property)
/// @description Deletes the binding of the specified property.

unbind_source

/// @function unbind_source = function()
/// @description Deletes ALL bindings where the current object instance is registered
///              as the SOURCE of a binding.
///		 NOTE: This is called for you in the "CleanUp" event of _raptorBase!
///		 It ensures, that your game doesn't crash, when an object gets destroyed.
///		 Due to the nature of Step, it is impossible to say, whether the binding
///		 engine or the instance, that receives a binding will processed first.
///		 So see this as a security belt in case of an unlucky processing order.

unbind_all

/// @function unbind_all = function()
/// @description Deletes ALL bindings of the current object.
///		 NOTE: This is called for you in the "CleanUp" event of _raptorBase!
///		 Normally, you do not need to deal with this method.

Converter Functions

These are small functions, that take one input value (the new value from the source of the binding), and convert it to a target value/format, the receiving object can use or assign.

Here are two examples of "standard" converters, which are delivered with raptor:

#macro STRING_TO_NUMBER_CONVERTER	function(_value) { return real(_value); }
#macro NUMBER_TO_STRING_CONVERTER	function(_value) { return string(_value); }

Getting started

Raptor Modules

Clone this wiki locally