Creating a component-based game (Part V: Data Binding)

I already wrote some posts about data binding and our Unity asset Data Bind for Unity which gives you a clean way to separate your logic from the presentation of your data. The last parts were mostly about the logic side though, so let’s have a look how to combine those two.

This example uses an older version of the Slash Framework which can be downloaded here and is in the branch examples/kill-stuff of the repository. If you want to follow the examples step-by-step make sure to use those older versions. If you are already familiar with the framework you may be able to adjust the code to fit the current version of the framework.

Installing Data Bind

As soon as you’ve got the asset from the asset store, you can just unzip it and will have a new folder “Slash.Unity.DataBind” in your Assets folder. Everything DataBind related is in there.

The repository contains a stripped version of the asset, so you can still check how it is used. If you like it and want to use it in your own projects, consider to support us and buy it in the store. You’ll get many more tools at your hand with the full asset 🙂

Creating a HUD

When connecting logic and presentation of your game, you can approach this connection from both sides. Either you create the context first and setup all the data that should be available for the presentation. Or you create the presentation first and connect it with the correct data afterwards.

Most of the time I take the latter approach. It is easy to think of what the player should see in the end, especially UI-wise. So let’s start using the new Unity UI to create a nice HUD for the player where he can see how many bullets he has left, how many he can have at maximum and an indicator that shows up when the player should reload (i.e. has only one bullet left).

A simple HUD in our game, showing the current and maximum bullets and a reload indicator on low ammo
A simple HUD in our game, showing the current and maximum bullets and a reload indicator on low ammo

As you can see I’m not a UI designer 😉 Luckily there are some free resources available on the internet, so I didn’t have to draw myself. But you should get the idea. The player will see opaque bullets for the ones that are still in his clip and grayed out bullets for the ones he already shot. The alarm sign will be visible when he has only one bullet left or when his clip is empty.

Creating the data context

Designing the UI often makes clear already which data you will need to fill it. In our small HUD we need only two data values:

  • Current number of bullets
  • Maximum number of bullets

So our data context will be quite small, too:

If you are using DataBind more regularly, you should definitively add the code snippets we provide, so you can create new data properties even faster.

Back to our HUD UI scene. Now that we have our data available, we can use it to steer our UI. Therefore just put a ContextHolder on the root object. This is where the data context will be put.

To make editing the bindings easier, you can already tell the ContextHolder which type of context will be placed here. This way DataBind can already find out which data is available and give you some drop down fields to define the data paths. Otherwise you would have to type in the path manually which is a bit error-prone and less comfortable.

Context holder on the HUD root
Context holder on the HUD root

Adding bindings

Reload indicator visibility

Let’s setup the reload indicator first. As we said it should only be shown if the weapon has one or none bullets left. DataBind already comes with the most common scripts on board, so we don’t have to write a single line of code to achieve this behavior.

The first script we use is the ActiveSetter. It enables/disables a specified game object depending on a boolean data value. There are three ways where this value can come from:

  1. Data Context
  2. Constant
  3. Data Provider

In our case we are not using a value from the data context directly but need an additional script, the ComparisonCheck. This script acts as a data provider by comparing two data values which again can come from different data sources. Here we use the data value “CurrentBulletCount” from the data context and the constant value 2 and compare both with the lesser than comparer.

Reload indicator uses two built-in scripts for setup
Reload indicator uses two built-in scripts to decide if it should be visible

Note: A common mistake (which I made myself a lot of times) is to put the ActiveSetter on the same GameObject which should be activated/deactivated. This will deactivate it correctly but never activate it again, as the script itself is deactivated together with the GameObject.

Bullet count display

We like to show bullets for each one left in the weapon and grayed out ones for already used ones. We can use the same way for both, using the built-in LayoutGridItemsSetter script. This scripts creates a child GameObject from a specified prefab for each item in a collection or (in our case) as many GameObjects as the data value specifies.

Capacity of the player's weapon is visualized with grayed out bullets
Capacity of the player’s weapon is visualized with grayed out bullets

The available bullets use a normal bullet for display and the CurrentBulletCount instead of the MaxBulletCount data value from the context.

Testing the HUD

When you design your interfaces the data to fill it might not be available yet or it is too tedious to always start your game and go to the screen you are designing to check for your changes.

As the data context completely hides the underlying logic from the presentation you can easily create dummy contexts to test your UI without even starting the game. Just derive it from your real context class and put in some test values.

There is a script called ContextHolderInitializer which will initialize a specific ContextHolder with an instance of a context on startup. It was created for exactly this use case to test a UI with a dummy context.

