Search Unity

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

Discussion in 'Scripting' started by larrypickle, Aug 1, 2020.

  1. larrypickle

    larrypickle

    Joined:
    Oct 8, 2019
    Posts:
    8
    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
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Projectile : MonoBehaviour
    6. {
    7.     public float speed = 8f;
    8.     public int damage = 5;
    9.     // Start is called before the first frame update
    10.  
    11.     //2 ways of handling public classes from other scripts
    12.     //plugging in the public game object and using get component to access the script
    13.     //public GameObject player;
    14.  
    15.     //OR accessing the script and then accessing the classes inside that script
    16.     public Player_Combat PlayerCombat;
    17.     public EnemyCombat enemy_Combat;
    18.  
    19.     public GameObject thisObject;
    20.     public bool Player;
    21.  
    22.  
    23.     void Update()
    24.     {
    25.         //moves the projectile forward
    26.         transform.position += (transform.forward) * speed * Time.deltaTime;
    27.     }
    28.  
    29.     //triggers damage on hit
    30.     private void OnTriggerEnter(Collider other)
    31.     {
    32.         Debug.Log("HIT");
    33.         if (other.gameObject.tag == "Player")
    34.         {
    35.             //player.GetComponent<Player_Combat>().PlayerTakeDamage(2);
    36.             PlayerCombat.PlayerTakeDamage(damage);
    37.             thisObject.SetActive(false);
    38.  
    39.         }
    40.  
    41.         else if (other.gameObject.tag == "Enemy")
    42.         {
    43.             Debug.Log("enemy hit");
    44.             thisObject.SetActive(false);
    45.             enemy_Combat.EnemyTakeDamage(damage);
    46.  
    47.         }
    48.  
    49.  
    50.     }
    51. }
    52.  
    EnemyCombat script
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EnemyCombat : MonoBehaviour
    6. {
    7.     public GameObject EmpathySphere;
    8.     public GameObject HateSphere;
    9.     public Transform EnemyPosition;
    10.     public bool canFire;
    11.  
    12.  
    13.     //maybe have an enemymanager script that handles this stuff later
    14.     //enemy stats
    15.     public int enemyMaxHealth = 20;
    16.     public int enemyCurrentHealth;
    17.     public int enemyStartingHealth = 20;
    18.     private int enemyMinHealth = 0;
    19.     public float shootSpeed = 2.5f; //how many seconds between shots
    20.  
    21.     public HealthBarScript healthBar;
    22.  
    23.     // Start is called before the first frame update
    24.     void Start()
    25.     {
    26.         enemyCurrentHealth = enemyStartingHealth;
    27.         healthBar.SetMaxHealth(enemyMaxHealth);//once ur health reaches 100 and your opponents reaches 100 u win
    28.         healthBar.SetHealth(enemyStartingHealth);
    29.  
    30.         StartCoroutine(RandomFire());
    31.  
    32.     }
    33.     // Update is called once per frame
    34.     void Update()
    35.     {
    36.  
    37.         if (canFire == true && enemyCurrentHealth != enemyMaxHealth)
    38.         {
    39.             //choosing random ability
    40.             int randomNumber = Random.Range(1, 6);
    41.             Debug.Log("randomNumber: " + randomNumber);
    42.             Move(randomNumber);
    43.  
    44.             if (randomNumber < 4)
    45.             {
    46.                 ShootHateSphere();
    47.  
    48.             }
    49.  
    50.             else if (randomNumber >= 4)
    51.             {
    52.                 //Debug.Log("empathy sphere");
    53.                 ShootEmpathySphere();
    54.  
    55.             }
    56.         }
    57.  
    58.  
    59.     }
    60.  
    61.     //coroutine to fire bullets in random interval
    62.     IEnumerator RandomFire()
    63.     {
    64.         yield return new WaitForSeconds(Random.Range(0.5f, shootSpeed));
    65.         canFire = true;
    66.  
    67.     }
    68.  
    69.     public void EnemyTakeDamage(int damage)
    70.     {
    71.         if(enemyCurrentHealth != enemyMinHealth)
    72.         {
    73.             enemyCurrentHealth -= damage;
    74.             healthBar.SetHealth(enemyCurrentHealth);
    75.         }
    76.      
    77.     }
    78.  
    79.     public void ShootHateSphere()
    80.     {
    81.         //Instantiate(HateSphere, EnemyPosition.position, EnemyPosition.rotation);
    82.  
    83.         //using singleton allows me to access objectpooler just through objectpooler.instance instead of having to establish the class in this script first
    84.         ObjectPooler.Instance.SpawnFromPool("EnemyHateSphere", EnemyPosition.position, EnemyPosition.rotation);
    85.  
    86.         canFire = false;
    87.  
    88.         StartCoroutine(RandomFire());
    89.  
    90.     }
    91.  
    92.     public void ShootEmpathySphere()
    93.     {
    94.         ObjectPooler.Instance.SpawnFromPool("EnemyEmpathySphere", EnemyPosition.position, EnemyPosition.rotation);
    95.  
    96.         canFire = false;
    97.  
    98.         StartCoroutine(RandomFire());
    99.     }
    100.  
    101.     public void Move(int direction)
    102.     {
    103.         //maybe find another way to do this or move to another file as this will vary from enemy to enemy
    104.         switch (direction)
    105.         {
    106.             case 1:
    107.                 this.transform.position = new Vector3(3, 1.5f, 9);
    108.                 break;
    109.             case 2:
    110.                 this.transform.position = new Vector3(0, 1.5f, 9);
    111.                 break;
    112.             case 3:
    113.                 this.transform.position = new Vector3(-3, 1.5f, 9);
    114.                 break;
    115.             case 4:
    116.                 this.transform.position = new Vector3(-6, 1.5f, 9);
    117.                 break;
    118.             case 5:
    119.                 this.transform.position = new Vector3(6, 1.5f, 9);
    120.                 break;
    121.    
    122.         }
    123.     }
    124. }
    125.  
    Here's the Player_Combat script that doesn't have any errors when accessed by the Projectile Script

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Player_Combat : MonoBehaviour
    6. {
    7.     public int playerMaxHealth = 20;
    8.     public int playerCurrentHealth;
    9.     public int playerStartingHealth = 10;
    10.     private int playerMinHealth = 0;
    11.     public float ShootSpeed = 1.5f;
    12.  
    13.     bool canFire = true;
    14.     public Transform PlayerPosition;
    15.  
    16.     public HealthBarScript healthBar; //reference to the public class health bar script
    17.     // Start is called before the first frame update
    18.     void Start()
    19.     {
    20.         playerCurrentHealth = playerStartingHealth;
    21.         healthBar.SetMaxHealth(playerMaxHealth);//once ur health reaches 100 and your opponents reaches 100 u win
    22.         healthBar.SetHealth(playerStartingHealth);
    23.  
    24.         StartCoroutine(SetFire());
    25.  
    26.     }
    27.  
    28.     // Update is called once per frame
    29.     void Update()
    30.     {
    31.         if (canFire == true)
    32.         {
    33.             //choosing random ability
    34.             PlayerEmpathySphere();
    35.  
    36.             //Debug.Log("empathy sphere");
    37.  
    38.  
    39.         }
    40.     }
    41.  
    42.     public void PlayerTakeDamage(int damage)
    43.     {
    44.         if(playerCurrentHealth != playerMinHealth)
    45.         {
    46.             playerCurrentHealth -= damage;
    47.             healthBar.SetHealth(playerCurrentHealth);
    48.         }
    49.      
    50.     }
    51.  
    52.     void PlayerEmpathySphere()
    53.     {
    54.         ObjectPooler.Instance.SpawnFromPool("PlayerEmpathySphere", PlayerPosition.position, PlayerPosition.rotation);
    55.  
    56.         canFire = false;
    57.  
    58.         StartCoroutine(SetFire());
    59.     }
    60.  
    61.     IEnumerator SetFire()
    62.     {
    63.         yield return new WaitForSeconds(ShootSpeed);
    64.         canFire = true;
    65.  
    66.     }
    67.  
    68.  
    69. }
    70.  
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    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.
     
  3. larrypickle

    larrypickle

    Joined:
    Oct 8, 2019
    Posts:
    8
    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
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    Well normally you would do something like this:

    Code (CSharp):
    1.     private void OnTriggerEnter(Collider other)
    2.     {
    3.         Debug.Log("HIT");
    4.         if (other.gameObject.CompareTag("Player"))
    5.         {
    6.             Player_Combat pc = other.GetComponent<Player_Combat>();
    7.             pc.PlayerTakeDamage(damage);
    8.             gameObject.SetActive(false);
    9.         }
    10.         else if (other.gameObject.CompareTag("Enemy"))
    11.         {
    12.             Debug.Log("enemy hit");
    13.             gameObject.SetActive(false);
    14.             EnemyCombat ec = other.GetComponent<EnemyCombat>();
    15.             ec.EnemyTakeDamage(damage);
    16.         }
    17.     }
    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.
     
    larrypickle likes this.
  5. larrypickle

    larrypickle

    Joined:
    Oct 8, 2019
    Posts:
    8
    Thanks so much! Idk what the problem was but trying it your way seemed to work haha.