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 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:
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
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:
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:
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:
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.
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:
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
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:
First up, we’re going to write a method that synchronizes the color stored on the model to the mesh renderer:
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:
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:
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 CubeSync component to it. Normcore will detect it, automatically add a RealtimeView component and then add the CubeSync component to the list of observed components.
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:
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: