Data Binding

Data binding is a very powerful feature that allows developers to control the game UI through the game state. That eliminates a lot of boilerplate that would otherwise be needed.

Data binding basics

Detailed documentation about how the player works with data binding can be found here.

Data binding makes it possible to update UI elements in response to game state changes. Data binding requires a "model" that is created in the game and exposed to the UI. The UI elements' styles are then bound to the model and each time that the UI calls a synchronize method, all changes made in the game are applied to the UI.

The following example illustrates how to create a simple health bar through data binding.

Complete example

The example is a single HTML page that uses the cohtml.js script and the Coherent Prysm Player.

<!DOCTYPE html>
<html lang="en">

<head>
    <style>
        body {
            background-color: white;
        }

        .health-bar {
            position: relative;
            border: 5px solid black;
            border-radius: 15px;
            overflow: hidden;
            width: 300px;
        }

        .health-bar-value {
            background-color: rgb(0, 128, 0);
            height: 50px;
            transition: width 0.1s linear;
        }

        .dying {
            background-color: rgb(192, 32, 0);
        }
    </style>
</head>

<body>
    <div class="health-bar">
        <div class="health-bar-value"
            data-bind-class-toggle="dying:{{state.currentHealth}} < {{state.maxHealth}} / 2"
            data-bind-style-width="{{state.currentHealth}} / {{state.maxHealth}} * 100 + '%'">
        </div>
    </div>

    <script src="cohtml.js"></script>
    <script>
        const model = {
            maxHealth: 100,
            currentHealth: 50
        };

        engine.whenReady.then(() => {
            engine.createJSModel("state", model);
            engine.synchronizeModels();

            setInterval(() => {
                state.currentHealth = Math.random() * state.maxHealth;
                engine.updateWholeModel(state);
                engine.synchronizeModels();
            }, 1000);
        });
    </script>
</body>

</html>

Basic page

The starting HTML page looks like the following.

body {
    background-color: white;
}

.health-bar {
    position: relative;
    border: 5px solid black;
    border-radius: 15px;
    overflow: hidden;
    width: 300px;
}

.health-bar-value {
    background-color: rgb(0, 128, 0);
    height: 50px;
    transition: width 0.1s linear;
}

.dying {
    background-color: rgb(192, 32, 0);
}
<div class="health-bar">
    <div class="health-bar-value"></div>
</div>
Style Purpose
.health-bar Creates a rectangular border that illustrates the total health of the player.
.health-bar-value Creates the filled part of the rectangle that represents the current health as a percent of the total health.
.dying Creates the style that applies danger.

In order to setup the data binding and communicate with the game the cohtml.js script is included in the HTML page.

<script src="cohtml.js"></script>

Creating the model

The example uses a simple way to create the game state model - JavaScript mocking.

const model = {
    maxHealth: 100,
    currentHealth: 50
};

engine.whenReady.then(() => {
    engine.createJSModel("state", model);
    engine.synchronizeModels();
});
Method Purpose
engine.whenReady Promise that is resolved when Coherent Prysm is ready to use data binding.
engine.createJSModel("state", model) Creates a mock model named state from the simple JavaScript object that contains the currentHealth and the maxHealth variables.
engine.synchronizeModels() Updates the UI with the game state.

Applying data binding attributes

Data binding attributes are expressions added to the HTML that change a CSS property with the model's value.

<div class="health-bar">
    <div class="health-bar-value"
        data-bind-class-toggle="dying:{{state.currentHealth}} < {{state.maxHealth}} / 2"
        data-bind-style-width="{{state.currentHealth}} / {{state.maxHealth}} * 100 + '%'">
    </div>
</div>
Data binding Purpose
data-bind-style-width="{{state.currentHealth}} / {{state.maxHealth}} * 100 + '%'" Sets the width of the filled part of the bar to a percent of the total health.
data-bind-class-toggle="dying:{{state.currentHealth}} < {{state.maxHealth}} / 2" Applies the .dying style when the health drops below half.

Updating the UI

The UI is updated when the state is updated. In the example this is done every 1000ms by updating the game state and then updating the UI.

setInterval(() => {
    state.currentHealth = Math.random() * state.maxHealth;
    engine.updateWholeModel(state);
    engine.synchronizeModels();
}, 1000);
Method Purpose
state.currentHealth = Math.random() * state.maxHealth; Updates the currentHealth of the state to a new valid value.
engine.updateWholeModel(state); Notifies Coherent Prysm that the mock model has changed.
engine.synchronizeModels(); Updates the UI with the game state.

Data binding in the Exporter

Prototyping UI with data binding depends on mock models that should be defined in a JavaScript file and added as external JavaScript files to the Coherent Prysm document.

When the prototyping stage is over the external files that define the models are removed and the game continues to work the same, but with the real models.

Data binding attributes and events are attached to the UI through the "Coherent Prysm 2.0" panel.

Data binding attributes in the Exporter

Data binding attributes are applied to elements through the Bindings tab.

The full list of data binding attributes can be found here.

Interface Purpose
Plus icon Creates a new data binding attribute.
Apply to children checkbox Checking this applies the data binding attributes to the children of the movie clip.
Data binding attribute dropdown Sets the type of the data binding attribute.
Value input Sets the value of the data binding attribute.
Trash bin icon Removes the data binding attribute.

The following steps describe how to add a data binding attribute.

  1. Click the + icon next to the "Data binding attributes" accordion menu.
  2. Select a data binding attribute from the "Data binding attribute" dropdown.
  3. Write a value for the attribute in the "Value" input.

Units in data binding attributes

By default, the data binding system treats numbers as pixels. For convenience, a different unit can be selected for the following properties.

Data binding property
data-bind-style-left
data-bind-style-top
data-bind-style-width
data-bind-style-height

A unit type is selected through the "Unit suffix" dropdown.

Selecting a unit results in something like the following data-bind-style-left="({{state.hudPosition}}) + 'vw'" that transforms the data binding attribute value to a string and forces Coherent Prysm to use the unit.

The following table describes the units that the options set.

Interface Purpose
vw Sets a vw unit.
vh Sets a vh unit.
percent Sets a % unit.
px Sets a px unit.
unitless Doesn't set a unit.

Data binding events in the Exporter

Data binding events are applied to elements through the Bindings tab.

The full list of data binding events can be found here.

Interface Purpose
Plus icon Creates a new data binding event.
Apply to children checkbox Checking this applies the data binding events to the children of the movie clip.
Data binding event dropdown Sets the type of the data binding event.
Value input Sets the value of the data binding event.
Trash bin icon Removes the data binding event.

The following steps describe how to add a data binding event.

  1. Click the + icon next to the "Data binding events" accordion menu.
  2. Select a data binding event from the "Data binding event" dropdown.
  3. Write a value for the event in the "Value" input.

The following variables can be used in the value of data binding events.

Variable Purpose
event The JavaScript event object from the fired event.
this The DOM element on which the event handler is registered.

Data binding aliases

Data binding aliases are a Coherent Prysm Exporter only feature that helps in the creation of components. They are templates used in data binding expressions that are resolved when the document is published.

Creating data binding aliases

Global data binding aliases are created through the Global data binding aliases menu in the Document tab.

Symbol data binding aliases are created through the Data binding aliases menu in the Bindings tab.

Data binding aliases resolution

When a document is published, the data binding aliases are resolved from the scene of the document down to its elements. The expressions are then evaluated with the resolved data binding aliases. Aliases in children deeper down the scene override global aliases and aliases further up the document. Symbol aliases are visible to all of its children.

For a document that contains the following chain of symbols. Scene -> Symbol 1 -> Symbol 2 where Symbol 2 is a child of Symbol 1 and Symbol 1 is a child of the Scene.

And the following aliases.

Global alias name Global alias value Symbol 1 alias name Symbol 1 alias value Symbol 2 alias name Symbol 2 alias value
clrProp globalClrProp clrProp symbol1ClrProp sizeProp symbol2ClrProp
sizeProp globalSizeProp - - nameProp symbol2NameProp
nameProp globalNameProp - - - -

The resolved aliases in the symbols are the following.

Scene alias name Scene alias value Symbol 1 alias name Symbol 1 alias value Symbol 2 alias name Symbol 2 alias value
clrProp globalClrProp clrProp symbol1ClrProp clrProp symbol1ClrProp
sizeProp globalSizeProp sizeProp globalSizeProp sizeProp symbol2ClrProp
nameProp globalNameProp nameProp globalNameProp nameProp symbol2NameProp

Using data binding aliases

Resolved data binding aliases are used in expressions through the #(aliasName) syntax. When an #(aliasName) is found in an expression it is replaced with the value of the alias for the symbol that contains the expression.

Data binding aliases can be used in the following places.

Menu that accepts data binding aliases
"Data binding attributes" in the "Bindings" tab
"Data binding events" in the "Bindings" tab
"Bind value" in the "Events" tab
"Custom attributes" in the "Properties" tab
"Children custom attributes" in the "Properties" tab

The following tables describe how expressions are evaluated for specific aliases.

Alias name Alias value
model hudModel
clrProp hudColor
Expression Result
#(model).#(clrProp) hudModel.hudColor
#(model).#(sizeProp) hudModel.#(sizeProp)