Unity 2D Tile Map Tutorial – destructible tiles

The unity 2D tile map system is a very efficient and powerful tool that enables you to quickly prototype and build quality games. Because it’s a relatively new system, developpers may don’t know how to deals with it. Probably beginners only use this system to buid maps but they forgot that this system can be « easely » customizable.

As this system is free and directly available into the Unity editor, its pertinent to invest time to build extra functionalities around this system.

In this tutorial, we’ll be focusing on how to make a destructible tile because this feature is commonly asked. I’ve seen a lot of topic dealing with it whitout giving a clear answer. So,You’ll find it there.

Prerequisites

This tutorial assume that you have a good knowledge of the basic unity tilemap system. If not, there is a lot of good tutorials, here is some links :

Download the project

You can work directly within the project right here : Github

Getting Started

As you can see, destructible tiles have a broken sprite wich is rendered when their life is under 50%. This properties are not available within the built-in system. We’ll need to create our customizable tile with these 2 properties.

Let’s start by creating a simple tile map like this. You can use my empty project. I’ll Assume that the palette is already prepared. If you need some assets, i higly recommands you to download this one

Create the map

Let’s make a simple map like below but if you want to make it more complex, you can.

Basic tile map for the tutorial

Now you have designed the map, you have to add colliders. We’ll use the dedicated tile map collider. In addition of it, we’ll use a composite collider wich will merge all single colliders into one.

Don’t forget to set Rigidbody to Kinematic

Create the player

Now, we have to create a player who’ll shoot some projectiles. Let’s just make a basic character with rigidbody and collider like that

Basic player

The scene is set up and we’ll write our first line of code. The 2 followings scripts are only for the player. That’s the shooter and the projectil itself.

Projectile.cs

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class Projectile : MonoBehaviour
{
    #region Properties

    private Rigidbody2D rigidbodyReference;
    private CircleCollider2D colliderReference;

    [SerializeField]
    private float velocity;
    [SerializeField]
    private LayerMask whatIsDestructible;

    private RaycastHit2D hit;
    private Vector3 originalVelocity;

    #endregion

    #region Implementation

    public void ShootTo(Vector2 pDirection)
    {
        rigidbodyReference.velocity = pDirection * velocity;
        originalVelocity = rigidbodyReference.velocity;
    }

    #endregion

    #region Unity callbacks

    public void Awake()
    {
        rigidbodyReference = GetComponent<Rigidbody2D>();
        colliderReference = GetComponent<CircleCollider2D>();
    }

    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag != "Player") {
            Destroy(this.gameObject);
        }
    }

    #endregion
}

ProjectileShooter.cs

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

public class ProjectileShooter : MonoBehaviour
{
    #region Properties

    public GameObject projectilePrefab;

    #endregion

    #region Implementation

    private Projectile InstantiateNewProjectile()
    {
        GameObject _newProjectile = Instantiate(projectilePrefab) as GameObject;
        _newProjectile.transform.position = transform.position;
        return _newProjectile.GetComponent<Projectile>();
    }

    public void ShootTo(Vector2 pDirection)
    {
        Projectile _newProjectile = InstantiateNewProjectile();
        _newProjectile.ShootTo(pDirection);
    }

    #endregion

    #region Unity callbacks

    public void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 worldMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            ShootTo((worldMousePosition - (Vector2)transform.position).normalized);
        }
    }

    #endregion

}

Now script are ready, let’s create a prefab for the projectile like this

Don’t forget to set Gravity scale to 0

Let’s add the projectil shooter to the player and try it

Don’t forget to set tag to “Player”

Create the destructible Tile

Tiles are scriptable object that can be customized, for that, we’ll need to inherits from Tile class base. We’ll just override the StartUp() function to initialize some additional data :

  • tilePosition : Position of the tile in the grid
  • startLife : Life a the creation of the tile

We’ll create a specific function which will be called when the tile receive damage. It will be in charge of changing the sprite or removing it if the tile has been destructed.

Create the scriptable tile

Let’s create a function in the script to be able create a scriptable tile in the editor.

using UnityEditor;
using UnityEngine;
using UnityEngine.Tilemaps;

public class DestructibleTile : Tile
{
    #region Properties

    [Space(20)]
    [Header("Destructible Tile")]
    /// <summary>
    /// Life remaining before destroy tile
    /// </summary>
    public float life;
    private float StartLife;

    /// <summary>
    /// Sprite to display when life is below 50%
    /// </summary>
    public Sprite brokenSprite;

    public ITilemap tileMap;
    public Vector3Int tilePosition;

    #endregion

    #region Tile Overriding

    public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    {
        StartLife = life;

        //Store some data
        this.tileMap = tilemap;
        this.tilePosition = position;

        return base.StartUp(position, tilemap, go);
    }
    #endregion

    #region Implementation

    /// <summary>
    /// Apply damage to the tile
    /// </summary>
    /// <param name="pDamage"></param>
    public void ApplyDamage(float pDamage)
    {
        life -= pDamage;
        if (life < StartLife / 2 && base.sprite != brokenSprite)
        {
            base.sprite = brokenSprite;
        }
        if (life < 0)
        {
            base.sprite = null;
        }
    }
    #endregion

    #region Asset DataBase

    [MenuItem("Assets/MateriaGame/DestructibleTile")]
    public static void CreateDestructibleTile()
    {
        string path = EditorUtility.SaveFilePanelInProject("Save Destructible Tile", "DestructibleTile_", "Asset", "Save Destructible Tile", "Assets");

        if (path == "")
            return;

        AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<DestructibleTile>(), path);
    }
    #endregion
}

Go now in the editor, and create a new instance of a destructible tile. Fill all information. For the destructible environnement, i recommend to create a new palette wich will host all destructible tiles. Once it’s done, place some destructible tile into the scene :

Collide with the tile map

As said earlier in the tutorial, the collider is attached to the tilemap and not to the tile, so we need a script that will handle collisions. Here is the code of the script.

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

public class DestructibleTileMap : MonoBehaviour
{
    #region Properties
    private Tilemap tileMap;
    private GridLayout grid;
    private Vector3Int tilePosition;
    #endregion

    #region Unity callbacks
    public void Start()
    {
        tileMap = GetComponent<Tilemap>();
        grid = GetComponentInParent<GridLayout>();
    }
    #endregion

    public void Damage(Projectile projectile, Vector3 pContactPoint)
    {
        TileBase tileToDamage = tileMap.GetTile(grid.WorldToCell(pContactPoint));
        if (!Equals(tileToDamage, null))
        {
            if (tileToDamage is DestructibleTile)
            {
                ((DestructibleTile)tileToDamage).ApplyDamage(10);
                tileMap.RefreshTile(((DestructibleTile)tileToDamage).tilePosition);
            } 
        }
    } 
}

Get tile coordinate on collision

You can see that we can get a tile using a world position. This feature is natively provided by the unity tilemap built-in system. That’s by this way that we retrieve the tile on wich we’ll apply damage and this is this tile that we’ll refresh.

So, add this component to the tile map, and create a new layer called “Destructible” Assign it to the tile map

Send the position on the tilemap

It may be the most difficult part of this tutorial. The collider is not attached to the tile, it’s attached to the tilemap. When you hit the tile map collider, it actually doesn’t tell you wich tile you have hitten. You need to calculate the tile position when collision occurs.

This is how projectile will hit the tile map, we need to get the position (0,0)

So, how to determine which tile has been hitten. We’ll use the normal of the hitten vertice.

Normal and vertice explanation

Now we know that, we easely can get the tile by inversing the normal to get the tile, but there is something i didn’t mentionned about.

The difficulty is that when we use a trigger, we don’t have the collision point. Of course, you can use collider without trigger, like that, you can have simply the contact point. On this tutorial, we assume that our game is in the most unfavorable position, we have to use Trigger.

For this,  we’ll cast a circle when OnTriggerEnter2D is called by the unity engine. So, we’ll be able to get the contact point.

Modify the projectile to get the collision point

Update the projectile.cs script as below

    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag != "Player")
        {
            hit = Physics2D.CircleCast(transform.position, colliderReference.radius, rigidbodyReference.velocity.normalized, 0.1f, whatIsDestructible);
            if (hit)
            {
                DestructibleTileMap damageable = hit.collider.gameObject.GetComponent<DestructibleTileMap>();
                if (!Equals(damageable, null))
                {
                    Vector2 directionHit = (hit.point - (Vector2)transform.position).normalized;
                    damageable.Damage(this, hit.point + (directionHit.normalized * 0.5f));
                }                
            }
            Destroy(this.gameObject);
        }
    }

Update the prefab of the projectile to select the destructible layer

If you tested the game right now, be aware when you hit a destructible tile, you are modifying directly the scriptable tile. It means that all modifications will be persistent !

Create a copy of scriptable tiles

As you may know, Scriptable Objects in unity, when is modified apply its modification to all reference to this scriptable object and keep them persistent. In addition of placing destructible tile into the map, we need to create an instance, wich will be a copy of this tile when the map is created. By this way, modification will be applied to the single instance, not to the entire destructible tiles.

We need to update the DestructibleTile component as following

public void Start()
{
    tileMap = GetComponent<Tilemap>();
    grid = GetComponentInParent<GridLayout>();

    foreach (Vector3Int position in tileMap.cellBounds.allPositionsWithin)
    {
        TileBase t = tileMap.GetTile(position);
        if (!Equals(t, null))
        {
            if (t is DestructibleTile)
            {
                DestructibleTile dt = Instantiate(t) as DestructibleTile;
                dt.StartUp(position, dt.tileMap, dt.gameObject);
                tileMap.SetTile(position, dt);
            }
        }
    }
}

That’s all. You can launch the game right now and notice that all goes well ! We have a destructible tilemap totally functional

To conclude, the unity 2D tile map system is very powerful but it’s a little complicated to find complete example. That’s is why i write this article. I hope you find what you were looking for, and that’s will improve your game.

If you liked it, please share this article and follow me on Twitter !
Thanks you for reading, feel free to ask me questions.

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

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

Prepare the bouncy ball to be synchronized

On the script NetworkBouncyBall, we want to receive the ball position from the server. So we need to listen to the event “MessageReceived” from the client manager. As said in the previous article, messages order is not guaranteed, thus we need to store the last received message from the server wich contains the serverTick (it’s incremented each FixedUpdate()).

We also need the clientTick wich is the counter that is incremented by the client each FixedUpdate in the NetworkBouncyBall Script. This counter will help us to do reconciliation.

The reconciliation is the process wich will correct the information of the client compared to those of the server. As the client send the data each 10 ticks, we’ll need a list of all client position each fixed update.

Here are the new properties we need in the NetworkBouncyBall script :

/// <summary>
/// Last received message from the server
/// </summary>
public BouncyBallSyncMessageModel lastReceivedMessage;

/// <summary>
/// Tick counted by the client
/// </summary>
public int clientTick = -1;

/// <summary>
/// Contains data wich will be generated by the client
/// </summary>
public List<BouncyBallSyncMessageModel> reconciliationInfoList;

Listen for the message receive events

As said, the client need to receive messages from the server, As you already did it in the past articles, i just will give you the code :

public override void Start()
{
	base.Start();

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

	//If we are on client side
	if (!Equals(ClientManager.instance, null))
	{
		//////////////////
		/// Suscribe to events
		ClientManager.instance.clientReference.MessageReceived += UpdateFromServerState;
		reconciliationInfoList = new List<BouncyBallSyncMessageModel>();
	}
}

private void OnDestroy()
{
	//If we are on client side
	if (!Equals(ClientManager.instance, null))
	{
		ClientManager.instance.clientReference.MessageReceived -= UpdateFromServerState;
	}
}

You can notice here that there is 2 interesting things :

  • Initialization of the reconciliation data : reconciliationInfoList. (Be sure to use the namespace using System.Collections.Generic)
  • Unsuscribe to the event on the OnDestroy function. It’s a good practice to always to that.

Now, we finally can create our function UpdateFromServerState :

/// <summary>
/// update from the server state
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UpdateFromServerState(object sender, DarkRift.Client.MessageReceivedEventArgs e)
{
	if (e.Tag == NetworkTags.InGame.BOUNCY_BALL_SYNC_POS)
	{
		//Get message data
		BouncyBallSyncMessageModel syncMessage = e.GetMessage().Deserialize<BouncyBallSyncMessageModel>();

		//If this is the first time we receive the message
		if (Object.Equals(null, lastReceivedMessage))
		{
			//Update data
			rigidbodyReference.velocity = syncMessage.velocity;
			rigidbodyReference.transform.position = syncMessage.position;
			clientTick = syncMessage.serverTick;
			lastReceivedMessage = syncMessage;
		}

		//If the message regards this object and is older than the previous one
		if (id == syncMessage.networkID && syncMessage.serverTick > lastReceivedMessage.serverTick)
		{
			lastReceivedMessage = syncMessage;
		}
	}
}

