简体   繁体   中英

In Unity2D, GameObject gets instantiated but does not trigger what it is supposed to do when collided with (sometimes)

I have a health GameObject that has a chance to get instantiated when destroying an enemy. Most of the time, it heals the player, plays the sound effect, etc. In other words, it behaves the way it is supposed to. However, sometimes (maybe 1 out of 10 times) when running into the instantiated object, it will play the sound effect but will not heal the player and it will not get destroyed. The error that accompanies this is:

MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object. UnityEngine.Object.Internal_InstantiateSingle (UnityEngine.Object data, Vector3 pos, Quaternion rot) UnityEngine.Object.Instantiate (UnityEngine.Object original, Vector3 position, Quaternion rotation) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.bindings.cs:211) UnityEngine.Object.Instantiate[GameObject] (UnityEngine.GameObject original, Vector3 position, Quaternion rotation) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.bindings.cs:285) Heart.OnTriggerEnter2D (UnityEngine.Collider2D collision) (at Assets/Scripts/Heart.cs:36)

I've been trying to figure out for quite some time what is happening and I can't seem to pinpoint the issue. Under the health up object where the error is pointing to is (error is actually pointing to the line where the tempPickupEffect is getting instantiated):

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.name == "Player_Ship")
    {
        AudioSource.PlayClipAtPoint(receiveSound, Camera.main.transform.position, 0.5f);

        GameObject tempPickupEffect = Instantiate(pickupEffect, transform.position, transform.rotation) as GameObject;
        tempPickupEffect.GetComponent<ParticleSystem>().Play();
        Destroy(tempPickupEffect, 3f);

        heal = true;
        Level1.playerHealth += healAmount;

        Destroy(gameObject);
    }
}

Under my enemy script, I have a method named spawnHealthUp() being called when the enemy's health is at or below zero. This is the method:

private void spawnHealthUp()
{
    chance = Random.value;
    if (chance <= healthSpawnChance)
    {
        GameObject copyHealthUp = Instantiate(healthUp, transform.position, Quaternion.identity) as GameObject;
        copyHealthUp.GetComponent<Rigidbody2D>().velocity = new Vector2(-healthSpeed, 0f);
    }
}

Thank you in advance for any insight into this anyone can offer.

Quite a few things can be happening in your case, but I would say that either your "pickupEffect" variable gets invalidated somehow, or this is happening because of the call to:

tempPickupEffect.GetComponent<ParticleSystem>().Play();

I'll break down both cases in more details below.

First, you can add a check to see if the "pickupEffect" variable is being set no null or some script is destroying that GameObject/prefab (by calling Destroy() on it). Add this check right before you call Instantiate(pickupEffect, ...) inside your OnTriggerEnter2D(...) method. This can be done by comparing pickupEffect == null (the == operator is overriden and will check if the GameObject/prefab has been destroyed, as per sugested in this answer ).

If the object is getting destroyed, a simple way to detect where it is being destroyed is by doing the following: create a small script containing the OnDisable() function, and add a Debug.Log() call with any text on it. The Debug.Log() call will log your text, and will also add a stack trace to the log message, which you can use to verify where the call to the Destroy() function has been made (and thus discover where your GameObject is being destroyed).

// This script should be added to the "pickupEffect" gameobject
void OnDisable()
{
    Debug.Log("Object destroyed!");
}

When te object gets destroyed, an output similar to the following will be generated:

Object destroyed!
UnityEngine.Debug:Log(Object)
MyObjectBeingDeleted:OnDisable() (at
Assets/MyObjectBeingDeleted.cs:10) UnityEngine.Object:Destroy(Object)
MyDestroyerObject:Update() (at Assets/MyDestroyerObject.cs:11)

The stack trace above tells me that the Destroy() method has been called on line 11 of the MyDestroyerObject.cs script.


On the other hand, the problem can actually be in this part of your code:

tempPickupEffect.GetComponent<ParticleSystem>().Play();
Destroy(tempPickupEffect, 3f);

Here you are commanding the ParticleSystem to play, but right after you emit that command, you are destroying the GameObject which contains that Particle System. This is actually a bad practice (and might eventually cause exceptions that are hard to debug). I recommend you to create a script with an Update() method which checks if the Particle System has ended its execution, and destroys the object after that execution has ended.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM