DarkRift 2 Tutorial for Unity 3D – Part 11 – Synchronize physics over network (1/2)

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Define data to be synchronized

For the tutorial, we only want to synchronize the ball position. As it’s a game object based on physics, the player must synchronize two informations :

  • Position of the ball
  • Velocity of the ball

So, let’s define a message model (has done previously for the spawn message). Create a new script called BouncyBallSyncMessageModel in the folder Scripts/Network/Messages

Here is the code of the class :

using DarkRift;
using UnityEngine;

public class BouncyBallSyncMessageModel : IDarkRiftSerializable
{
    #region Properties
    public int networkID;
    public int serverTick;
    public Vector3 position;
    public Vector3 velocity;
    #endregion

    #region IDarkRiftSerializable implementation
    public void Deserialize(DeserializeEvent e)
    {
        networkID = e.Reader.ReadInt32();
        serverTick = e.Reader.ReadInt32();
        position = new Vector3(e.Reader.ReadSingle(), e.Reader.ReadSingle(), e.Reader.ReadSingle());
        velocity = new Vector3(e.Reader.ReadSingle(), e.Reader.ReadSingle(), e.Reader.ReadSingle());
    }

    public void Serialize(SerializeEvent e)
    {
        //Write id of the network object
        e.Writer.Write(networkID);

        //Write id of the network object
        e.Writer.Write(serverTick);

        //Write position
        e.Writer.Write(position.x);
        e.Writer.Write(position.y);
        e.Writer.Write(position.z);

        //Write velocity
        e.Writer.Write(velocity.x);
        e.Writer.Write(velocity.y);
        e.Writer.Write(velocity.z);
    }
    #endregion
}

You can see a property called serverTick, it will be discussed later on this tutorial.

Create the associated tag

As you know, we need to create an associated tag wich will be used to identify the message. Take the previsouly created script called NetworkTag and the tag like that :

/// <summary>
/// Classes that stores all tags
/// </summary>
public static class NetworkTags
{
    /// <summary>
    /// Tags used in game
    /// </summary>
    public struct InGame
    {
        ///////////////////////////
        // Common
        public const ushort SPAWN_OBJECT = 1001;

        ///////////////////////////
        // Bouncy ball
        public const ushort BOUNCY_BALL_SYNC_POS = 2001;
    }

}

How synchronize the bouncy ball ?

Now, we need a script that will be shared by both server and client to synchronize the position. From the server side, it will just send the position to the client each “X” frame where “X” will be defined within the script.
On the client side, it will be more difficult because we want that the movement of the ball to be fluid. If we update the position of the ball each received message, the result will probably be not very fluid. Here are the cons of doing that :

  • Messages order are not guarranted with DarkRift2, means that we can receive message N°27 before the message N°22
  • The visual effect of updating the ball position on each received message won’t be acceptable for the player

So, we’ll need interpolation between client and server to fix that. We’ll talk about it later on this tutorial.

As the server is the master for the ball position and there is no input for controlling the ball, the server doesn’t need any interpolation, only the client.

How to get the number position of a message ?

Dark Rift 2, at my knowledge, doesn’t provide a way to get the position order of messages. So we need to create it entirely. With unity 3D, we have 2 functions wich are defined below :

  • Update() : called each frame, the elapsed time between each update call is not fixed and will be different on each machine. Depends too on what the game is processing, means that if there is a lot of game object that performs actions, the time between each call will be longer. that is what we call Frame Rate in game developpement or Frame Per Second (FPS)
  • FixedUpdate() : this function is frame-rate independant. It’s the frequency of the physic system in Unity 3D. by default, it’s called every 0.02 seconds (equivalent to 50 times per second).

By knowing that, we know that to set up our network frame counter, we’ll use the FixedUpdate function on both server and client side !

Create the server tick counter

The server tick is a simple counter wich increments by 1 every FixedUpdate(). It start to 0 on the server. On the client, we just store the last received counter on each game object wich needs it

It’s very simple to do this on the server. For the client, we’ll see it on the next article

GameServerManager :

/// <summary>
/// Last tick received from the server
/// </summary>
public int currentTick = -1;

...

void FixedUpdate()
{
    currentTick++;
}

NetworkBouncyBall controller

Create a folder Objects in the Network folder and create the following script :

and attach it to the BouncyBall in the main game scene :

As you can see, it’s not very elegant to have a NetworkObject and a NetworkBouncyBall. The best practice is to make the script NetworkBouncyBall inherit from the NetworkObject :

public class NetworkBouncyBall : NetworkObject

Now, remove the NetworkObject component from the BouncyBall and re-set the resource id to 1 (as it was before) :

That’s much better ! We need to do one thing in order the make it works fine. Declare the Start() function as virtual on the parent class (NetworkObject) and override it on the child class :

NetworkObject :

public virtual void Start()

NetworkBouncyBall :

#region Properties
public Rigidbody rigidbodyReference;
#endregion

// Start is called before the first frame update
public override void Start()
{
	base.Start();

	////////////////////////////////////
	// Get references
	rigidbodyReference = GetComponent<Rigidbody>();

}

I took the opportunity to add the reference right now to the rigidbody of the ball. We’ll need it to get the velocity and the position too when we’ll send the message.

Send the ball position to all clients

The message sending will occurs only on the server side. so for that, we’ll create a function to send the ball position to all clients within the script :

/// <summary>
/// Send ball server position to all clients
/// </summary>
private void SendBallPositionToClients()
{
	//Create the message
	BouncyBallSyncMessageModel bouncyBallPositionMessageData = new BouncyBallSyncMessageModel(
		base.id,
		GameServerManager.instance.currentTick,
		rigidbodyReference.transform.position,
		rigidbodyReference.velocity);

	//create the message 
	using (Message m = Message.Create(
		NetworkTags.InGame.BOUNCY_BALL_SYNC_POS,        //Tag
		bouncyBallPositionMessageData)                  //Data
	)
	{
		foreach (IClient client in GameServerManager.instance.serverReference.Server.ClientManager.GetAllClients())
		{
			client.SendMessage(m, SendMode.Reliable);
		}
	}
}

And call this function into the FixedUpdated() function. We need to test in wich side we are :

private void FixedUpdate()
{
	//If we are on server side
	if (!Equals(GameServerManager.instance, null))
	{
		if (GameServerManager.instance.currentTick % 10 == 0)
			SendBallPositionToClients();
	}
}

I’ve decided to send the ball position every 10 tick to not broadcast this message every frame and then reduce the necessary network bandwith. But by doing that, we’ll see that if we update the position on message received, the behavior of our synchronization will be weird.

What’s next

As you have noticed, we don’t have implemented the client synchronization. we’ll do it on the next and before last article ! Stay tuned.

Thanks for reading.