Networking Player Health

Checked with version: 5.3

-

Difficulty: Intermediate

Changes to the player’s current health should only be applied on the Server. These changes are then synchronized on the Clients. This is called Server Authority. For more information on Server Authority please see the page on Network System Concepts.

To make our current health and damage system network aware and working under Server authority, we need to use State Synchronization and a special member variable on networked objects called SyncVars. Network synchronized variables, or SyncVars, are indicated with the attribute [SyncVar]. For more information on SyncVars, please see the page on State Synchronization.

  • Open the Health script for editing.
  • Add the namespace UnityEngine.Networking.
using UnityEngine.Networking;

  • Change script to derive from NetworkBehaviour.
public class Health : NetworkBehaviour

  • Make currentHealth a [SyncVar].
[SyncVar]
public int currentHealth = maxHealth;

  • Add a check for “isServer” to the TakeDamage function, so that damage will only be applied on the Server.
if (!isServer)
{
    return;
}

The final script should look like this:

Health

Code snippet

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    [SyncVar]
    public int currentHealth = maxHealth;
    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
        {
            return;
        }
        
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }

        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
}
  • Save the script.
  • Return to Unity.
  • Build and Run this scene as a standalone application.
  • Click the Host button from the in-game UI to start this game as a Host.
  • Move the Player GameObject.
  • Return to Unity.
  • Enter Play Mode.
  • Click the LAN Client button from the in-game UI to connect to the Host as a Client.

Now the player’s current health is only being applied on the Server and being synchronized on all of the Clients.

This is difficult to see on all clients, however, because the Healthbar is not working on all of the instances of the game. The variable currentHealth is public and can be seen in the editor. If the editor is being run as the connected client, not the host, the current health on the Player GameObjects should be easy to see in the Inspector.

description

It should also be easy to see that the Healthbar is working, but only working on the Host Client attached to the Server and not on any of the other Clients.

This is because we are not synchronizing the value of the Foreground GameObject’s RectTransform across the network and the code to set the Healthbar’s Size Delta is only being run on the Server. The reason the Healthbar works on the Host Client is because it is local to the Server. The Host Client does not have data serialized to it because it shares the same scene with the Server.

We now need to synchronize the RectTransform on the Healthbar's Foreground GameObject.

  • Close the standalone player.
  • Return to Unity.
  • Exit Play Mode.

This brings us to another tool for State Synchronization: the SyncVar hook. SyncVar hooks will link a function to the SyncVar. These functions are invoked on the Server and all Clients when the value of the SyncVar changes. For more information on SyncVars and SyncVar hooks, please see the page on State Synchronization.

  • Open the Health script for editing.
  • Move the code that changes the Healthbar into it’s own function called OnChangeHealth.
void OnChangeHealth (int currentHealth)
{
    healthBar.sizeDelta = new Vector2(health, currentHealth.sizeDelta.y);
}

It is worth noting that this function must have a parameter of the same type as the variable with the [SyncVar] attribute, in this case int currentHealth, and that the current value of the SyncVar will be sent to the hooked function as an argument.

  • Set a hook to this new function in the SyncVar attribute for currentHealth.
[SyncVar(hook = "OnChangeHealth")]

The final script should look like this:

Health

Code snippet

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;

    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;
        
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }
    }

    void OnChangeHealth (int health)
    {
        healthBar.sizeDelta = new Vector2(health, healthBar.sizeDelta.y);
    }
}

Now when the value of currentHealth changes, OnChangedHealth will be called on the Server and all Clients to update the Healthbar.

  • Save the script.
  • Return to Unity.
  • Build and Run this scene as a standalone application.
  • Click the Host button from the in-game UI to start this game as a Host.
  • Move the Player GameObject.
  • Return to Unity.
  • Enter Play Mode.
  • Click the LAN Client button from the in-game UI to connect to the Host as a Client.

When the players shoot each other, all of the Healthbars should now reflect the value of player GameObject’s Current Health. The Healthbar is now synchronized and works in all instances of the game.

  • Close the standalone player.
  • Return to Unity.
  • Exit Play Mode.

Related documentation