I am trying to add a health pickup system for my laser defender game. I managed to pickup the pill within the scene with this newly created HealthPickup class
using UnityEngine;
public class HealthPickup : MonoBehaviour
{
[SerializeField] int healAmount = 30;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Player player = other.GetComponent<Player>();
if (player != null)
{
Health playerHealth = player.GetComponent<Health>(); // Get the Health component from the player
if (playerHealth != null)
{
playerHealth.Heal(healAmount); // Pass maxHealth from the Health component
Destroy(gameObject);
}
}
}
}
}
but when I update this class to spawn the health pickup object by default when the player’s health reduces to 10%
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HealthPickup : MonoBehaviour
{
[SerializeField] int healAmount = 30;
[SerializeField] float despawnTime = 10f; // Time in seconds before the pickup disappears
private bool isActive = false;
private float spawnTime;
void Start()
{
Health playerHealth = FindObjectOfType<Player>().GetComponent<Health>();
if (playerHealth != null)
{
float healthPercentage = (float)playerHealth.GetHealth() / playerHealth.maxHealth;
if (healthPercentage < 0.1f)
{
isActive = true;
spawnTime = Time.time;
}
}
}
private void Update()
{
if (isActive && Time.time - spawnTime > despawnTime)
{
Destroy(gameObject);
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (isActive && other.CompareTag("Player"))
{
Player player = other.GetComponent<Player>();
if (player != null)
{
Health playerHealth = player.GetComponent<Health>();
if (playerHealth != null)
{
playerHealth.Heal(healAmount);
Destroy(gameObject);
}
}
}
}
}
I get this compiling error " Assets\Scripts\HealthPickup.cs(19,85): error CS0122: ‘Health.maxHealth’ is inaccessible due to its protection level"
I am trying to add this feature to the game but it seems I went way over my head and feeling frustrated as a total noob Could you please have a look and share some wisdom about how to create this setup for my game?
First of all, fix the compiler error. maxHealth in your Health class is private (by default).
Secondly, add Debug.Logs to your code to see what’s going on at runtime. You developed a logic flow but, just by reading the code, it is often difficult or even impossible to see if the code works as you think it works because a lot is going on at runtime.
You are trying to access maxHealth here, but it’s not accessible. You need to either 1) make it accessible, or 2) let the health calculate the percentage. I vote for number 2. In your Health script, add this
public float GetHealthPercentage()
{
return health / (float)maxHealth;
}
Then, on the line with the issue, you just get that
okay I did what you said (2nd option) now I don’t have any compiler errors but now the player ship is not visible when I hit play
while placing health pickup object I’ve created several sorting layers and tagged the player as player is this issue occurs because of that or what can cause this?
when I hit play it becomes disabled, I’ve applied all on overrides dropdown on the player prefab but still it becomes not visible when I try to play the game
yeap I guess my healthpickup script was disabling it, I tweaked it a little and now I can see the player when I play the game.
using UnityEngine;
public class HealthPickup : MonoBehaviour
{
[SerializeField] int healAmount = 30;
void Awake()
{
CheckAndSpawnHealthPickup();
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Player player = other.GetComponent<Player>();
if (player != null)
{
Health playerHealth = player.GetComponent<Health>(); // Get the Health component from the player
if (playerHealth != null)
{
Destroy(gameObject);
}
}
}
}
private void CheckAndSpawnHealthPickup()
{
Health playerHealth = FindObjectOfType<Player>().GetComponent<Health>(); // Get the Health component of the player
Debug.Log("player health" + playerHealth);
if (playerHealth != null)
{
float healthPercentage = playerHealth.GetHealthPercentage();
Debug.Log("player percentage" + healthPercentage);
if (healthPercentage < 0.5f)
{
// Activate the health pickup object
gameObject.SetActive(true);
}
else
{
// Deactivate the health pickup object
gameObject.SetActive(false);
}
}
}
}
With this health pickup class I was trying to spawn the pill when player’s health reduces to 50%. Does this code is correct or what should I add it to work as I was planning. Do you have any suggestions?
Btw I’ve added this script to the healthpickup object and made the healthpickup object a prefab was it a correct thing to do?
This script will only check the player’s health once - when it is first created. I don’t know how and when the object this script is on is getting spawned, but it only ever checks once. If it’s in the scene at design time, you probably want to have the CheckAndSpawnHealthPickup() in the update loop.
Usually you’d have a script on an empty game object that checks the player’s health constantly, or listens to an event that fires each time the player’s health changes. When the health meets certain criteria (like dropping below 50%) you can spawn a health pickup. At least that’s the approach I would take.
This is fine, especially if you are going to place a few of them in the scene
okay I’ve made some changes in both HealthPickup and Health scripts.
using UnityEngine;
public class HealthPickup : MonoBehaviour
{
[SerializeField] int healAmount = 30;
void Update()
{
CheckAndSpawnHealthPickup();
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Player player = other.GetComponent<Player>();
if (player != null)
{
Health playerHealth = player.GetComponent<Health>();
if (playerHealth != null && playerHealth.GetHealthPercentage() < 1f)
{
playerHealth.Heal(healAmount);
Destroy(gameObject);
}
}
}
}
private void CheckAndSpawnHealthPickup()
{
Health playerHealth = FindObjectOfType<Player>().GetComponent<Health>();
if (playerHealth != null)
{
float healthPercentage = playerHealth.GetHealthPercentage();
Debug.Log("Health Percentage: " + healthPercentage);
if (healthPercentage < 0.5f)
{
// Activate the health pickup object
gameObject.SetActive(true);
}
else
{
// Deactivate the health pickup object
gameObject.SetActive(false);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Health : MonoBehaviour
{
[SerializeField] bool isPlayer;
[SerializeField] int health = 100;
[SerializeField] int maxHealth = 100;
[SerializeField] int score =50;
[SerializeField] ParticleSystem hitEffect;
[SerializeField] bool applyCameraShake;
CameraShake cameraShake;
AudioPlayer audioPlayer;
ScoreKeeper scoreKeeper;
LevelManager levelManager;
void Awake()
{
cameraShake = Camera.main.GetComponent<CameraShake>();
audioPlayer = FindObjectOfType<AudioPlayer>();
scoreKeeper = FindObjectOfType<ScoreKeeper>();
levelManager = FindObjectOfType<LevelManager>();
}
void OnTriggerEnter2D(Collider2D other)
{
DamageDealer damageDealer = other.GetComponent<DamageDealer>();
if (damageDealer != null )
{
TakeDamage(damageDealer.GetDamage());
PlayHitEffect();
audioPlayer.PlayDamageClip();
ShakeCamera();
damageDealer.Hit();
}
}
public int GetHealth()
{
return health;
}
void TakeDamage(int damage)
{
health -= damage;
if (health <= 0)
{
Die();
}
Debug.Log("Current Health after Damage: " + health);
}
void Die()
{
if(!isPlayer)
{
scoreKeeper.ModifyScore(score);
}
else
{
levelManager.LoadGameOver();
}
Destroy(gameObject);
}
void PlayHitEffect()
{
if( hitEffect != null)
{
ParticleSystem instance = Instantiate( hitEffect, transform.position, Quaternion.identity);
Destroy(instance.gameObject, instance.main.duration + instance.main.startLifetime.constantMax);
}
}
void ShakeCamera()
{
if (cameraShake != null && applyCameraShake)
{
cameraShake.Play();
}
}
public void Heal(int amount)
{
health += amount;
health = Mathf.Clamp(health, 0, maxHealth);
Debug.Log("Current Health after Healing: " + health);
}
public float GetHealthPercentage()
{
return health / (float)maxHealth;
}
}
After adding Debug to both classes I see that the damage systeam working as expected but I am not sure why the healthpickup object does not appears after player’s health reduces to 50% here is a screenshot of the console
You need a different object to do the health check. When the pickup runs the first loop, it sees that the health is above 0.5f and disables itself. When it does that, the script stops executing Update, so it never checks the health again.
thank you so much for this aproach I’ve created this script and now the object appears as intended
using UnityEngine;
public class HealthPickupController : MonoBehaviour
{
[SerializeField] HealthPickup healthPickup; // Reference to the health pickup object
[SerializeField] float checkInterval = 2f; // Time interval for health checks
[SerializeField] float healthThreshold = 0.5f; // Health percentage threshold for pickup activation
private Health playerHealth; // Reference to the player's health script
private float nextCheckTime; // Time for the next health check
private void Start()
{
playerHealth = FindObjectOfType<Player>().GetComponent<Health>();
nextCheckTime = Time.time + checkInterval;
}
private void Update()
{
if (Time.time >= nextCheckTime)
{
nextCheckTime = Time.time + checkInterval;
CheckAndManageHealthPickup();
}
}
private void CheckAndManageHealthPickup()
{
if (playerHealth != null)
{
float healthPercentage = playerHealth.GetHealthPercentage();
if (healthPercentage < healthThreshold)
{
healthPickup.gameObject.SetActive(true); // Activate health pickup
}
else
{
healthPickup.gameObject.SetActive(false); // Deactivate health pickup
}
}
}
}
using UnityEngine;
public class HealthPickup : MonoBehaviour
{
[SerializeField] int healAmount = 30;
void Update()
{
CheckAndSpawnHealthPickup();
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Player player = other.GetComponent<Player>();
if (player != null)
{
Health playerHealth = player.GetComponent<Health>();
if (playerHealth != null && playerHealth.GetHealthPercentage() < 1f)
{
playerHealth.Heal(healAmount);
Destroy(gameObject);
}
}
}
}
private void CheckAndSpawnHealthPickup()
{
Health playerHealth = FindObjectOfType<Player>().GetComponent<Health>();
if (playerHealth != null)
{
float healthPercentage = playerHealth.GetHealthPercentage();
Debug.Log("Health Percentage: " + healthPercentage);
if (healthPercentage < 0.5f)
{
// Activate the health pickup object
gameObject.SetActive(true);
}
else
{
// Deactivate the health pickup object
gameObject.SetActive(false);
}
}
}
}
but now the player is not able to pickup the object as it seems below from the object and I got this error on the console NullReferenceException: Object reference not set to an instance of an object
HealthPickup.CheckAndSpawnHealthPickup () (at Assets/Scripts/HealthPickup.cs:31)
HealthPickup.Update () (at Assets/Scripts/HealthPickup.cs:9)
I was thinking that this might be an issue about layer collision matrix so here is a screenshot of it.
I feel like I am so close to add this feature to the game but I could tell that now I am burnout
using UnityEngine;
public class HealthPickup : MonoBehaviour
{
[SerializeField] int healAmount = 30;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Player player = other.GetComponent<Player>();
if (player != null)
{
Health playerHealth = player.GetComponent<Health>();
if (playerHealth != null && playerHealth.GetHealthPercentage() < 1f)
{
playerHealth.Heal(healAmount);
Destroy(gameObject);
}
}
}
}
}
Well that should do it, I think. You should now get a health pickup when the player’s health drops below 50%. Just be aware that you destroy the pickup when the player picks it up, so the next time the health drops below 50% you will get an error because the controller will still try to enable it. You can remove the Destroy(gameObject) from the pickup script because as soon as the player heals and their health goes above 50% again, the controller will disable the pickup.
Ideally, you’d want to spawn a pickup instead of enabling/disabling the same one all the time.
yes the health pickup object is available now when the player’s health is below 50% but the player could not pickup the object as it seems to go below the object and does not collide with it. I’ve shared the collision matrix screenshot because of this as I was thinking may be something wrong with its setup but I am not sure may be there is something missing in the code as well?
The matrix is fine - player collides with pickups - but it doesn’t help me. I don’t know what the layers your objects are on. Make sure the health pickup is on the Pickups layer and the player is on the Player layer. Also make sure both have colliders, and I believe one of them needs a Rigidbody2D. I’d put it on the player.
Edit
Sorry, I thought it was the error that caused it to not collide, which is why I didn’t address that specific point