Data-binding
The data binding feature allows you to modify the UI based on the current state of the game. To better understand how the data binding works, please refer to the corresponding content creation docs. This document only describes how to use it in Unreal Engine.
Overview of the process
Let’s say that you want to display the player’s health somewhere in the UI. On the game side, this feature requires you to do 3 things.
Step 1 - Create a model
You need to tell Prysm what game objects you’d like to expose. Any such exposed object we call a data model.
Blueprint Editor
- Call the
UCohtmlBaseComponent::CreateDataModelFromStruct
/UCohtmlBaseComponent::CreateDataModelFromObject
depending on whether you want to expose aUObject
instance, or aUSTRUCT
.
When iterating a collection of Blueprint struct objects, you should use theGet(a ref)
node if you intend to create or manipulate models. You can read more about this here.Blueprint maps containing BP struct objects also return copies when theFind
node is used. Since the node cannot be changed to return refs, registering models in that manner should be avoided and be done in C++ instead.- Call the
C++
- The same can be achieved with
cohtml::View::CreateModel
:
- The same can be achieved with
ACharacter* Player = GetPlayerCharacter();
CohtmlComponent->GetView()->CreateModel("player", Player);
ACharacter
s. In order for CreateModel
to compile, the only requirement is that Player
is an object which Prysm knows how to bind. Prysm already knows how to bind most Unreal types and their derivatives. If you want to bind a custom type that doesn’t fit in that category, refer to the general Prysm docs for more information.TArray
, you will get a compiler error for unsupported type. You can, however, add these types as properties to your model.Get(a copy) vs Get(a ref) when using Blueprint struct objects
If you use a Get(a copy)
node when iterating a collection of Blueprint struct objects and attempt to create a data model for them, Prysm will prevent you from doing that by ignoring the CreateModel
call, because internally Unreal Engine copies each object data over to the same memory address “buffer”, which would cause incorrect data-binding behavior.
ForEach
Blueprint macro node, as it utilizes the Get(a copy)
node.Step 2 - Update the model
Next, when something in your model has changed in the game, you need to tell Prysm that it needs to be updated in the UI.
- Blueprint Editor
- Use
UCohtmlBaseComponent::UpdateWholeDataModelFromStruct
/UCohtmlBaseComponent::UpdateWholeDataModelFromObject
.
- Use
- C++
- Use
cohtml::View::UpdateWholeModel
:
- Use
ACharacter* Player = GetPlayerCharacter();
CohtmlComponent->GetView()->UpdateWholeModel(Player);
Step 3 - Tell the UI you are ready for the frame
Calling the update methods from the previous step does not actually update the UI - these methods only mark certain pieces of it as dirty (the models). To avoid constant UI updates, this is done in another method that walks through all elements (models) that need updates and syncs them with the game - SynchronizeModels
.
- Blueprint Editor
- Use
UCohtmlBaseComponent::SynchronizeModels
.
- Use
- C++
- Use
CohtmlComponent->GetView()->SynchronizeModels();
which internally callscohtml::View::SynchronizeModels
.
- Use
cohtml::View::UpdateWholeModel
and cohtml::View::SynchronizeModels
on your data models before you Tick (which calls cohtml::View::Advance
) the cohtml::View
object to ensure your data binding changes reflect on the UI in the same frame. Otherwise, your changes will make it to the the UI on the next frame. By default, the UCohtmlBaseComponent
ticks at TG_StartPhysics
and the SCohtmlWidget
near the end of the frame after all UE actors have ticked and been rendered. For more information on the ETickingGroup
, you can check this UE documentation page.A complete Blueprint script may look like this: