Synchronizing your own data with custom Realtime Components

In this guide, I’m going to walk you through how to synchronize custom data in your Unity application using custom Realtime Components.

Realtime Components

Realtime Components are by far the most common way to synchronize data in your application. A RealtimeComponent script is responsible for keeping a specific part of your scene in sync. The most common built-in component is RealtimeTransform, which synchronizes the transform of a GameObject.

The flow of data looks something like this:

The flow of data in a Normcore application.

A RealtimeComponent keeps a game object in sync with its corresponding model in the datastore. When the game object changes, it updates the model, and when the model changes, it updates the game object to match. This means that in the diagram above, when Player 1 moves a game object, RealtimeTransform can set the new position on its model in the datastore. When Player 2 gets a notification that the model changed, it can update the position of the matching game object in its scene.

Creating a custom RealtimeComponent

For this example, I’m going to make a component that synchronizes the color of an object. In order to create a custom component, you’ll also need to create a model to put in the datastore. Let’s start with by making a script for the component, and one for the model. We’ll call these ColorSync.cs and ColorSyncModel.cs respectively.

Creating a RealtimeModel

First, we’re going to start by writing the model. Start by removing : MonoBehaviour from the class definition. Models shouldn’t be a subclass of any other class. Next, add a private field to hold our color value. One quick note: variables need to be private and they need to start with an underscore. Once you have you’ve added your property, your class should look something like this:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColorSyncModel {
    private Color _color;
}
  

Now, this isn’t enough to synchronize our model with the datastore. We need a few methods to expose this property publicly, detect changes to the color, and send any changes to the server. Luckily, Normcore comes with a plugin that will write all of that logic for us and ensure that it’s written with correctness and performance in mind. In order to use it, we’re going to add a [RealtimeModel] attribute to our class, and a [RealtimeProperty] attribute to the _color variable. Your class should look something like this:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Normal.Realtime.Serialization;

[RealtimeModel]
public class ColorSyncModel {
    [RealtimeProperty(1, true, true)]
    private Color _color;
}
  

Here I’ve added a [RealtimeModel] attribute to signal to Normcore that this is a model class, this will be relevant in the next step. Second, I’ve added a [RealtimeProperty] attribute to the color property. RealtimeProperty has three arguments:

PropertyID

The first is the property ID. This is a unique ID that is used by Normcore to identify this property among others when sending data to and from the server. This ID must be unique to the model, and if you make any changes to this property, you’ll need to assign a new ID. This will ensure that newer versions of your application can still communicate with older versions. If you would like to delete this field, make sure to retire its property ID. Comment out the field and leave it in your source code so you can see previously used property IDs.

Reliable / Unreliable

This marks whether the property should be synced reliably or unreliably. In general, if you plan to change a property very often (such as animating a color, or moving a transform), you should use an unreliable property. Unreliable updates are not resent if they’re dropped in transit because it’s expected that another update is following shortly after.

Reliable properties are good for things that you update once, but that should be resent if the packet is dropped in transit. This is great for state such as whether your game has started or not. When you change it, Normcore will ensure this value is synchronized between all clients and will make sure it is in sync before any newer reliable updates to are applied to the datastore.

Change Event

The last option is an optional argument that specifies if you would like a change event added to the model. When this is set to true, a C# event is added that will fire when a property is changed locally or remotely. This is a useful signal to update your scene to match the model.

Now that we have our model defined, it’s time to compile it. Go back to Unity and highlight the ColorSyncModel.cs file in your project. The inspector should look something like this:

Now that our class has a [RealtimeModel] attribute on it. Normcore will detect it and display the model inspector. If your inspector is not updating correctly, make sure your Unity project has no compile errors. If Unity is unable to compile this class, it will be unable to detect the changes you’ve made to your model.

In order to actually make use of the model, we’ll need to compile it. With the inspector up, click the “Compile Model” button. If we look back at our ColorSyncModel.cs class, it should look something like this:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Normal.Realtime.Serialization;

[RealtimeModel]
public partial class ColorSyncModel {
    [RealtimeProperty(1, true, true)]
    private Color _color;
}

/* ----- Begin Normal Autogenerated Code ----- */

/* Your autogenerated model code will appear here. */

/* ----- End Normal Autogenerated Code ----- */
  

You can ignore all of the autogenerated code. Normcore has compiled everything needed to keep this model in sync with the datastore. It’s also added a public property called color that we can use to get or set the value on the model. It’s also added a colorChanged event that we can use to detect changes to the model.

Now that our model is ready, let’s implement the ColorSync component to synchronize the color of a GameObject.

Open up the ColorSync.cs script we created earlier. First we’re going to do a little house-keeping. Change the superclass from MonoBehaviour to RealtimeComponent, and be sure to add using Normal.Realtime; to the top of your file. This will bring in Nomrcore, and allow it to automatically detect your component later on. We’re also going to add some code to Start() to grab a reference to the Mesh Renderer, as well as a model property that will be automatically populated by Normcore:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Normal.Realtime;

public class ColorSync : RealtimeComponent {

    private MeshRenderer   _meshRenderer;
    private ColorSyncModel _model;

    private void Start() {
        // Get a reference to the mesh renderer
        _meshRenderer = GetComponent<MeshRenderer>();
    }