This needs some explanations.

First time we receive the message

When it’s the first time we receive this message, we need to initialize some data as :

  • Client tick : this is the counter of the ball in the client side. When we receive the first message from the server, we set the same value than the server. It will be used to make reconciliation.
NOTE : using the fixed update function to increment the counter is not the safest way. I heard that sometimes, Unity can skip some fixed update. That why if you plan to do a multiplayer game, you need a strongest way to handle counters.
  • Synchronize immediately the velocity and the position.
  • Store the message in the last received message

Receive following messages

For the following messages, we only need to store the message received in the property lastMessagereceived.

Reconciliation

At this point, we only synchronize the position and the velocity of the ball from the first received message .

As it’s a game object affected by physics, we need to do correction, if needed, in the FixedUpdate() function.

In this function we’ll perform these following actions :

  • Increment the client tick counter
  • Add data to the reconcialiation info list.
  • Performs reconciliation

Here is the code :

private void FixedUpdate()
{
	//If we are on server side
	if (!Equals(GameServerManager.instance, null))
	{
		if (GameServerManager.instance.currentTick % 1 == 0)
			SendBallPositionToClients();
	}
	else if (!Equals(ClientManager.instance, null) && clientTick != -1)
	{
		clientTick++;
		reconciliationInfoList.Add(new BouncyBallSyncMessageModel
		{
			position = transform.position,
			serverTick = clientTick,
			velocity = rigidbodyReference.velocity
		});

		Reconciliate();
	}
}

How it will works ?

As the physic engine will works both on client and server, we need to check frequently if the divergence between client and server is not to important.

When we receive the message from the server, we need to check the position of the ball at this specific tick on the client. For that, we’ll use the reconciliationInfoList to take the snaphot of the ball at this tick (wich is the server tick received).

If the difference is above a certain threshold, we’ll correct basically the position and the velocity of the client immediately.

/// <summary>
/// Reconciliate the client with the server data
/// </summary>
private void Reconciliate()
{
	if (reconciliationInfoList.Count() > 0)
	{
		//Get the position of the client at this specific frame
		BouncyBallSyncMessageModel clientInfo = reconciliationInfoList.Where(i => i.serverTick == lastReceivedMessage.serverTick).FirstOrDefault();

		//If there is more than 50 tick that the ball has not been updated depending to the server position
		if (reconciliationInfoList.Count() > 50)
		{
			rigidbodyReference.velocity = lastReceivedMessage.velocity;
			rigidbodyReference.transform.position = lastReceivedMessage.position;
			clientTick = lastReceivedMessage.serverTick;
			clientInfo = lastReceivedMessage;
		}                

		if (!Equals(clientInfo, null))
		{
			//Check for position divergence
			if (Vector3.Distance(clientInfo.position, lastReceivedMessage.position) >= 0.05f)
			{
				//Update data
				rigidbodyReference.velocity = lastReceivedMessage.velocity;
				rigidbodyReference.transform.position = lastReceivedMessage.position;
			}

			//Empty the list
			reconciliationInfoList.Clear();
		}            
	}
}

If a reconcialiation is made, we empty the list to free memory.

NOTE : In this tutorial, i clear the list only when a reconcialition occurs. In your game, i suggest you to limit the length of the list to not consumme  
a large amount of memory, especially if there is no often reconciliation

Let’s try the result

At this point of the tutorial, we receive the ball position each 10 tick. (as defined in the previous article). The ball only bounce on the Y axis. Let’s try it. I will
will launch the server scene on Unity then build my client an launch it.

To build the client executable, you need to select both MainClientScene and MainGameScene like that (The order is important). Unselect Server Build if already select :
NOTE : I added two cubes to make the ball bounce randomly each start

Here is the result :

Seems to be good ! we finally synchronized our physic game object.

As the position of the ball is lead by the physic engine, it’s a little complicated to make interpolation. We definitively can do that to make the reconciliation smoother.

Improvement ideas

There is a lot to do to increase the playability, the smoothness, … Here is some tips that you can use :

  • Increase the rate of messages sending by the server
  • Use math to predict the ball position to make the ball move smoother on the client screen when reconciliate
  • Force reconcialition each X tick should be adapted to each client connection

Thanks for reading !

Thanks for reading this last article about how to synchronize a physic object bewteen a server and a client with Dark Rift 2. If you liked it, you can thanks me by :

  • Following me on twitter
  • Give me you feedback

See you soon for another tutorial or a Unity 3D tips
thanks you, David.

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.

DarkRift 2 Tutorial for Unity 3D – Part 10 – Get spawn messages from server

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

Tell the client to listen messages

First thing to perform, is to update our client to tell him to listen when a message is arrived. We will tell him to connect in background instead of auto-connect (on the inspector). Why ? Just to show you that we can connect easely with code. There is two way of connecting :

  • Client.Connect() : Connect to the server, but keep “freezed” until the connection is made. It must be used when a connection to the server is mandatory or when you need to be sure that the connection is made.
  • Client.ConnectInBackground() : Performs the connection asynchronously. Means that the game won’t freeze, even if the connection is not made.

Disable the auto connect in the client :

So, let’s update the ClientManager script with this code :

void Start()
{        
    //////////////////
    /// Load the game scene
    SceneManager.LoadScene("MainGameScene", LoadSceneMode.Additive);

    //////////////////
    /// Suscribe to events
    clientReference.MessageReceived += SpawnGameObjects;

    //////////////////
    /// Connect to the server manually
    clientReference.ConnectInBackground(
        IPAddress.Parse("127.0.0.1"),
        4296,
        DarkRift.IPVersion.IPv4,
        null
        );

}

I’ve put the server adress without variables, but in your game, you should use another way. That’s only for the example.
As you can see, we’ve added a listener on message received called SpawnGameObjects. Let’s now create this function !

Create the spawn function

ok, what wee need to do ? i will sum up this function feature :

  • Check if the received message is tagged as SPAWN_OBJECT (see Part 9)
  • Load the specified game object
  • Instantiate him at the specified position

And that’s all. Are you ready ? … but wait a minute. How can i know what is the desired ressource ? Yes, you’re right, at this point we can’t know. We need to create our NetworkDictionnary !

Network Game Object Dictionnary

The dictionnary will be useful to determines wich resource is focus for a specified ID. He will looks like this :

IDResourcePath
0“BouncyBall”
1“MyFolder/AnotherGameObjectToSpawn”
2“OnMoreGameObjectToSpawn”

As said before, for loading object, we’ll use the resource folder wich is a special folder within unity3D (see this for more information). I don’t encourage to use this in a production environnement ! But for prototypes, it’s ok.

Let’s create a new script called NetworkObjectDictionnary in the Scripts/Network folder :

Here is the code :

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

public static class NetworkObjectDictionnary
{
    /// <summary>
    /// Dictionnary that contains all gameobjects spawnable
    /// </summary>
    private static readonly Dictionary<int, string> dictionnary = new Dictionary<int, string>
    {
        {1, "BouncyBall" } 
    };

    /// <summary>
    /// Returns the specified object name 
    /// </summary>
    /// <param name="pID"></param>
    /// <returns></returns>
    public static string GetResourcePathFor(int pID)
    {
        string objectName;
        dictionnary.TryGetValue(pID, out objectName);
        return objectName;
    }
}

The dictionnary cannot be modified during run time, that’s why we specify the readonly keyword. We create a function to get the resource path for a specific if (GetResourcePathFor)

Doing it with a function enable us to modify the way how the path is retrieved whitout modifyng everywhere the dictionnary is used.

Finally, just need to create the Resources folder and add our BouncyBall as a prefab (you will need to open the MainGameScene) :

Perfect ! we propably may now writing our SpawnFunction ? not yet, because, if you take a look on the message receveid, we only have 2 informations :

  • NetworkID : wich is the network identifier
  • Position : Position where the object has to be spwaned

We are missing one information : the ressourceID wich is defined in our dictionnary. So, i think you guessed, we need to update our message model and the way we send the message.

Update the server message

Let’s firstly update our message spawn model wich contains the data structure of the message. The script to update is SpawnMessageModel :

We just will add a new property called RessourceID :

...

/// <summary>
/// Resource to spawn
/// </summary>
public int resourceID { get; set; }

#region DarkRift IDarkRiftSerializable implementation
public void Deserialize(DeserializeEvent e)
{
	networkID = e.Reader.ReadInt32();
	resourceID = e.Reader.ReadInt32();
	x = e.Reader.ReadSingle();
	y = e.Reader.ReadSingle();
}

public void Serialize(SerializeEvent e)
{
	e.Writer.Write(networkID);
	e.Writer.Write(resourceID);
	e.Writer.Write(x);
	e.Writer.Write(y);
} 
#endregion

Now, we need to add this property into the NetworkObject Script. This property will be manually filled within the inspector :

/// <summary>
/// Resource identifier for the spawner
/// </summary>
public int resourceId;

#region Unity Callbacks

private void Start()
{
	//If we are not on the server and id is not set, destroy the gameobject
	if (Equals(GameServerManager.instance, null) && id == 0)
	{
		Destroy(gameObject);
	}
	else if(!Equals(GameServerManager.instance, null))
	{
		// Get the instance id of the gameobject on the server scene
		id = GetInstanceID();
		//Register with the server
		GameServerManager.instance.RegisterNetworkObject(this);
		//Test for resource ID
		if (resourceId == 0)
			throw new System.Exception(string.Format("There is no resource id for {0} gameobject", name));
	}
}
#endregion

Just look on the Start() function, i added a check to inform us if we forget to set the resourceId for a NetworkObject. So let’s do this now. Open the prefab and set the resourceID to 1 :

And now, we’ll simply modifiy the SendObjectToSpawnTo function within the GameServerManager script :

/// <summary>
/// Send a message to the client to spawn an object into its scene
/// </summary>
/// <param name="pClient"></param>
public void SendObjectToSpawnTo(NetworkObject pNetworkObject, IClient pClient)
{
    //Spawn data to send
    SpawnMessageModel spawnMessageData = new SpawnMessageModel
    {
        networkID = pNetworkObject.id,
        resourceID = pNetworkObject.resourceId,
        x = pNetworkObject.gameObject.transform.position.x,
        y = pNetworkObject.gameObject.transform.position.y
    };

    //create the message 
    using (Message m = Message.Create(
        NetworkTags.InGame.SPAWN_OBJECT,                //Tag
        spawnMessageData)                               //Data
    ){
        //Send the message in TCP mode (Reliable)
        pClient.SendMessage(m, SendMode.Reliable);
    }
}

The spawn function (finally)

Here we are, we now can write our function within the ClientManager script. There is nothing complicated with this and the code speaks by itself :

/// <summary>
/// Spawn object if message received is tagged as SPAWN_OBJECT
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SpawnGameObjects(object sender, MessageReceivedEventArgs e)
{
	if (e.Tag == NetworkTags.InGame.SPAWN_OBJECT)
	{
		//Get message data
		SpawnMessageModel spawnMessage = e.GetMessage().Deserialize<SpawnMessageModel>();

		//Spawn the game object
		string resourcePath = NetworkObjectDictionnary.GetResourcePathFor(spawnMessage.resourceID);
		GameObject go = Resources.Load(resourcePath) as GameObject;
		go.GetComponent<NetworkObject>().id = spawnMessage.networkID;
		Instantiate(go, new Vector3(spawnMessage.x, spawnMessage.y, 0), Quaternion.identity);
	}
}

Build server and try the client

Let’s try our implementation. Here is how you need to process :

  1. Build the server by selecting these 2 scenes : MainServerScene and MainGameScene
  2. Launch the exe previously builded
  3. Open your client scene in unity : MainClientScene and start the game.

If you need more information about how to build, see the part

You should see the ball appears in the scene ! That’s cool but the position of the ball should be random because as the ball bounce in the server scene, the spawn message will send the current position of the ball. That’s why when you lauch the game several time, the ball is not spawned on the same position !

What’s next ?

On the next article, we’ll now synchronize the position of the ball with the server position. Exciting, isn’t it ?

Thanks for reading.

DarkRift 2 Tutorial for Unity 3D – Part 9 – Send spawn messages to clients

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

Create the serializable class

As seen in the last article, we’ll use serializable class to send message. We need two information.

  • The network ID
  • The object spawn position

In fact, we need a third information wich contains what object it is but it will be discussed in dedicated article.
Create a new folder into Scripts/Network and name it SpawnMessageModel :

Here is the code :

using DarkRift;

public class SpawnMessageModel : IDarkRiftSerializable
{

    #region Properties
    /// <summary>
    /// Id of the object in the server
    /// </summary>
    public int networkID { get; set; }

    /// <summary>
    /// X position
    /// </summary>
    public float x { get; set; }

    /// <summary>
    /// Y position
    /// </summary>
    public float y { get; set; }
    #endregion


    #region DarkRift IDarkRiftSerializable implementation
    public void Deserialize(DeserializeEvent e)
    {
        networkID = e.Reader.ReadInt32();
        x = e.Reader.ReadSingle();
        y = e.Reader.ReadSingle();
    }

    public void Serialize(SerializeEvent e)
    {
        e.Writer.Write(networkID);
        e.Writer.Write(x);
        e.Writer.Write(y);
    } 
    #endregion
}

You don’t need any reference to unity namespace. This class is elementary. It’s the representation of a spawn message.

Create a tag dictionnary

As you know each message is tagged to give a general meaning of what it concerns. Thus, for that, i decided to create a specific class that will contains all code tags.
Create a new Script into the folder previously created and name it NetworkTags :

For now, we just need one tag, but it’s good to be organized in order to add some tags easely. I use a classe and structs to build this “dictionnary”. Here is the implementation :

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

}

Update the GameServerManager

OK, all it’s ready to create our messages and send them trough the network, but, what we exactly need to send ? There is two cases :

  1. When a client connects, send a spawn message for all network objects
  2. When a new object appears, send a spawn message for all clients.

In the first case, in a production environnement, this step is made during the loading phase. The second case is when the game runs.

First step is to create a property in the GameServerManager that will keep in memory all network objects :

/// <summary>
/// List of objects handled by the server
/// </summary>
public List<NetworkObject> networkObjects;

Don’t forget to initialize it into the Start() unity callback :

private void Start()
{
	...
    networkObjects = new List<NetworkObject>();

Then, let’s create the two functions to send our messages plus on function that will be called by all network objects in the scene : RegisterNetworkObject

#region Implementation

    /// <summary>
    /// Use this function to add a network object that must be handle by the server
    /// </summary>
    /// <param name="pNetworkObject"></param>
    public void RegisterNetworkObject(NetworkObject pNetworkObject)
    {
        //Add the object to the list
        networkObjects.Add(pNetworkObject);
    }

    /// <summary>
    /// Send a message to the client to spawn an object into its scene
    /// </summary>
    /// <param name="pClient"></param>
    public void SendObjectToSpawnTo(NetworkObject pNetworkObject, IClient pClient)
    {
        //Spawn data to send
        SpawnMessageModel spawnMessageData = new SpawnMessageModel
        {
            networkID = pNetworkObject.id,
            x = pNetworkObject.gameObject.transform.position.x,
            y = pNetworkObject.gameObject.transform.position.y
        };

        //create the message 
        using (Message m = Message.Create(
            NetworkTags.InGame.SPAWN_OBJECT,                //Tag
            spawnMessageData)                               //Data
        ){
            //Send the message in TCP mode (Reliable)
            pClient.SendMessage(m, SendMode.Reliable);
        }
    }

    /// <summary>
    /// Send a message with all objects to spawn
    /// </summary>
    /// <param name="pClient"></param>
    public void SendAllObjectsToSpawnTo(IClient pClient)
    {
        foreach (NetworkObject networkObject in networkObjects)
            SendObjectToSpawnTo(networkObject, pClient);
    }

    #endregion

Finally, we just need to call the function SendAllObjectsToSpawnTo() when a client connects. For that, we already have a listener, so just modify the callback as below :

/// <summary>
    /// When a client connects to the DarkRift server
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ClientConnected(object sender, ClientConnectedEventArgs e)
    {
        clientsId.Add(e.Client.ID);

        //Send all objects to spawn
        SendAllObjectsToSpawnTo(e.Client);
    }

Here we are ! the server will send all necessary spawn message when a client connects ! but there is something missing to make it working ! we need to register each network object to the server.

In the NetworkObject script, just modify the Start() function to have this :

private void Start()
{
    //If we are not on the server, destroy the gameobject
    if (Equals(GameServerManager.instance, null))
    {
        Destroy(gameObject);
    }
    else
    {
        // Get the instance id of the gameobject on the server scene
        id = GetInstanceID();
        //Register with the server
        GameServerManager.instance.RegisterNetworkObject(this);
    }
}
#endregion

In fact we already have written this, you just need to uncomment the line where we call the registration method.

Now, you can try it. Run the MainServerScene and take a look to the GameServerManager into the inspector. You should see in the networkObjects list that there is one entry. It’s our ball !

What’s next ?

In the next article, we will receive the message within the client. We’ll need to create a new component name SpawnManager that will be in charge to instantiate the right game object depending to the message received !

Thanks for reading, don’t hesitate to give me your feedbacks !

DarkRift 2 Tutorial for Unity 3D – Part 8 – DarkRift 2 Messages – Concepts

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

What is a DarkRift2 message ?

Each communication between a client and a server is message-based. That’s the only way to send and receive information from a client or the server. Messages are used for example to :

  • Spawn objects into the client scene, when another players connects for exemple
  • Send network objects positions
  • Send profil information (player rank, connected friends, …)

All message are send by TCP or UDP. Depending of the type, messages is built like that :

  • A : Header Message : This part is uncompressible. The size taken by this one cannot be reduced. In DarkRift2, the size has been incredibly optimized
    • DarkRift1 : 13 bytes
    • DarkRift2 : 3 bytes with UDP, 7 bytes with TCP composed by :
      • TCP additional header : 4 bytes wich are necessary for TCP communication only.
      • Flag : 1 byte : for additional information like bit numbering or future features like server-server communication or ping I think (Thx @Allmaron)
      • Tag : ushort (2 bytes) :
        What the message is about (Spawn, Position, login info, …)
  • B : Message content data : Here is the information you want to send :
    size depends on sended data

Send DarkRift2 messages

There is 2 classes (in DarkRift namespace) that we need to create and send the message :

  • DarkRift.Writer : This class will build the content message.
  • DarkRift.Message : This is the complete message with the 3 parts described above.

Here is its usage :

using (DarkRiftWriter messageDataWriter = DarkRiftWriter.Create())
{
	//Build the message data
	messageDataWriter.Write("MyDataToSend");
	messageDataWriter.Write("AnotherDataToSend");
	messageDataWriter.Write(154);

	using (Message myMessage = Message.Create(MESSAGE_TAG, playerWriter))
	{
		//Send a message to a client
		ClientToSendMessage.SendMessage(myMessage, SendMode.Reliable);
	}
		
}

If you need more information about Using(), you can find what you need here. To sum up, using this will destroy automatically the object instantiated into the Using(), wich free the memory of this object

You can see the MESSAGE_TAG parameter. It describes what the message is about. It will be useful when you need to read the message, in order to know what this message concerns

Receive DarkRift2 messages

When a client receive a message, it throws an event MessageReceived that must be used in order to read it. It’s the only way to read the message.

For this, you will need 2 classes (in DarkRift namespace) wich are :

  • Reader : This class will get data inside the message. You can read data one by one or can use serializable classes (wich are described below)
  • Message : This is the same classe used for the sending.

So, when you instantiate your client, you need to listen to this message :

client.MessageReceived += MyFunctionToCall;

Once it’s done, you need to create the function MyFunctionToCall with these parameters wich are mandatory to be compliant with the suscribtion of the event

void MyFunctionToCall(object sender, MessageReceivedEventArgs e)
{
    //Get the received message
    using (Message message = e.GetMessage())
    //Use the DarkRift2 reader
    using (DarkRiftReader reader = message.GetReader())
    {
        //Get the tag
        int tag = message.Tag;

        //read message content
        string firstLine = reader.ReadString();
        string secondLine = reader.ReadString();
        int firstInt = reader.ReadInt32();

        if(tag == 1)
            //Do something with your data
        else if (tag == 2)
            //Do another thing with your data
    }
}

The code is pretty simple to understand but there is something that perturbs me. the way of getting back data from the message. As you can see, you have to know exactly how the data are organized in the message. Of course, you know that because it’s probably you that have written the message. but it’s very inappropriate in a big network game with a lot of messages.

There is another way to send and read data from DarkRift2 messages. that’s serializable classes. DarkRift2 provides an interface that allows you to build message structure independantly from the message. Like that you can modify easly a message content whitout rewritting everywhere it’s used.

How to use IDarkRiftSerializable 

In order to use classe to send message, you need to create a classe that implements this interface : IDarkRiftSerializable .
Here is an example :

public class MyMessageContent : IDarkRiftSerializable
{
    //Data to send within the message
    public string myName;
    public int myAge;

    //Constructor (can be empty)
    public MyMessageContent()
    {    }

    #region Interface implementation IDarkRiftSerializable
    //Used by the DarkRift reader
    public void Deserialize(DeserializeEvent e)
    {
        myName = e.Reader.ReadString();
        myAge = e.Reader.ReadInt32();
    }

    //Used by the DarkRift writer
    public void Serialize(SerializeEvent e)
    {
        e.Writer.Write(myName);
        e.Writer.Write(myAge);
    }
    #endregion
}

Sending message with serializable

using (DarkRiftWriter messageDataWriter = DarkRiftWriter.Create())
{
    //Build the message data
    MyMessageContent content = new MyMessageContent
    {
        myName = "TheUncas",
        myAge = 30
    };

    using (Message myMessage = Message.Create(MESSAGE_TAG, content))
    {
        //Send a message to a client
        ClientToSendMessage.SendMessage(myMessage, SendMode.Reliable);
    }
}

We just need to create the object, fill properties with some data and then pass the object to the message.

Receiving message with serializable

using (DarkRiftReader messageReader = message.GetReader())
{
    //...
    MyMessageContent content = reader.ReadSerializable<MyMessageContent>();
    //...
}

No way, reading message like that is more elegant and maintenable. You can fin more information about DarkRift2 messages here

What’s next ?

In the next article, we will write the spawn message to all connected clients and read it in the client.

Stay informed by all my article by following me on twitter here

DarkRift 2 Tutorial for Unity 3D – Part 7 – How about synchronized objects

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

Concept

When you’re working in a Server/Client environment, there is some problems that doesn’t exists in a solo game. For exemple, the synchronisation of gameobjects.

One diffuculty is to keep a track of each network gameobjects and synchronize them. You get it ? It means that all synchronized game object on the network must have an Unique ID. This unique ID must be the same both on server and on all connected clients

Here is the way we’ll proceed :

As you can see, we need to identify on the scene wich game object need to be synchronized. After that, when the scene is loaded, we have to check on wich side we are executing the code (Server or Client side) in order to generate an unique network ID or to destroy the gameobject itself.

We’ll first focus on identifying where the game is executed.

Where game is executed ?

There is many ways of doing that. I choosed to use the Singleton pattern to accomplish this task. How it will works ?

Both ServerManager and ClientManager will use the singleton pattern. It means that we can access at any time (except on Awake()) to the ServerManager instance by writing :

GameServerManager.instance; //Return the unique instance

Download my utilities on GitHub

I provide you some utilities on my official github repository that you can download right now here : https://github.com/TheUncas/unity3D-tools

Download it as .ZIP and copy/paste the content on the scripts folder on the unity project. You should have this : (The content may change over time…)

We’ll use 2 classes wich are :

  • MonoBehaviourSingletonNonPersistent : Singleton Pattern that doesn’t persists after scene is destroyed
  • MonoBehaviourSingletonPersistent : Singleton Pattern that persits after scene is destroyed. It means that the game object will be not destroyed.

In our case, we want to keep an instance always alive, so we’ll use the Persistent Singleton. Let’s implement it on both GameServerManager. Modify the class declaration like that :

using Utilities;

public class GameServerManager : MonoBehaviourSingletonPersistent<GameServerManager>

Do the same for the ClientManager class.

Generate a unique network ID

For each object that needs to e synchronized over network, we want a unique network ID, so… let’s create a specific script that will perform it. Let’s call him NetworkObject in the Network folder :

We want from this component that it :

  • Generates an unique ID if we are on the server and register the object into a the ServerManager gameobject (To be created)
  • Destroys immediately the gameobject if we are on a client.

So simple as it is. Here is the code :

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

public class NetworkObject : MonoBehaviour
{
    #region Properties

    /// <summary>
    /// Id of the network object
    /// </summary>
    [HideInInspector]
    public int id;

    #endregion

    #region Unity Callbacks

    private void Start()
    {
        //If we are not on the server and id is not set, destroy the gameobject
        if (Equals(GameServerManager.instance, null) && id == 0)
        {
            Destroy(gameObject);
        }
        else if(!Equals(GameServerManager.instance, null))
        {
            // Get the instance id of the gameobject on the server scene
            id = GetInstanceID();
            //Register with the server
            //GameServerManager.instance.RegisterNetworkObject(this);
        }
    }

    #endregion
}

As you can see, i commented the line with the RegisterNetworkObject() because this method doesn’t exists yet on the ServerManager script. We’ll create it on the next article.

So now, let’s assign this script to the ball on the MainGameScene and launch both the client and both the server in unity :

That’s perfect. We are now ready to tell the server to send a message to the client to tell him to spawn a ball into its own scene. Of course, we’ll need to instantiate gameobject on the fly. There is 2 way to do that :

  • Using the Resource folder, wich is not an option if we decide to put in production our produce
  • Using Asset Bundles, wich is the most efficient and elegant way of instanciate objects

For our purpose, we’ll use the resources folder because it’s totally appropriate to fastly prototype a game. If you decide to make a production version of your game, you should consider to move to AssetBundles.

What about object position synchronization ?

Regarding the position of synchronized objects, we will treat this subject later, we first need to instantiate these objects on each connected clients.

What’s next ?

On the next article of this tutorial, we’ll keep a track of each network gameobject in the server scene and create our message to send to the client when he connects.

DarkRift 2 Tutorial for Unity 3D – Part 6 – Create the client

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

This article deals with the client implementation within Unity 3D. Here are the steps we’ll accomplish :

  • Create the client scene
  • Create the DarkRift2 client game object
  • Load the main game scene
  • Try to connect to the server (built on the last article)

Create the client scene

Open the project and create a new scene called “MainClientScene“. As for the server, we can delete the camera because the camera is contained on the MainGameScene.

Create a new GameObject “ClientManager” in the hierarchy :

Add a new component Client wich is the official DarkRift2 client :

The DarkRift2 Client

By adding the component on the GameObject, you saw that there is a lot of properties. Let’s talk about them :

  • Adress : IP adress of the DarkRift2 Server (127.0.0.1 is the lookup adress wich reference the current machine)
  • Port : Port number of the server
  • IPVersion : You can choose between IPV4 or IPV6
  • InvokeFromDispatcher : As you know, there is a dispatcher. You can invoke from it
  • SniffData : Print all messages into the console
  • Cache : Some settings to adjust for the cache system

For the tutorial, we can let it like that because the server we ran in the last article had this adress : 127.0.0.1:4296

Create a custom ClientManager

As for the server again, we’ll create a new script in the Network folder called ClientManager. It will handle our logic for our game regarding the client (What to do on connection, disconnect, …)

Create the ClientManager and add it to the GameObject ClientManager. For now, we just need a reference to the Client (DarkRift2).

First of all, you need to download my Utilies in order to use the script MonoBehaviourSingletonPersistent wich when is inherited, it implements the Singleton pattern. Persistent means that it doesn’t destroy on load.

Here is the link : https://github.com/TheUncas/unity3D-tools
Then, you can write the following code for the ClientManager :

using DarkRift.Client.Unity;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ClientManager : MonoBehaviourSingletonPersistent<ClientManager>
{

    #region Properties

    /// <summary>
    /// Reference to the DarkRift2 client
    /// </summary>
    public UnityClient clientReference;

    #endregion

    #region Unity Callbacks
    private void Awake()
    {
        base.Awake();

        //////////////////
        /// Properties initialization
        clientReference = GetComponent<UnityClient>();
    }

    // Start is called before the first frame update
    void Start()
    {
        //////////////////
        /// Load the game scene
        SceneManager.LoadScene("MainGameScene", LoadSceneMode.Additive);
    }
    #endregion
}

Don’t forget to use the DarkRift.Client.Unity namespace !

Try the client connection

All is ready to be tested, do you think that we can lauch the client right now ? Maybe.. let’s try :

Seems to work. The MainGameScene is correctly loaded but if you look at the console window, you will notice that there is an error. The client cannot connect to the server.

This error occurs only if the server is unreachable. In my case (and maybe yours) i don’t have launched the server before lauching the client. Of course, it’s basic. If the client need to connects to the server, the server must be turned on before the client try to connect.

Start the server with the build we made on the last article and try to restart the client.

We did it right. The clients succeded to connect to the server. And if you go back to the server windows process, you will notice that the server displayed a message :

Yes, it informs you that a new client has connected ! Perfect, isn’t it ? …. but, wait… is the ball synchronized ?

Of course not ! we’ve just handle the client connection. We havn’t send any message to client about the ball position… And the client doesn’t know read message from the server for now.

What’s next ?

On the next article, we’ll explain how we can synchronize object of the MainGameScene and how we will do it… a lot of work is waiting for us but we are on the good way !

Thanks for reading.

DarkRift 2 Tutorial for Unity 3D – Part 5 – Build the server and load game scene

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

Load the game scene

In the last article, we’ve built our server scene. As you surely know, this scene needs to load the shared game scene between the server and the client. So, we just need to include the following namespace in the GameServerManager script :

using UnityEngine.SceneManagement;

and load the scene on the Start() function :

//////////////////
/// Load the game scene
SceneManager.LoadScene("MainGameScene", LoadSceneMode.Additive);

The LoasScene method of the SceneManager takes 2 parameters which are :

  • The name of the scene.
  • The loading mode. If we don’t specify the Additive mode, Unity will remove from the game all opened scenes and load the scene “MainGameScene” alone

It’s not finished, in order to use the correctly the SceneManager, all scenes loaded by this classe need to be added to the build settings. So, follow these steps :


Close the Build Settings window. Let’s run the game. You can see that the MainGameScene is now loaded correctly in the hierarchy.

Build the server

For now, if you have already built games on windows (or another OS), you know that what you see on unity will be displayed on a window on your computer. That’s fine if you need to test your game or build a client version, but for a server, it’s inappropriate.

Yes, the server process doesn’t need to display graphics, play sounds, …, it just needs to handle the game in a background process. Desactivating graphics and sounds for example will reduce the need of resource. By doing that, you will be able to run more servers on the same machine.

Unity provide a “Headless mode” for that. It’s specialy recommended when you build servers.

First step is to add the MainServerScene into the Build Settings. After that, you need to put the server scene at the first place of the build. Like that, when the build will be executed, it will launch the first scene in the Build Settings, to know : MainServerScene :

Select a destination folder for the build, and launch the .exe

Oh damn ! the build seems to execute but throw an exception ! By reading the message, it seems that there is an issue with the DR2 library… the error is :

NotSupportedException: Specified method is not supported.
  at System.Threading.Mutex..ctor (System.Boolean initiallyOwned, System.String name) [0x00006] in <0000d56de0ae43ca875d7babfd990580>:0
  at (wrapper remoting-invoke-with-check) System.Threading.Mutex..ctor(bool,string)
  at DarkRift.Server.DataManager..ctor (DarkRift.Server.DarkRiftServer server, DarkRift.Server.ServerSpawnData+DataSettings settings) [0x0006f] in <56e64ae805e047608792040aeb2db28d>:0
  at DarkRift.Server.DarkRiftServer..ctor (DarkRift.Server.ServerSpawnData spawnData) [0x00024] in <56e64ae805e047608792040aeb2db28d>:0
  at DarkRift.Server.Unity.XmlUnityServer.Create (System.Collections.Specialized.NameValueCollection variables) [0x0007f] in <88a1dbe1fedd44a6993e063fea92a13f>:0
  at DarkRift.Server.Unity.XmlUnityServer.Create () [0x00006] in <88a1dbe1fedd44a6993e063fea92a13f>:0
  at DarkRift.Server.Unity.XmlUnityServer.OnEnable () [0x00008] in <88a1dbe1fedd44a6993e063fea92a13f>:0

Ok, don’t worry, it’s only because the “mutex” class used in DarkRift2 is not compatible with the current “Api Compatibility Level*” parameter. So, open the Build Settings window, and open Player Settings :

Scroll down and change the value to “.NET 4.x” and rebuild the server. Launch it :

Authorize the application to communicate accross the network. And now, you cannot see any error ! Even better, if you read well, you can see a message informing you that the server is listening :

[Info]    DefaultNetworkListener Server mounted, listening on port 4296.

What next ?

On the next article, we will create the client and load the game scene too. It will be relatively simple because it will be extremely closed to what we’ve done in this article. So, if you think that you can do it alone wihtout my help, you can skip the part 6 and go directly to the part 7 !

Thanks you for reading.

DarkRift 2 Tutorial for Unity 3D – Part 4 – Create Unity embedded server

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

Welcome back ! At this point of the tutorial, we are ready to create the unity embedded server. As explained on the last article, we need to create a specific scene for the server. Let’s create the scene named “MainServerScene” :

Create the Unity server

Open the scene

As the scene is created only to host the server process, we don’t need any camera. So delete it. Create a new GameObject in the hierarchy, and name it “GameServerManager“.

Once created, add the “Server” component provided by DarkRift. See the screenshot below :

Be careful of using the right server component, don’t use the Server (Legacy) wich is an old version of the DarkRift server component

Oh, you can see that there is few properties. I will explain them right now !

XML Unity Server properties

  • Configuration : It’s a text asset that the server needs and wich contains the configuration of the server. We’ll take a look on it later.
  • Create on enable : Says to the component that the server creates automatically on startup.
  • Events from dispatcher : DarkRift2 as an internal dispatcher (see more here). Enabling this option allow the server to receive events from the dispacther.
Good to know : The server component is named XML Unity Server only because it is excpecting a configuration file on the XML format !

The configuration file

The configuration file is XML formatted and has the following structure :

<configuration>
  <server/>
  <pluginSearch></pluginSearch>
  <logging></logging>
  <plugins ></plugins>
  <data/>
  <databases></databases>
  <listeners></listeners>
  <cache></cache>
</configuration>

As you can see, there is a lot of elements. All are mandatory, except one : <cache>. – See this for reference documentation.

Are you ready to create the configuration file ? No ? Uhhh, ok, by chance, DarkRift provides an example configuration file within the unity package.

You can find it in the following location : DarkRift/DarkRift/Plugins/Server. Drag the file to the component on the GameServerManager :

Now, try to launch the scene, and take a look to the console :

That’s all, the server is mounted and it’s ready to handle client connections and broadcast messages. That’s good but it’s not enough. we need a server manager that will handle clients connections, message broadcasting, ..

So, create folder Scripts/Network and add a new C# script named “GameServerManager“. Add it as component to the GameServerManager game object

The GameServerManager component

For now, we just want that the GameServerManager keep a list of all connected clients. for that we will use a C# List and listen to an event from the XML Unity Server called ClientConnected.

Open the script on Visual Studio or your favorite IDE… wich i hope is Visual Studio 🙂 and copy this code :

/// <summary>
/// List of connected clients
/// </summary>
public List<int> clientsId;

This list need to be initialized before to be used, so in the Start() function, write this :

    private void Start()
    {
        //////////////////
        /// Properties initialization
        clientsId = new List<int>();
    }

Now the list is ready to be used, we want to fill it with all the new client who will connect. First thing is to create a property and then get the reference of the XML Unity Server.

using DarkRift.Server.Unity;

...

    /// <summary>
    /// Reference to the DarkRift server
    /// </summary>
    public XmlUnityServer serverReference;

Don’t forget to reference the DarkRift.Server.Unity namespace ! Then, in the Start() method, use the GetComponent() function :

serverReference = GetComponent<XmlUnityServer>();

Now we have the reference to the DarkRift server, we’ll just suscribe to the event in order to add the client ID to the list. So in the Start() method :

//////////////////
/// Events subscription
serverReference.Server.ClientManager.ClientConnected += ClientConnected;

Finally, create now the ClientConnected callback function :

#region Server events
/// <summary>
/// When a client connects to the DarkRift server
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClientConnected(object sender, ClientConnectedEventArgs e)
{
    clientsId.Add(e.Client.ID);
}
#endregion

Ok, that’s all. You now know when a client is connected and what is its ID. Good job !

Client disconnection

You can reproduce the same behavior when a client disconnect. Here is the step you need to follow :

  • Suscribe to the ClientDisconnect event
  • Create the ClientDisconnect callback function
  • Remove the ID from the clientsId list

Here is the code

private void Start()
{
    //////////////////
    /// Properties initialization
    clientsId = new List<int>();
    serverReference = GetComponent<XmlUnityServer>();

    //////////////////
    /// Events subscription
    serverReference.Server.ClientManager.ClientConnected += ClientConnected;
    serverReference.Server.ClientManager.ClientDisconnected += ClientDisconnected;
}

...

/// <summary>
/// When a client disconnects to the DarkRift server
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClientDisconnected(object sender, ClientDisconnectedEventArgs e)
{
    throw new NotImplementedException();
}
#endregion

I have something to say you…

In fact, you can have at any time the list of all connected clients by using the following code :

//Returns all clients
serverReference.Server.ClientManager.GetAllClients();
//Return the first client connected
serverReference.Server.ClientManager.GetClient(0);

So, yes, our code is a litte useless now you are aware (JCVD) that you can access these information natively. But later in the tutorial, we’ll use this events to perform additional actions.

In the next article, we’ll load dynamically the MainGameScene and we’ll make a server build of our “game”. Bye !