Gameobject is destroyed even though I don't actively destroy it (or it's parent)

Hello guys,

I have found a weird behavior in my script where I get the follow error:

This is the stack trace:


This is How I instantiate my “destroyed” object:

 private void InitializeVitalBar(GameObject vitalsContainer, VitalType vitalType, Vector3 pos)
        {
            var bar = Resources.Load<FloatingVitalBar>($"UI/Vitals/Floating{vitalType.ToString()}");
            var barInstance = Instantiate(bar, pos, Quaternion.identity);
            Debug.Log(barInstance == null);

            barInstance.Init(_context, _playerId, vitalType);
            barInstance.transform.SetParent(vitalsContainer.transform);
        }

And this is my object:

 public class FloatingVitalBar : MonoBehaviour
    {
        private Guid _characterId;
        private UnityEngine.Camera _mainCamera;

        public Image ForeGroundHealthImage;
        public Text HealthText;
        public static FloatingDamage DamageObject;

        void Awake()
        {
            _mainCamera = UnityEngine.Camera.main;
            DamageObject = Resources.Load<FloatingDamage>("UI/FloatingDamage");
        }

        public void Init(IStateContext context, Guid characterId, VitalType vitalType)
        {
            _characterId = characterId;
            var character = context.Selector.GetCharacter(_characterId);

            Debug.Log(gameObject == null);
            VitalUiUtils.InitializeVitalBar(vitalType, character, HandleHealthUpdate);

            var vital = character.State.Vitals[vitalType];
            var healthPercent = vital.CurrentValue / vital.MaxValue;

            HealthText.text = $"{Mathf.RoundToInt(vital.CurrentValue)}/{Mathf.RoundToInt(vital.MaxValue)}";
            ForeGroundHealthImage.fillAmount = healthPercent;
        }

        void LateUpdate()
        {
            transform.LookAt(_mainCamera.transform);
            transform.Rotate(0, 180, 0);
        }

        private void HandleHealthUpdate(float healthPercent, float curValue, float maxValue, float damage)
        {
            Debug.LogWarning("HEALTH " + HealthText == null);
            Debug.LogWarning("HEALTH IMAGE" + ForeGroundHealthImage == null);
            //Debug.LogWarning("gameObject " + gameObject == null);
            StartCoroutine(VitalUiUtils.UpdateBar(ForeGroundHealthImage, HealthText, healthPercent, curValue, maxValue));

            // set the damage transform to follow the player
            var instance = Instantiate(DamageObject, transform.parent.position, Quaternion.identity, transform.parent.parent);

            // set the damage text to the calculated damage
            instance.GetComponent<TextMeshPro>().text = Math.Ceiling(damage).ToString();
        }

        private void OnDestroy()
        {
            Debug.LogWarning("DDDDDDDDDDDDDESSSTROY");
        }
    }
}

OnDestroy does not print anything prior to the above exception it is only being called once I stop the editor

Any help is welcome :slight_smile:

So here is the cause of the problem:

public static void InitializeVitalBar(VitalType vitalType, ICharacter character, Action<float, float,float, float> updateMethod)
        {
            character.OnVitalModified += (vitalT, healthPercent, curValue, maxValue, change) =>
            {
                if (vitalType != vitalT)
                    return;
                updateMethod(healthPercent, curValue, maxValue, change);
            };
        }

The above method (found in the attached stack-trace) registers my character to an object that is ultimately destroyed.
I solved the problem by making my character Interface also IDisposable and when I destroy his gameObject I carefully set all of the related events on the character to null.

It’s hard to tell what’s wrong from just looking at this, but here’s a few things you can try. Note that none of it really makes sense if the object is truly never destroyed, so I’m going to work off the assumption that it is being destroyed, but in a way that’s not immediately apparent.

  1. Call StopAllCoroutines() in OnDestroy(). I have a feeling the stack trace mentions that StartCoroutine call because it’s the closest it can get to actually telling you that the issue happened during a frame’s invocation of the coroutine. I’m wondering if it’s still going after the health bar is destroyed.

  2. Comment out that coroutine and see if you still get the error.

  3. Comment out the inside of the HandleHealthUpdate method (or just add return at the top) and see if you still get the error. If so, then the issue is with another script holding a reference to HandleHealthUpdate after it’s been destroyed. It seems like you ruled this out already but this test would help us know definitively.

If that doesn’t help, could you explain a bit more when exactly this happens? From the stack trace, it seems to happen after the character takes damage. But is it only when the damage causes the character to be destroyed?