[RESOLVED] Object reference not set to an instance of an object

I have been trying to debug this for a while now but can’t figure out for the life of me why this error is occurring.

The error occurs at line 45 when trying to use the method EnemyTakeDamage which is in another script EnemyCombat. The error says:
NullReferenceException: Object reference not set to an instance of an object
Projectile.OnTriggerEnter (UnityEngine.Collider other) (at Assets/Projectile.cs:45)

What’s weird about this is that I don’t have this problem for the script Player_Combat and accessing the PlayerTakeDamage method which are pretty identical to EnemyCombat and EnemyTakeDamage method. If anyone could point out any errors they see, that would be helpful as I cannot see anything wrong. Thank you in advance!

Projectile Script where the error occurs on line 45

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Projectile : MonoBehaviour
{
    public float speed = 8f;
    public int damage = 5;
    // Start is called before the first frame update

    //2 ways of handling public classes from other scripts
    //plugging in the public game object and using get component to access the script
    //public GameObject player;

    //OR accessing the script and then accessing the classes inside that script
    public Player_Combat PlayerCombat;
    public EnemyCombat enemy_Combat;

    public GameObject thisObject;
    public bool Player;
 

    void Update()
    {
        //moves the projectile forward
        transform.position += (transform.forward) * speed * Time.deltaTime;
    }

    //triggers damage on hit
    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("HIT");
        if (other.gameObject.tag == "Player")
        {
            //player.GetComponent<Player_Combat>().PlayerTakeDamage(2);
            PlayerCombat.PlayerTakeDamage(damage);
            thisObject.SetActive(false);

        }

        else if (other.gameObject.tag == "Enemy")
        {
            Debug.Log("enemy hit");
            thisObject.SetActive(false);
            enemy_Combat.EnemyTakeDamage(damage);

        }


    }
}

EnemyCombat script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyCombat : MonoBehaviour
{
    public GameObject EmpathySphere;
    public GameObject HateSphere;
    public Transform EnemyPosition;
    public bool canFire;


    //maybe have an enemymanager script that handles this stuff later
    //enemy stats
    public int enemyMaxHealth = 20;
    public int enemyCurrentHealth;
    public int enemyStartingHealth = 20;
    private int enemyMinHealth = 0;
    public float shootSpeed = 2.5f; //how many seconds between shots

    public HealthBarScript healthBar;

    // Start is called before the first frame update
    void Start()
    {
        enemyCurrentHealth = enemyStartingHealth;
        healthBar.SetMaxHealth(enemyMaxHealth);//once ur health reaches 100 and your opponents reaches 100 u win
        healthBar.SetHealth(enemyStartingHealth);

        StartCoroutine(RandomFire());

    }
    // Update is called once per frame
    void Update()
    {

        if (canFire == true && enemyCurrentHealth != enemyMaxHealth)
        {
            //choosing random ability
            int randomNumber = Random.Range(1, 6);
            Debug.Log("randomNumber: " + randomNumber);
            Move(randomNumber);

            if (randomNumber < 4)
            {
                ShootHateSphere();

            }

            else if (randomNumber >= 4)
            {
                //Debug.Log("empathy sphere");
                ShootEmpathySphere();

            }
        }


    }

    //coroutine to fire bullets in random interval
    IEnumerator RandomFire()
    {
        yield return new WaitForSeconds(Random.Range(0.5f, shootSpeed));
        canFire = true;

    }

    public void EnemyTakeDamage(int damage)
    {
        if(enemyCurrentHealth != enemyMinHealth)
        {
            enemyCurrentHealth -= damage;
            healthBar.SetHealth(enemyCurrentHealth);
        }
     
    }

    public void ShootHateSphere()
    {
        //Instantiate(HateSphere, EnemyPosition.position, EnemyPosition.rotation);

        //using singleton allows me to access objectpooler just through objectpooler.instance instead of having to establish the class in this script first
        ObjectPooler.Instance.SpawnFromPool("EnemyHateSphere", EnemyPosition.position, EnemyPosition.rotation);

        canFire = false;

        StartCoroutine(RandomFire());

    }

    public void ShootEmpathySphere()
    {
        ObjectPooler.Instance.SpawnFromPool("EnemyEmpathySphere", EnemyPosition.position, EnemyPosition.rotation);

        canFire = false;

        StartCoroutine(RandomFire());
    }

    public void Move(int direction)
    {
        //maybe find another way to do this or move to another file as this will vary from enemy to enemy
        switch (direction)
        {
            case 1:
                this.transform.position = new Vector3(3, 1.5f, 9);
                break;
            case 2:
                this.transform.position = new Vector3(0, 1.5f, 9);
                break;
            case 3:
                this.transform.position = new Vector3(-3, 1.5f, 9);
                break;
            case 4:
                this.transform.position = new Vector3(-6, 1.5f, 9);
                break;
            case 5:
                this.transform.position = new Vector3(6, 1.5f, 9);
                break;
    
        }
    }
}

