- Home /
Game Object not self destroying after Destroy(gameObject)
Here's a video that shows what's happening: Click Here (YouTube)
I don't know why this is happening. I suspect it might have something to do with using a UnityEvent that Invokes a function within a component attached to the same Game Object as the script that calls that event, but I have no clue on how that works or how I should fix it. I tried disabling the HealthManager script before destroying the Game Object; no luck. Tried calling Destroy(gameObject) every frame if health gets to 0; no luck, got a "GameObject you are trying to access has been destroyed" error. And why does my code work almost every time, but it just randomly fails (that's what really bugs me out)? Does anyone have an idea of how to fix this? I got this in the middle of a game jam, so it's really stressful :/
Here are the pieces of code highlighted in the video:
(HealthManager.cs)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
public class HealthManager : MonoBehaviour
{
public float maxHealthPoints;
public float healthPoints;
public float defense = 1;
public GameObject hitParticles;
public GameObject deathParticles;
public bool isDead;
public HealthBar healthBar;
public UnityEvent onDeath;
private void Start()
{
if (maxHealthPoints == 0)
maxHealthPoints = healthPoints;
if (healthBar)
{
healthBar.SetMaxHealth(maxHealthPoints);
healthBar.SetHealth(healthPoints);
}
}
public void ReceiveDamage(float amount)
{
healthPoints -= amount / defense;
if (healthBar)
healthBar.SetHealth(healthPoints);
if (healthPoints <= 0)
{
Destroy(Instantiate(deathParticles, transform.position, Quaternion.identity), 1);
healthPoints = 0;
isDead = true;
onDeath.Invoke();
}
else
Destroy(Instantiate(hitParticles, transform), 1);
}
public void ReceiveHealth(float amount)
{
healthPoints += amount;
if (healthPoints > maxHealthPoints)
healthPoints = maxHealthPoints;
if (healthBar)
healthBar.SetHealth(healthPoints);
}
}
(E2.cs - Enemy Behaviour)
using System;
using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using Pathfinding.Util;
using UnityEngine;
using UnityEngine.Events;
// Enemy 2: Runs towards player with a knife. Low Health, Mid Damage, Mid Speed, Low Defense, Mid Range.
public class E2 : MonoBehaviour
{
public int scorePoints;
public string targetTag;
public float angleOffset;
private Item knifeItem;
private Knife knife;
private AIPath _aiPath;
private AIDestinationSetter _aiDestination;
private GameObject target;
private void Start()
{
_aiPath = GetComponent<AIPath>();
_aiDestination = GetComponent<AIDestinationSetter>();
knifeItem = GetComponentInChildren<Item>();
knife = GetComponentInChildren<Knife>();
}
void Update()
{
target = GameObject.FindWithTag(targetTag);
if (target)
{
if (Vector3.Distance(transform.position, target.transform.position) <=
transform.localScale.y / 2 + knife.range)
{
_aiPath.enabled = false;
Vector3 dir = target.transform.position - transform.position;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg + angleOffset;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
knife.attack = true;
}
else
{
_aiPath.enabled = true;
_aiDestination.target = target.transform; // For when the target changes.
knife.attack = false;
}
}
}
public void OnDeath()
{
PlayerPrefs.SetInt("lastScore", PlayerPrefs.GetInt("lastScore") + scorePoints);
knife.attack = false;
knife.canAttack = true;
Item iog = Instantiate(knifeItem.selfPrefab, transform.position, transform.rotation).GetComponent<Item>();
iog.UpdatePickupbleTrigger(true);
iog.UpdateActionCollider(false);
iog.UpdateMainBehaviour(false);
Destroy(gameObject);
}
}
Answer by UDN_7bcbe08e-783e-45c6-a9b2-4e942df1984b · May 28, 2020 at 05:26 PM
Ok, I figured it out. I'm really sorry if you're having this problem and are reading this thread, because in my case it was just due to my stupidity :/ Every item on my game has a "isPickedUp" bool. When that bool stays false for too long, the item self destructs, so that the items that were dropped on the ground clean themselves up. When the player picks an item up (or throws it away) the variable is updated. I, however, forgot to do so with the enemies. So every enemy would lose their item if they survived long enough. I would like to thank and apologize @ADiSiN and @Dev1910 for their time.
Answer by ADiSiN · May 27, 2020 at 02:28 AM
Hi!
I couldn't find the issue within your script, however I would like to point out that moment in your video: So, what happens is that you are killing other type of enemies and at the same time your future undestroyable enemy loses its sword.
It can be coincidence, but as we can see here: Your death animation is still playing (the explosive one), but it fails to Destroy even though animation played twice (2-nd and 4-th).
Therefore I assume next thing: for some reason sometimes by destroying one of your enemies your another enemy loses(?) its sword, so when you Invoke Death it gets called, but it throws an error here:
Item iog = Instantiate(knifeItem.selfPrefab, transform.position, transform.rotation).GetComponent<Item>();
Where it should create prefab of your sword therefore the Destroying never reaches since code stops.
You can test it out by commenting out for a time the whole iog section in OnDeath() and I assume you will not encounter the issue.
I hope it will help you.
Thank you so much for your detailed answer, it really helped me. I'll be away from my computer today, but once I'm back I'll try what you suggested.
Thanks, once again.
Hey! I commented all the item dropping section of the code out. It definitely reduced how often those undead enemies would show up. But, for some reason, they still are, but they're not invincible anymore, I'm now able to kill them. Weird. I'll have to leave it as it is, otherwise I won't have time to do everything I have to. But thank you for you help!
Hi!
Since you have limited time I would suggest then to make your code more safe in terms of execution, for example, when you are instantiating iog, you can simply do this:
if(knifeItem != null)
And only in that case create prefab. Similar to all other stuff related to the refrences, so in case if something went missing it will not crash leaving the rest of the code unexecuted, but simply will not execute certain part of the code and move on.
Good Idea. I thought about writing something along the lines of if (!knifeItem) Destroy(gameObject); but that seems better. Thanks :)
Answer by DevGa_me · May 27, 2020 at 05:23 AM
You can try instantiating particles not with transform but with transform.position with quaternion.identity.
It can be because when a gameobject is destroyed, info about transform is lost, but its position is still there untill all of the behaviour attached to that object is executed.
Hm, I don't know if I posted the code here, but the way I handle particles is by "Destroy(Instantiate(particles, pos, rot), 1);". So the particles are destroyed after a second. And one of the first solutions I tried was to create a "ForceDestroy" coroutine as such: IEnumerator ForceDestroy() { while (gameObject) { Destroy(gameObject); yield return null; } } Even then, it didn't destroy the object :/
You have instantiated particles using transform, instantiate them using trasform.position with quaternion.identity. see line 45 in the first script.
Well, I need it to be transform. transform, in that case, means that the particles will be instantiated as a child of that gameObject, an thus that the particles will have a local position relative to that gameObject. I need that, otherwise the particles will be left behind as the object moves.
You can instantiate particle and then parent them