The “Magic” behind Data Binding (Part 1)

Logo Data BindIf you read my introduction post last week, you already know that I’m quite into Data Binding at the moment, as I just published a Data Bind asset on the Unity3D Asset store.

After reading through the documentation I wrote for the asset, my co-founder Nick suggested to add an article what goes on behind the scenes. This also sounded like a good idea for a blog post, so I’ll just be lazy and combine those with this dev blog post 🙂

The article will be quite technical, but I hope you can follow anyway. If you have any questions, just put a comment below the post, I’m happy to answer it!

An interface between logic and presentation

The main idea about data binding is to separate the logic/model/data from the view/presentation/UI. A goal which leads to a solid software architecture is to make sure that your game can be played completely without any visualization which has several advantages:

  • Running unit tests easily on the command line (e.g. on the build server)
  • Changing the presentation side without much risk to break your game
  • If you plan a multiplayer part, you’ll need this separation anyway for a dedicated server

To get those advantages, data binding provides a clean interface between your logic and presentation side of the game. The interface manages the communication between the two sides, which requires:

  1. Logic provides data for the presentation side and informs it when the data changes
  2. Presentation side may need to change data values
  3. Presentation has to have the possibility to send commands to the logic side, mainly player input
Data Bind Interface
Data Bind Interface to connect presentation and logic

Let’s see now how Data Bind for Unity implements those requirements.

Contexts and data properties

The first requirement demands a way that the presentation side can get data from the logic side. What would you visualize otherwise? In addition it also has to tell the presentation side when a data value changed.

Furthermore the presentation side needs to have a way to change the data on the logic side, e.g. the volume of the music in the option menu.

The requirements are fulfilled by the classes

and

Data Property

The property class is not much more than a wrapper around a data value. This way the data property knows when the data value changed and can inform its listeners:

As you can see, the “magic” behind this class is minimal at most.

Data Context: Setup

The data context isn’t much more than a collection of this data properties and it defines the data which the logic makes available for the presentation.

Now to keep the internals as capsuled as possible, the recommended way of defining a data property is like this:

From the outside the class looks like a simple data holder and as a user there is really not much more to know about it to make use of the asset. But the goal of this dev post is to give you some insights about what happens behind the scenes, so have a look at the base class Context, the driving force behind the whole asset.

Data Context: Using a data value

The base class Context has only three public methods which are used from the presentation side to access the data the context provides:

  • object RegisterListener(string path, Action<object> onValueChanged)
  • void RemoveListener(string path, Action<object> onValueChanged)
  • void SetValue(string path, object value)

The methods are quite self-explaining: RegisterListener / RemoveListener  let’s you add/remove a callback for when the data at the specified path changed. SetValue  changes the data value at the specified path.

For a simple data property of the context, the path would be the name of the public property, so in the example above “Text“. There can be nested contexts as well, where you would need a path like “SubContextA.SubSubContext2.DataValueName” to access it, but let’s stay with the simple case for now.

When you call the RegisterListener  method, the context searches for the data node which represents the path. There are several information stored in the DataNode object:

  • Type Info
    • Type of the data
    • FieldInfo if it’s a field
    • PropertyInfo if it’s a property
    • MethodInfo if it’s a method
  • Name (if it’s a field or property)
  • Current value
  • Child nodes
  • ValueChanged event delegate with listeners for value changes
  • Data property which belongs to this node

The nodes are cached, so each one is only looked up once.

When the correct data node for the path was found, the context registers for the ValueChanged  event on it and then returns the current data value. The RemoveListener  method does almost the same, but removes the delegate from the ValueChanged  event.

Even the SetValue  method works similar: After finding the correct data node, it just sets its value. The data node then takes care about setting a new value for the data property if it has one.

Data Context: Matching path and data property

To find the data property which belongs to a data node, the naming of the data property is important. To keep things simple, there is a naming convention when using data properties: You append “Property” after the desired name of your data value, like “textProperty” in our example.

So when the data node is created, the type info, name, value,… are taken from the data value itself, but the data property which capsules the value, is searched by:

  1. A public or non-public field with lower case name plus “Property” (e.g. “textProperty”)
  2. A public property with name plus “Property” (e.g. “TextProperty”)
  3. A public or non-public field with name plus “Property” (e.g. “TextProperty”)

It is recommended to use the first way as it makes sure that the data properties can’t be used from outside the context.

If no data property is found, but there is a data value with the name, the data node is still created and used. So you can also use normal properties and fields on your presentation side. However, the presentation side doesn’t get informed about any data changes in this case, so you might only use this if you have some constant values which don’t change after creating the context.

Summing it up for part one

The blog post really seems to get a bit long already, so I will explain how commands work and what goes on with the bindings on presentation side in a follow-up post.

That was quite a lot of details for the context class already. If you are a bit familiar with reflection and check out the code of the context class yourself, it should be straight forward now what happens where. Remember the following things:

  • A data property is just a wrapper around a data value, so it is possible to determine when a data value changed
  • A context provides a collection of data properties for the presentation side to use
  • The data is looked up via name. The data property has to follow a naming convention (i.e. appending “Property”)

If you have any questions or found something that you think could be made easier or faster, let me know in the comments or contact me. It’s a nice and clean approach in my opinion, but there may be room for improvements of course.

Thanks for reading, hope it was interesting for you! Feel free to share the link to the blog post anywhere you want. And if you like to look into the code of the asset, just buy it for only 10$ in the Asset store.

Oh, and don’t miss the following parts by subscribing with your mail address on the left! 🙂