Here’s the Player_Combat script that doesn’t have any errors when accessed by the Projectile Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player_Combat : MonoBehaviour
{
    public int playerMaxHealth = 20;
    public int playerCurrentHealth;
    public int playerStartingHealth = 10;
    private int playerMinHealth = 0;
    public float ShootSpeed = 1.5f;
 
    bool canFire = true;
    public Transform PlayerPosition;

    public HealthBarScript healthBar; //reference to the public class health bar script
    // Start is called before the first frame update
    void Start()
    {
        playerCurrentHealth = playerStartingHealth;
        healthBar.SetMaxHealth(playerMaxHealth);//once ur health reaches 100 and your opponents reaches 100 u win
        healthBar.SetHealth(playerStartingHealth);

        StartCoroutine(SetFire());

    }

    // Update is called once per frame
    void Update()
    {
        if (canFire == true)
        {
            //choosing random ability
            PlayerEmpathySphere();

            //Debug.Log("empathy sphere");


        }
    }

    public void PlayerTakeDamage(int damage)
    {
        if(playerCurrentHealth != playerMinHealth)
        {
            playerCurrentHealth -= damage;
            healthBar.SetHealth(playerCurrentHealth);
        }
     
    }

    void PlayerEmpathySphere()
    {
        ObjectPooler.Instance.SpawnFromPool("PlayerEmpathySphere", PlayerPosition.position, PlayerPosition.rotation);

        canFire = false;

        StartCoroutine(SetFire());
    }

    IEnumerator SetFire()
    {
        yield return new WaitForSeconds(ShootSpeed);
        canFire = true;

    }


}

enemy_Combat is null. You neglected to give it a value.

The most likely answer is that you forgot to assign a value to enemy_Combat in the inspector for your Projectile component.

It’s kind of unusual that you’re using direct references instead of just getting the PlayerCombat or enemy_Combat components from the collider you collided with though.

Hmm i have assigned it a value in the inspector so im still not sure what could be causing it.
And could you elaborate on what you mean by getting the components from the collider you collided with and what Unity functions would help me accomplish that because im new to unity coding and would definitely love to learn what the best practices are.

thanks a bunch

Well normally you would do something like this:

    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("HIT");
        if (other.gameObject.CompareTag("Player"))
        {
            Player_Combat pc = other.GetComponent<Player_Combat>();
            pc.PlayerTakeDamage(damage);
            gameObject.SetActive(false);
        }
        else if (other.gameObject.CompareTag("Enemy"))
        {
            Debug.Log("enemy hit");
            gameObject.SetActive(false);
            EnemyCombat ec = other.GetComponent<EnemyCombat>();
            ec.EnemyTakeDamage(damage);
        }
    }

You get the component from the collider you collided with. This avoids you having to set references to players and enemies ahead of time, and allows you the possibility to handle colliding with any different enemy/player etc…

Note this will only work if the Player_Combat or EnemyCombat (you should probably make the naming consistent on those…) components are directly on the object that has the collider. If it’s on a parent or child you might have to switch the GetComponent calls to GetComponentInParent or GetComponentInChildren.

I made a few other small optimizations too:

  • changed thisObject to gameObject. Unity already gives you a built in property called gameObject that refers to THIS GameObject.
  • changed tag == "tag" comparisons to CompareTag calls. CompareTag is more efficient because it avoids allocating extra memory. unity unfortunately allocates extra memory when you get the tag from a GameObject.

You can then get rid of the fields: PlayerCombat, enemy_Combat and thisObject with these changes, which simplifies things.

1 Like

Thanks so much! Idk what the problem was but trying it your way seemed to work haha.