    private ColorSyncModel model {
        set {
            // Store the model
            _model = value;
        }
    }
}
  

First up, we’re going to write a method that synchronizes the color stored on the model to the mesh renderer:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColorSync : RealtimeComponent {

    private MeshRenderer   _meshRenderer;
    private ColorSyncModel _model;

    private void Start() {
        // Get a reference to the mesh renderer
        _meshRenderer = GetComponent<MeshRenderer>();
    }

    private ColorSyncModel model {
        set {
            // Store the model
            _model = value;
        }
    }

    private void UpdateMeshRendererColor() {
        // Get the color from the model and set it on the mesh renderer.
        _meshRenderer.material.color = _model.color;
    }
}
  

This is pretty simple, when the method is called, it grabs the color on the model and updates the color on the mesh renderer. Now we could just call this from Update() and make sure our renderer matches the model every frame, but personally, I don’t like this approach. Among other issues, it’s susceptible to your Script Execution Order which can cause updates to be delayed by a frame. Instead, we can register for an event from the model that will fire whenever it’s changed locally or by a remote client. We’ll use that to call our UpdateMeshRendererColor color method:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColorSync : RealtimeComponent {

    private MeshRenderer   _meshRenderer;
    private ColorSyncModel _model;

    private void Start() {
        // Get a reference to the mesh renderer
        _meshRenderer = GetComponent<MeshRenderer>();
    }

    private ColorSyncModel model {
        set {
            if (_model != null) {
                // Unregister from events
                _model.colorDidChange -= ColorDidChange;
            }

            // Store the model
            _model = value;

            if (_model != null) {
                // Update the mesh render to match the new model
                UpdateMeshRendererColor();

                // Register for events so we'll know if the color changes later
                _model.colorDidChange += ColorDidChange;
            }
        }
    }

    private void ColorDidChange(ColorSyncModel model, Color value) {
        // Update the mesh renderer
        UpdateMeshRendererColor();
    }

    private void UpdateMeshRendererColor() {
        // Get the color from the model and set it on the mesh renderer.
        _meshRenderer.material.color = _model.color;
    }
}
  

Here I’ve made a method called ColorDidChange that fires when the color property on the model changes. I’ve also added some logic to the model setter. Whenever a new model is set, we update our mesh renderer. We also register for the colorDidChange event so if the color changes later, we’ll be notified so we can update our mesh renderer.

We have one final method to implement. Our ColorSync component will now properly keep the color of the game object in sync with the model, but we haven’t exposed a method to update the model itself. Let’s add a simple method that does that to our class:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColorSync : RealtimeComponent {

    private MeshRenderer   _meshRenderer;
    private ColorSyncModel _model;

    private void Start() {
        // Get a reference to the mesh renderer
        _meshRenderer = GetComponent<MeshRenderer>();
    }

    private ColorSyncModel model {
        set {
            if (_model != null) {
                // Unregister from events
                _model.colorDidChange -= ColorDidChange;
            }

            // Store the model
            _model = value;

            if (_model != null) {
                // Update the mesh render to match the new model
                UpdateMeshRendererColor();

                // Register for events so we'll know if the color changes later
                _model.colorDidChange += ColorDidChange;
            }
        }
    }

    private void ColorDidChange(ColorSyncModel model, Color value) {
        // Update the mesh renderer
        UpdateMeshRendererColor();
    }

    private void UpdateMeshRendererColor() {
        // Get the color from the model and set it on the mesh renderer.
        _meshRenderer.material.color = _model.color;
    }

    public void SetColor(Color color) {
        // Set the color on the model
        // This will fire the colorChanged event on the model, which will update the renderer for both the local player and all remote players.
        _model.color = color;
    }
}
  

That’s it! Let’s test our component out.

Open up a blank scene in Unity and add a game object with a Realtime component to it. Make sure to enter your App Key.

Now let’s make a Cube game object that we’ll use to test our new component. We can add a ColorSync component to it. Normcore will detect it, automatically add a RealtimeView component and then add the ColorSync component to the list of observed components. Finally, be sure to expand the “Advanced Settings” in the RealtimeView, and uncheck Owned By Creating Client. This will allow all connected clients to modify the cube.

Last, we’ll make a quick test component that changes the Cube’s color. This component will display a color picker in the inspector, and whenever the color changes, it will update the color using the ColorSync component:

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColorSyncTest : MonoBehaviour {
    [SerializeField]
    private Color _color;
    private Color _previousColor;

    private ColorSync _colorSync;

    private void Start() {
        // Get a reference to the color sync component
        _colorSync = GetComponent<ColorSync>();
    }

    private void Update() {
        // If the color has changed (via the inspector), call SetColor on the color sync component.
        if (_color != _previousColor) {
            _colorSync.SetColor(_color);
            _previousColor = _color;
        }
    }
}
  

Let’s take this new component for a test drive! Hit play and you should see your cube chillin on screen. Try changing the color property on the ColorSyncTest component. We can see here the cube color updates! Now let’s test our networking.

Export a standalone build and make sure your scene’s camera is pointed at the Cube game object.

Now we can open that standalone build, hit play in the editor, and change the cube color. Go ahead and try it. We’ll see the standalone build’s cube color update in real time.

Download a finished copy of the project here.

Check out our other guides on synchronizing custom data: