Correct way to shoot things (Instantiate and Destroy)

Hey.
I have 2 scripts at the moment, one called Light_Shoot and one called Light_Spell.
Light_Spell does the checks for which button was pressed etc and once I press a certain button, I instantiate a LightPrefab, which is a light that moves where I am and has the Light_Shoot script attached to it.

I want the light I shoot to live for only 5 seconds, then disapear, and allow the player to shoot again. They shouldn’t be able to shoot in the 5 seconds that a light has been created.
How can I do this properly? When I destroy the object as is, and I try to shoot again, it says that the object has been destroyed so there is no reference to it.

Here is some code:

Light_Shoot

public class LightSpell_Shoot : MonoBehaviour {

    private float speed = 10.0f;
    private Vector3 direction;
    private bool noCollision;



	void Start () {
		Destroy(this.gameObject, 5);
        direction = transform.forward;
        noCollision = true;
	}
	
	// Update is called once per frame
	void Update () {
        if(noCollision)
            transform.position += (speed * Time.deltaTime) * direction;
	}

    void OnCollisionEnter(Collision collision){
        noCollision = false;
        this.light.intensity = 0.3f;
    }
}

LightSpell

        //What happens when bumper is pressed
        if (isSelectedSpell  SixenseInput.Controllers[1].GetButtonDown (SixenseButtons.BUMPER)  triggerIsPressed == false  freeToShoot) {
            ShootLight();
		}
	}

    //Handles shooting the light
    private void ShootLight(){
		if(Time.time > fireRate + lastShot){
            wandLightProjectilePrefab = Instantiate(wandLightProjectilePrefab.gameObject, transform.position, transform.parent.transform.rotation) as GameObject;
			lastShot = Time.time;
		}

    }

In general, I never use prefabs in doing bullets. I always simply attach a bullet to the barrel of the gun and make it inactive. Then I instantiate the bullet in that spot, at that rotation and make it active. If I then only want that bullet to exist for a few seconds, i put Destroy(newBullet,5) in the gun’s script.

public class GenericGun : MonoBehaviour {
	public float fireRate = 5;
	public float bulletDecay = 5;
	private float lastShot = 0;
	public GameObject bulletPrefab;

	void Start(){
		if(bulletPrefab) bulletPrefab.SetActive(false);
	}
	
	void Update(){
		if (Input.GetButtonDown("Fire1")) Shoot();
	}

	public virtual void Shoot(){
		if(!bulletPrefab) return;
		if(Time.time > fireRate + lastShot){
			GameObject bullet = (GameObject)Instantiate(bulletPrefab, bulletPrefab.transform.position, bulletPrefab.transform.rotation);
			bullet.SetActive(false);
			if(bulletDecay > 0) Destroy(bullet, bulletDecay);
			lastShot = Time.time;
		}
	}
}

Very simple creator. Duplicate the bullet on the end of the gun, make it active, and tell it to destroy it’s self. There are a few changes to this, that you will notice. I changed the name, I made gave it a prefab so you can drag and drop a bullet onto the prefab for the gun and I used a term here… Virtual. Which I will discuss later.

OK, other than that, let’s change the way you think just a little.

first, lets simply create a generic bullet. This is a simple set of instructions that can be applied to any object that moves through space and hits something.

public class GenericBullet : MonoBehaviour {
	public float damage = 1;
	public float speed = 10.0f;
	public bool useGravity = true;
	public float damper = 0; // slows the bullet down over time.
	private Vector3 gravity = Vector3.zero;
	
	void Start () {
		if(collider) Destroy(collider);
	}
	
	// Update is called once per frame
	void Update () {
		DoMovement();
	}
	
	public virtual void DoMovement(){
		speed = speed - damper * Time.deltaTime;
		if(useGravity) gravity = gravity + Physics.gravity * Time.deltaTime;
		Vector3 direction = (gravity + transform.forward * speed) * Time.deltaTime;
		
		Ray ray = new Ray(transform.position, direction);
		RaycastHit hit;
		if(Physics.Raycast(ray, out hit, direction.magnitude)){
			HitTarget(hit.collider);
		}
	}
	
	public virtual void HitTarget(RaycastHit hit){
		Destroy(gameObject);
		Debug.Log("Hit Object: " + hit.collider.ToString());
		hit.collider.transform.root.gameObject.BroadcastMessage("ApplyDamage", this.damage, SendMessageOptions.DontRequireReceiver);
	}
}

You will notice some simple things, and some things you probably haven’t thought about. First, NO COLLIDERS… Physics is great and all that, but a simple collision system is far better than letting the big world handle yet another bullet from a gun crazy super spy.

So every update, we check for the bullet’s movement and see if it hit something. If so, we simply run a command that says we hit something. Very generic.

Now, lets change some more about how you think… The term “Vritual” means that this method can be replaced later down the road. This means that you can morph this whole class into another thing all together. You will notice that the DoMovement and HitTarget methods are virtual. This means you can simply rewrite them if you desire. If you don’t you can still call them as if they were there.

Next, look at the current HitTarget method. You will notice that we first destroy the bullet, so that it wont affect anything else. Next, we use BroadcastMessage on the root of the collider. BoradcastMessage calls a method from that object, or any child in that object. So if you have a piece of glass in your scene, it will cause damage to the glass. If you have a monster, it will cause damage to the monster. and so on. All you have to have for the whole thing to work is a method on the other side called ApplyDamage. and it has to recieve a float.

Next lets morph (yes morph, as in Polymorphism, look it up, it’s all there) this whole class into a special class. One for your LightSpell_Shoot.

public class LightSpell_Shoot : GenericBullet {
	public override void HitTarget(RaycastHit hit){
		Destroy(gameObject);
		Destroy(hit.collider.transform.root.gameObject);
		
		Debug.Log("Boom!!!!");
	}
}

First, instead of MonoBehaviour, we are using GenericBullet, the class we created before. Also, notice that I don’t rewrite the update, or various other things. The GenericBullet should take care of these. I did however, provide it with a new HitTarget. This one is simple… DESTROY EVERYTHING… muahahahawwwwawwaw…

From above… you can use the GenericGun also and overwrite the Shoot, or whatever you need. They are now core classes that you use to make things. In the editor, you make a gun, want or whatever, you make the bullet it shoots, you apply base scripts, or update them to your needs make, press a button and it makes everything work.

I appreciate the long response. It helped explain a lot to me thank you.
I solved it in a different way however, by simply making a new game object called LightShot and set that to the instantiate, and in the prefab script set it to destroy after 5 seconds of being instantiated, and this worked fine. But your explanation of other things really helps out thanks man :slight_smile: