Player Health (Single Player)

Checked with version: 5.3

-

Difficulty: Intermediate

Synchronizing Player States is an important concept in Multiplayer Networking.

As an example, we will have the bullets do damage by affecting a Health property of a player when the bullets collide them. The Current Health of the player will be a state and we will synchronize the state of the player’s current health across the network.

Create Bullet Collisions

The first step to having the bullet affect its target is to add collision handling logic to the bullet. For this example, we will simply destroy the bullet GameObject when it hits any other collider.

  • Create and Add a new script named “Bullet ” to the Bullet prefab asset.
  • Open the Bullet script for editing.
  • Remove all of the sample code in the script.
  • Add collision handling logic that destroys the bullet when it comes in contact with another collider.
using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {
    
    void OnCollisionEnter()
    {
        Destroy(gameObject);
    }
}

  • Save the Bullet Script.
  • Return to Unity.

Test the current state of the project.

  • 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 when a bullet hits another GameObject with a Collider attached, the bullet will be destroyed. Because the bullet is being managed by the NetworkManager, when the bullet on the Server is destroyed, it will be destroyed on all of the Clients as well.

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

Player Health

To create a single player version of Health, we will need a new script to track and display the Current Health of our player.

  • Create and Add a new script named “Health” to the Player prefab asset.
  • Open the Health script for editing.
  • Remove all of the sample code in the script.
  • Add a field to hold the constant starting value of, or maximum, health; initialized to 100.
public const int maxHealth = 100;

  • Add a field to hold the Current Health; initialized to Max Health.
public int currentHealth = maxHealth;

  • Add logic to handle taking damage and reducing health when player is hit.
public void TakeDamage(int amount)
{
    currentHealth -= amount;
    if (currentHealth <= 0)
    {
        currentHealth = 0;
        Debug.Log("Dead!");
    }
}

The final Health script should look like this.

Health

Code snippet

using UnityEngine;

public class Health : MonoBehaviour 
{
    public const int maxHealth = 100;
    public int currentHealth = maxHealth;

   public void TakeDamage(int amount)
    {
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }
    }
}
  • Save the Health script.

The Bullet script will need to be updated to call the TakeDamage function on target player’s Health script when it hits the target player.

  • Open the Bullet script for editing.
  • Change the OnCollisionEnter function to have a Collision parameter.
void OnCollisionEnter(Collision collision)

  • Add a call to TakeDamage from the Health script in the collision handling function.
var hit = collision.gameObject;
var health = hit.GetComponent<Health>();
if (health != null)
{
    health.TakeDamage(10);
}

The final Bullet script should look like this:

Bullet

Code snippet

using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {
    
    void OnCollisionEnter(Collision collision)
    {
        var hit = collision.gameObject;
        var health = hit.GetComponent<Health>();
        if (health  != null)
        {
            health.TakeDamage(10);
        }

        Destroy(gameObject);
    }
}
  • Save the Bullet script.

The bullet will now damage the target player. Damage to the player, however, is not visible in the scene. We will need to create a simple health bar for each player.

Creating A Simple Healthbar

To do this, we will need to create a simple set of UI elements. It is worth noting that the following solution is less than optimal for a final production quality game, but for the sake of this example, this solution will address the immediate problem of needing a simple healthbar in a few easy steps.

  • Create a new UI Image in the scene.

Note that this will also create a new parent Canvas and an EventSystem GameObject.

  • Rename the Canvas GameObject to “Healthbar Canvas”.
  • Rename the Image GameObject to “Background”.
  • With the Background GameObject selected:
  • ... set the RectTransform component’s Width property to 100.
  • ... set the RectTransform component’s Height property to 10.
  • ... set the Image component’s Source Image to the built-in InputFieldBackground.
  • ... set the Image component’s Color to Red.
  • Do not change the Anchor or Pivot in the Anchor Presets Window.
  • Duplicate the Background GameObject.
  • Rename the duplicated Background GameObject to “Foreground”.
  • Make the Foreground GameObject a Child of the Background GameObject.

