I am a beginner in Unity C#. I was trying make a scene where when I press “FIRE1” the gun will shoot a bullet prefab which will be in the Assets (not in the scene, kind of from an Empty gameObject from the barrel of the gun ) Then if the bullet collides with a “Bulb” in the scene the Health of the bulb will be reduced and if the health reaches 0 it will get destroyed! But I am stuck at this error :
NullReferenceException: Object reference not set to an instance of an object
Damager.OnCollisionEnter (UnityEngine.Collision col) (at Assets/iMadeScripts/Damager.cs:14)
This script is attached to the Bulb:
DT.cs
using UnityEngine;
using System.Collections;
public class DT : MonoBehaviour
{
public float speed = 5;
public float hp = 100;
private Light lit;
// Use this for initialization
void Start ()
{
lit = GetComponent<Light>();
}
// Update is called once per frame
void Update ()
{
if(Input.GetKey(KeyCode.RightArrow))
{
transform.position += new Vector3(speed * Time.deltaTime,0,0) ;
}
if(Input.GetKey(KeyCode.L))
{
lit.enabled = true;
}
if(hp <= 0)
{
Destroy(gameObject,2f);
}
}
/*void OnMouseDrag()
{
count--;
Debug.Log("Count = " +count);
if(count == 0)
{
GetComponent<Light>().enabled = true;
}
}*/
}
Attached to the Bullet Prefab:
Damager.cs
using UnityEngine;
using System.Collections;
public class Damager : MonoBehaviour
{
DT damager;
void OnCollisionEnter(Collision col)
{
if(col.gameObject.name == "Bulb" )
{
//Destroy(col.gameObject);
damager.hp--;
}
}
}
Follow my Script I will try to explain it in comments:
using UnityEngine;
using System.Collections;
public class Damager : MonoBehaviour
{
public GameObject enemy;
public int damage;
EnemyHealth enemyHealth; //you will need to reaname EnemyHealth to your scripts name. but the variable name can be anything you like.
void Awake ()
{
enemy = GameObject.FindGameObjectWithTag ("Enemy");
enemyHealth = enemy.GetComponent <EnemyHealth> ();
}
void OnTriggerEnter(Collider other)
{
if (other.tag == "Enemy") {
// bullet looks for the EnemyHealth Script
other.GetComponent<EnemyHealth> ();
// bullet calls the function in the EnemyHealth Script and takes away the amount you will set in the inspector as Damage
enemyHealth.TakeDamage (damage);
// bullet Destorys itself
Destroy (gameObject);
}
}
}
And now you will need this function in your Enemy script that stores health variable (EnemyHealth in my case)
// ALSO REMEBER TO MAKE THE FUNCTION PUBLIC like in the line below because your Damager script will try to access it
public void TakeDamage (int amount)
{
// this will take away the amount of health you stored in the damage varible in damager script
currentHealth -= amount
}
also Remember that the damage variable from the Damager script will be stored as amount variable in the TakeDamage funciton. so you can setup different kinds of bullets and that way you can set the damage they will do to the enemy in the inspector for each bulled that has the damager script attached to it
You can try but i see OnTriggerEnter used more.(Maybe its better or something)
Well what is it?.. Its basicly the same thing but you need to tick the “Is Trigger” checkbox in your collider for it to work
And what the trigger does is basicly if something toches the collider that trigger wont make the object fly away. Its that means it will activate once you enter it but wont prevent you form going inside of it.
A good expample would be like say you need to enter the elevator and you want it to automaticly start going up once you walk in. So you can add a collider to an elevator and set it as a trigger so you can walk into that collider and insted of preventing form letting you in it will call that OnTriggerEnter function. so its also a good way to place them around the map to trigger certain event.
It worked but I didn’t understand few parts on the code. Like line 6,12,13 ( also tell me why and when to use ). And do i have to do whatever you did in line 19 whenever i want to access component of other scripts?
The enemy is a cube it has a Box Collider pre-assigned to it with no Trigger if i enable trigger the box just sinks into the ground. I had to give it an extra mesh collider ( with trigger ON ) on top of box collider.
Also if i shoot after the enemy is destroyed it shows me the same error:
NullReferenceException: Object reference not set to an instance of an object
Damager.Start () (at Assets/iMadeScripts/Damager.cs:13)
using UnityEngine;
using System.Collections;
public class Damager : MonoBehaviour
{
public GameObject enemy;
DT health;
public int damage;
void Start()
{
enemy = GameObject.FindGameObjectWithTag("Enemy");
health = enemy.GetComponent <DT>();
}
void OnTriggerEnter(Collider col)
{
if(col.tag == "Enemy" )
{
col.GetComponent<DT>();
health.TakeDamage(damage);
if(health.hp==0)
{
Destroy(health.gameObject , 3);
}
}
}
}
Well the error actualy happens because you not Destroying the bullet… so the bullet keeps flying and looks for the script in the object you just destroyed.
Is it bad for the final game ? … well it depends and maybe in your case yes it will hurt the game because if you fire a ton of bullets the bullets will never get destoryed and unity will keep allocating memory so after some time you will start to run out of memory so the game could crash or fps could drop and you wont know why.
so anyway its a good idea to fix this issue
I see you have removed the destroy(gameObject) line in my expample which destoryed the bullet.
I think you should make the cube destory itself rather than bullet. Because say you want the cube to explode once its dead so if you dont have a script that checks if its dead that that one explosion will have instantiated(spawned) itself like 8 times already while the script destroys the object
But you will need to make isDead bool variable at the begining of the script
So this is what you need to add to your script
public void TakeDamage (int amount)
{
currentHealth -= amount;
if(currentHealth <= 0 && !isDead) //so this says If enemy has no health and is not dead that we call the Death function.
{
Death ();
}
void Death ()
{
isDead = true;
Destroy (gameObject);
}
}
And the Last thing
Do you need everything I in the 12, 13 line
Well no I just tested it and it worked just fine without them.
That needed that code to use this line:
enemy.GetComponent();
instead of this line:
other.GetComponent();
which caused me the main problem. so i replaced it with the one you see in my expample
Okay! Last question then I’ll stop troubling you, whenever we create and object of a script in another script we need to assign it (like health = GetComponent
()) , right? If yes then what does it means?
Please explain that statement!
And I am still getting that error but this time a bit different:
NullReferenceException: Object reference not set to an instance of an object
Damager.Start () (at Assets/iMadeScripts/Damager.cs:33)
Its is the start function where you initialse the values. So, if I tag a static object ,which has no script attached to it, ‘Enemy’ (which is actually not an enemy its a barrel) I got no error. Is this method healthy for the final game?