Check character health before destroying

Tutorial

·

Beginner

·

+0XP

·

30 mins

·

Unity Technologies

Check character health before destroying

In this tutorial, you’re going to adjust the HealthCollectible script so the collectible only gets destroyed if the player character actually needs more health.

By the end of this tutorial, you’ll be able to define and use a property to determine whether the collectible should be destroyed.

1. Overview

In the previous two tutorials, you set up a basic health system and created a CollectibleHealth GameObject to give the player character a health increase. Right now the CollectibleHealth GameObject gets destroyed when the PlayerCharacter GameObject collides with it, whether the player character actually needs the health increase or not. You’re going to adjust the CollectibleHealth GameObject so it only gets destroyed if the player character actually needs more health; this is a much more efficient way to manage in-game resources.

Note: For the purposes of this tutorial, we chose to use the Ruby’s Adventure asset set, and the images used in instructions will reflect this. If you chose another asset set, the file names will be the same, but in your corresponding theme folder.

2. Check the player character’s current health

To adjust your CollectibleHealth GameObject so that it only gets destroyed if the player character needs more health, the HealthCollectible script needs to determine whether or not the character is currently at the maximum health.

To add this check to your script, follow these instructions:

1. Open the HealthCollectible script in your IDE.

At the moment, the instructions to increase the PlayerCharacter’s health and destroy the HealthCollectible GameObject are executed whenever the physics system detects the collision with the PlayerCharacter GameObject. Now, you’ll add an extra condition so these instructions run if, in addition to the collision being detected, the player’s currentHealth is less than their maxHealth.

You can define this second condition using an if statement to check the currentHealth value before executing the code:

if (controller.currentHealth < controller.maxHealth)

Here, the “less than” symbol (<) means that the code will only be executed if the currentHealth value in the PlayerController script is less than the maxHealth value.

2. Choose one of the following options to implement the new condition:

Option 1: Nested if statements

One way to add this second condition is to nest a second if statement inside your first, with both statements wrapped around the code that passes the parameter to the ChangeHealth function and destroys the CollectibleHealth GameObject.

void OnTriggerEnter2D(Collider2D other)
{
   	PlayerController controller = other.GetComponent<PlayerController>();

   	if (controller != null)
   	{
     		if (controller.currentHealth < controller.maxHealth)
     		{
       		controller.ChangeHealth(1);
       		Destroy(gameObject);
     		}
   	}
}

This nested approach isn’t the most elegant solution and it won’t always scale well for complex conditional situations, but it does the job!

Option 2: A single if statement with two conditions

There’s a more efficient approach you can use to include both conditions in a single statement: the and (&&) conjunction operator. Both the condition before and the condition after the && operator must be met for the code inside to be executed.

void OnTriggerEnter2D(Collider2D other)
{
   	PlayerController controller = other.GetComponent<PlayerController>();

   	if (controller != null && controller.currentHealth < controller.maxHealth)
   	{
       	controller.ChangeHealth(1);
       	Destroy(gameObject);
   	}
}

This code has the same functionality as the nested if statements, but it’s neater and easier to scale in complex situations. You can use either approach in your HealthCollectible script.

Important: You’ll need to fix an issue before your code works in Unity, so don’t be concerned if you encounter an error, you’ll handle that in the next step.

3. Define a property in the PlayerController script

You’ll be seeing an error that you’ve previously encountered: the currentHealth variable is private, so your HealthCollectible script can’t currently access it. You can make the variable public, but when you make code public that doesn’t need to be, you’re increasing the chance of annoying bugs that you’ll need to fix later. For example, if you set currentHealth to public, then another script could change it to a value above the maxHealth value – a function like ChangeHealth that can clamp the possible values is a much safer way to control this functionality.

The HealthCollectible script doesn’t need to write to the currentHeath variable as part of its condition checks, it just needs to read the variable’s current value. To facilitate this, you can use a read-only property, a technique you can use to read and store the value of a private variable.

To define a property to read and store the currentHeath value, follow these instructions:

1. Open the PlayerController script in your IDE.

2. At the top of the script, on a new line, above the declaration of the currentHealth variable, add the following code:

public int health { get { return currentHealth; }}

Here’s an explanation of this line of code:

  • The property definition is similar to a variable declaration. It has an access level keyword (public), a type (int), and a name (health).
  • This definition has some unfamiliar syntax: two sets of braces (code blocks), one nested inside the other.
  • The outer block contains the keyword get and a second block. The purpose of the get keyword is to retrieve whatever is in the nested block so that information can be read in other scripts.
  • The inner block returns the currentHealth value. The compiler treats this block like a normal function, so you can add instructions to declare a variable, complete computations, or call other functions here. However, in this case, all you need to do is get the value of the currentHealth variable.

3. Save your changes.

4. Use the health property in your health collectible

Now you can update the HealthCollectible script to use the new property:

1. Return to the HealthCollectible script in your IDE.

2. Update the if statement conditions to use the health property rather than the currentHealth variable directly:

if (controller != null && controller.health < controller.maxHealth)

Note: If you are using nested if statements, make this same change to the inner statement.

3. Save your changes.

4. Return to the Unity Editor and test your updates.

Now, if the player character has full health when it collides with the CollectibleHealth GameObject, the CollectibleHealth GameObject won’t be destroyed.

5. Check your scripts

Take a moment to check that your scripts are correct before continuing.

PlayerController.cs

Important: If you completed any extension work in your script, this will not be reflected in the reference script below.

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
  	// Variables related to player character movement
 	public InputAction MoveAction;
  	Rigidbody2D rigidbody2d;
  	Vector2 move;
  	public float speed = 3.0f;

  	// Variables related to the health system
  	public int maxHealth = 5;
  	public int health { get { return currentHealth; }}
  	int currentHealth = 1;

     	// Start is called once before the first execution of Update after the MonoBehaviour is created

  	void Start()
  	{
     		MoveAction.Enable();
     		rigidbody2d = GetComponent<Rigidbody2D>();
     		//currentHealth = maxHealth;
  	}
 
  	// Update is called once per frame
  	void Update()
  	{
     		move = MoveAction.ReadValue<Vector2>();
     		//Debug.Log(move);
  	}

  	// FixedUpdate has the same call rate as the physics system
  	void FixedUpdate()
  	{
     		Vector2 position = (Vector2)rigidbody2d.position + move * speed * Time.deltaTime;
     		rigidbody2d.MovePosition(position);
  	}

  	public void ChangeHealth (int amount)
  	{
     		currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
     		Debug.Log(currentHealth + "/" + maxHealth);
  	}

}

HealthCollectible.cs

using UnityEngine;

public class HealthCollectible : MonoBehaviour
{

void OnTriggerEnter2D(Collider2D other)
  {
   		PlayerController controller = other.GetComponent<PlayerController>();

   		if (controller != null && controller.health< controller.maxHealth)
   		{
       		controller.ChangeHealth(1);
       		Destroy(gameObject);
   		}
  }
}

Note: If you used the nested if statement option (Option 1) for the if condition, your HealthCollectible code will look slightly different.

6. Next steps

You’ve fully configured the health system for your game, but at the moment there’s no real need for it because there’s nothing that can damage the player character. In the next tutorial, you’ll add static damage zones that decrease the player character's health, the most basic challenge that your players will encounter.

Complete this tutorial