Using ContextHolderInitializer to use DummyWeaponContext for testing
Using ContextHolderInitializer to use DummyWeaponContext for testing

Now if you hit the play button, the context holder gets initialized with the DummyWeaponContext and the UI displays your testing values.

UI filled with testing values from DummyWeaponContext
UI filled with testing values from DummyWeaponContext

Using the data context

Updating the data when things change

Right now we have our logic working and our data context works together with the UI. The missing part is to connect our logic to the data context itself, so the data in it is updated from the logic.

For this case we created a new base class in our previous projects which connects a data context to the game logic. With the Slash framework this works via events and the EntityManager. If you are using a different framework to implement your logic, you may have to adjust this base class accordingly.

Our weapon context now derives from this GameContext instead of the Context class and listens to events to update its data when things change:

Creating the data context

The data contexts should be created right after the game was created, so they don’t miss any important events, e.g. for initialization. Most of the time you will have a hierarchical context structure, starting at one root context which creates all the underlying contexts.

The root context also takes care about the recreation of the data contexts when a new game was started. We put it on a game object in the Root scene.

Script to create the  contexts for the game
Script to create the contexts for the game

When the HUD scene is loaded, its ContextHolder has to be initialized with the correct context. In bigger projects you will have a window management or something similar which takes care about this. Right now we will just attach a small script which searches for the root context and sets its weapon context to the ContextHolder.

Loading the HUD

The last tiny bit is to load the HUD scene. The Slash framework contains a small script called LoadSceneBehaviour which can load a scene additionally. We can just use this to load the HUD scene into the Root scene on startup.

Using the LoadSceneBehaviour script to load the HUD scene additionally
Using the LoadSceneBehaviour script to load the HUD scene additionally

Now if you start the game the contexts are created by the GameRootContext script, the LoadSceneBehaviour loads the HUD scene and the WeaponContextInitializer sets the weapon context on the ContextHolder of the HUD UI. This triggers the update of the controls in the UI with the provided data.

If you fire your weapon you should see the UI react accordingly. The reload indicator should be visible when you have less than 2 bullets left.

The HUD in the game, reacting on data changes
The HUD in the game, reacting on data changes

Conclusion

Visualizing the data of your game with Data Bind for Unity is a task which is very separated from creating your logic. This is quite important when working on a bigger project as you don’t want the presentation to interfere with your logic.

After you have created reasonable data contexts that define which data the presentation side of your game has access to, you don’t have to write a lot of code anymore. For the main use cases there are already scripts in the Data Bind package, e.g. to hide GameObjects or to instantiate UI elements based on the values in your context. When the data values change the UI elements are updated accordingly.

From our personal experience this clean separation has already saved us a lot of time, especially when the UI changes during a project. In that case you just have to adjust the UI design and don’t have to touch your code. So there is much less potential to introduce new bugs.

There will be another update to 1.0.4 soon which adds more helpful scripts that we created along with our projects and I will raise the price to 20$ with that update because of the bigger content. So if you want to try out the whole power of Data Bind for Unity, consider purchasing it now from the Unity Asset Store to save some money. This will support us in further developing our assets and doing the extra work to put them together in a package which easy for others to use.

If you have any questions or comments on the topic, just leave a comment, write me mail or leave a post in the official Unity forum thread.

  • Adrien

    Hi Christian!

    Thanks for the tutorials. I realize that this tutorial hasn’t used many Unity components (I think you only used Transform?).

    I’m still a beginner and am trying to learn your framework, and I’m wondering if all of Unity’s objects can be integrated into the Slash Framework? (E.g., Unity’s Touch class for touchscreen touch data). Can this framework be used conveniently with Unity’s physics system as well? (For e.g. Rigidbodies, colliders, gravity).

    Sorry for the dumb questions.

    • coeing

      Hi Adrien,

      The framework replaces a lot of Unity’s stuff indeed. In the end Unity’s only job is to do the presentation of the logical entities, effects (based on logical events) and the user interface.

      The physics system is a real good example where Unity can still be helpful. In one of our game jam games, called Bones (http://www.slashgames.org/bones/), we used our framework as well. But the physics simulation was done by Unity. It wasn’t hard to connect it to the logic, in the end the physics system is a Game System as well as the other ones. We just added a wrapper around it which sends events into the logic (e.g. EntitiesCollided) and receives events about entities with a collider that were added to the game.

      Cheers
      Christian

      P.S.: We have a saying in Germany: “There are no dumb questions, only dumb answers” 🙂