OnTriggerEnter ignores any values set after initialization

In my code below I’m trying to grab the damage that should be getting done in my OnTriggerEnter on line 65, but it always is 0 when testing. I’ve tried putting debug statements on instantiation of the game object that the collider is on (Line 32 setting damage, line 36 instantiation), and the correct value is printed before the collider value of 0. Any attempt to reference a field set in code returns a null reference exception as well so I’m not sure what is going on.

Edit: Sorry added code tags. As well I can identify what is null when I get the exceptions but why there are null is the problem, logically if I am referencing a game object that has the collider responsible for my OnTriggerEnter function it should exist and have a reference to it so I’m confused there unless I’m missing something.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPG.Core;
using RPG.Combat;

namespace RPG.SpellCasting
{
    public class SpellCastController : MonoBehaviour
    {
        ///private Animator spellAnim;
        private SpellData spellToCast;
        private GameObject instantiatedSpell;
        private ParticleSystem ps;
        //getting reset to default value when OnTriggerEnter is called, why?
        private float spellDamage = 0f;
        private float maxPSDuration = 0;

        private void GetMaxDuration()
        {
            foreach(var _ps in spellToCast.spellPrefab.GetComponentsInChildren<ParticleSystem>())
            {
                if (_ps.main.duration > maxPSDuration) maxPSDuration = _ps.main.duration;
                _ps.loop = false;
            }
        }

        public void SetSpellAndCast(SpellData spell, Vector3 castPoint, Quaternion rotation)
        {
            spellToCast = spell;
           
            GetMaxDuration();
            ps = spell.spellPrefab.GetComponentInChildren<ParticleSystem>();
            StartCoroutine();

            instantiatedSpell = Instantiate(spell.spellPrefab.gameObject, castPoint, rotation);
            spellDamage = spellToCast.damage;
            var collider = spellToCast.spellPrefab.GetComponent<Collider>();
            collider.enabled = true;
            ps.Play();

            StartColliderCoroutine(collider);
            Destroy(instantiatedSpell, maxPSDuration);
        }

        private IEnumerator StartCoroutine()
        {
            yield return new WaitForSecondsRealtime(5);
        }

        public IEnumerator StartColliderCoroutine(Collider collider)
        {
            yield return new WaitForSecondsRealtime(spellToCast.colliderDurationTime);
            //collider.enabled = false;
           // Debug.Log("In coroutine:" + collider.enabled);
        }

        //just in case
        public void DestroySpellInstance(GameObject toDestroy, float timeToRemain = 0f)
        {
            Destroy(toDestroy, timeToRemain);
        }

        public void OnTriggerEnter(Collider other)
        {
            //TODO: Add check for if enemy

            if (!other.gameObject.GetComponent<CombatTarget>()) return;
            var target = other.gameObject.GetComponent<Health>();
            if (target.IsDead()) return;
            Debug.Log(spellDamage);
            target.TakeDamage(spellDamage);
        }
    }
}

First fix the nullref.

How to fix a NullReferenceException error

https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

Three steps to success:

  • Identify what is null ← any other action taken before this step is WASTED TIME
  • Identify why it is null
  • Fix that

In the future if you post code, please ALWAYS USE CODE TAGS:

How to use code tags: Using code tags properly

It sounds like you know what is null, but not why. But I didn’t see you share what you thought was null, so it’s hard to help you solve the why.

But one thing I do notice is you tried to set up two coroutines, but there are two issues. The first is, you don’t call these coroutines correctly. You even named one StartCoroutine, which is a bad name to give a coroutine since that is how you start one. I would suggest looking up how to properly start a coroutine before going further.

The next thing is, you seem to be using the coroutines thinking they will add a pause to your method. They don’t work like that. They don’t just insert a pause in the method that calls them. So your entire SetSpellAndCast method will run through and not pause like you think it would. Now, this might be the reason you are running into issues, but I can’t say for sure. Either way, you might need to just make the entire thing a coroutine and instead of calling coroutines, just have the yield calls right inside.

Sorry if this isn’t clear, so the coroutines are related to something separate and I’ve removed them for testing and it gives the same result so for this issue please ignore the. But on that note thank you I actually had been looking into that issue as well and noticed I had implemented them incorrectly and will refactor to use a timer that I’ve used in other project.

As to the null, the why is what is confusing. Because everything I try to call in the OnTriggerEnter method is null. For example I refactored my code so that lines 65 to 74 instead reference the instantiated spell and prints it to the log but it also gives null.

Sorry if something isn’t clear or requires more detail/examples/pictures please let me know and I will add them

public void OnTriggerEnter(Collider other)
        {
            //TODO: Add check for if enemy

            if (!other.gameObject.GetComponent<CombatTarget>()) return;
            var target = other.gameObject.GetComponent<Health>();
            if (target.IsDead()) return;
            Debug.Log(instantiatedSpell);
            target.TakeDamage(spellDamage);
        }

Line 5 above asks for a CombatTarget and exits if there isn’t one.

Line 6 gets a Health (into a confusingly named variable called target) and line 7 uses it blindly.

At a minimum, start with step 1 and find what is null. This is such a trivial problem for it to take more than 60 seconds to solve is just a terrible waste of time and resources.

On line 9. The game object instantiateSpell is null when I try to access a field on it to pass to the method TakeDamage. As to why this is null is what confuses me, the OnTriggerEnter method treats it as I never set it. From my understanding the order of events should be:

  1. I’ve cast a spell that contains the spell data in another method and it passes here to the SetSpellAndCast method

  2. The spell is then instantiated using the data passed and set to be destroyed after a certain time. (**Note, setting it to be destroyed is probably not the cause as I’ve removed that line of code and nothing changes)

  3. If i’ve hit an appropriate target with the instantiated spell, the damage that I’ve set on my SpellData object should be passed into the TakeDamage function that is on the target’s Health component.

Hopefully this will make things much clearer, I’ve renamed other components to make things hopefully a little clearer as I understand the logic is very messy especially without the context of the full project etc.

    public void OnTriggerEnter(Collider other)
            {
     
                if (!other.gameObject.GetComponent<TargetableEnemy>()) return;
                var enemyHit= other.gameObject.GetComponent<Health>();
                if (enemyHit.IsDead()) return;
                enemyHit.TakeDamage(instantiatedSpell.spellDamage);
            }

Originally, instantiatedSpell was a variable type GameObject. Now I assume you’ve changed it to be whatever the spell component is? And spellDamage is a float or int I assume?

If instantiatedSpell is null in OnTriggerEnter, then you need to go back to where you set it’s value and see what is going on there.

1 Like

Thanks for this, it was a good hint to the issue, and yes spellDamage is a float.

I’ve found that basically the instantiatedSpell gameobject is taking over the OnTriggerEnter function, to clarify that.

instantiatedSpell.GetInstanceID() and gameObject.GetInstanceID() inside of my SetSpellAndCast method give 2 different results.

But instantiedSpell.GetInstanceID() and gameObject.GetInstanceID() inside of my OnTriggerEnter method give the same ID.

I’m not sure why this is the case but I’ve clearly messed up the structure of this process and will need to refactor it.

I greatly appreciate all the advice