The organization of the Healthbar should look like this:

description

  • With the Foreground GameObject selected:
  • ... Set the Image component’s Color to Green.
  • ... Open the Anchor Presets Window and Set the Pivot and Position to Middle Left.

description

This Healthbar needs to be added to the player prefab and hooked into the current health and damage system.

First, the Canvas needs to be changed from a default Overlay Canvas into a World Space Canvas and then it needs to be added to the player prefab. For more information on Canvases and how they work, please see the page on Canvas.

  • Drag the Player prefab from the Project Window into the Hierarchy to create an instance in the scene.
  • With the Healthbar Canvas selected:
  • ... change the Canvas’ Render Mode to World Space.
  • Make the Healthbar Canvas a Child of the Player GameObject.

The organization of the Player should look like this:

description

  • With the Healthbar Canvas selected:
  • ... reset the RectTransform component with the gear menu.
  • ... set the RectTransform’s Scale to (0.01, 0.01, 0.01).
  • ... set the RectTransform’s Position to (0.0, 1.5, 0.0).
  • With the Player GameObject selected:
  • ... apply the changes to the Player prefab.
  • Save the scene.

To hook the Healthbar Canvas into the current health and damage system, we will need to reference the Healthbar from the Health script and set the Foreground Image to display the Current Health.

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

  • Add a field to hold the reference to the RectTranform on the Healthbar’s Foreground element.
public RectTransform healthBar;

Note that we are referencing the Foreground element’s RectTransform here as we will be changing the *RectTransform’s Width/ property to change the Healthbar’s display.

  • Add logic to update the Healthbar’s display to reflect the Current Health value.
healthBar.sizeDelta = new Vector2(
    currentHealth, 
    healthBar.sizeDelta.y);

Note that we need to change the RectTransform’s Size Delta as a Vector2 to change the width.

The final script should look like this:

Health

Code snippet

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

public class Health : MonoBehaviour {

    public const int maxHealth = 100;
    public int currentHealth = maxHealth;
    public RectTransform healthBar;

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

        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
}
  • Save the script.
  • Return to Unity.
  • With the Player GameObject selected in the Hierarchy Window:
  • ... set the Healthbar Field on the Health component to the Foreground GameObject.

description

  • With the Player GameObject selected:
  • ... apply the changes to the Player prefab.
  • Delete the instance of the Player GameObject from scene.
  • Save the scene.

Lastly, we will need to keep the Healthbar facing the main camera.

  • With the Healthbar Canvas on the Player prefab selected in the Project window:
  • ... create and add a new script called “Billboard”.
  • Open the Billboard script for editing.
  • Add logic in the Update function to keep the Healthbar looking at the Main Camera.
transform.LookAt(Camera.main.transform);

  • Remove all unnecessary sample code.

The final script should look like this:

Billboard

Code snippet

using UnityEngine;
using System.Collections;

public class Billboard : MonoBehaviour {

    void Update () {
        transform.LookAt(Camera.main.transform);
    }
}

Test these additions.

  • 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.

Currently, changes to player's current health are being applied independently on all of the Clients and the Server. When one player shoots another, the Bullet and Health scripts are working "locally” on each particular Client and the Server. There is no synchronization happening. The bullets, however, are being managed as Spawned objects through the NetworkManager. They will be destroyed on all Clients when they have detected a collision. Because the bullets exists on every Client, it is possible to have collisions between the bullet and a player, and it is possible that the player could take damage. It is also possible, however, due to latency and other issues, that the bullet could get destroyed on one Client before a collision is registered on another. Because the bullets are being synchronized, but the player’s Current Health is not, this can lead to the player’s Current Health being completely different between all of the Clients and the Server.

At this point if the two players shoot at each other often enough, it should be easy to see the player’s current health differ between Clients.

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

Related tutorials

Related documentation