1
votes

I am new to using unity and I am still learning C#. So pls bear with me if the problem mentioned below may seem a little odd or easy to fix.

I am creating a project in order to try to shoot a bullet from a turret, and I have included a function in my bullet script that will destroy the bullet after it has crossed certain boundaries and a function in my bulletSpawner script to Instantiate the bullet if it is destroyed. For some reason whenever I play and shoot the bullet and it crosses the boundary, it doesn't get cloned

Here is the Bullet script;

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

public class Bullet : MonoBehaviour
{
    [SerializeField]
    private float Power = 50f;

    private Rigidbody2D myBody;

    private SpriteRenderer sr;

    private float posX;

    private float posY;

    private void Awake()
    {
        myBody = GetComponent<Rigidbody2D>();
        sr = GetComponent<SpriteRenderer>();
    }

    void Update()
    {
        Shoot();
        destroyBullet();
    }

    void Shoot()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
           myBody.AddForce(new Vector2(Power, Power - myBody.gravityScale), 
           ForceMode2D.Impulse);
        }
    }

    void destroyBullet()
    {
        posX = transform.position.x;
        posY = transform.position.y;
        if (posX > 100 || posX < -100 || posY > 100 || posY < -100)
            Destroy(gameObject);
    }
}//class

Here is the BulletSpawner script

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

public class BulletSpawner : MonoBehaviour
{
    public Transform bullet; 

    void Update()
    {
        StartCoroutine(SpawnBullet());
    }

    IEnumerator SpawnBullet()
    {
        while (!GameObject.FindWithTag("Bullet"))
        {
            yield return new WaitForSeconds(3);

            Instantiate(bullet);

        }//while
    }//Ienum        
}//class

Note: I have attached my Bullet Prefab to bullet in the inspector panel

Whenever the bullet is out of bounds, or gets destroyed, I get this error:

MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.

I know it has been destroyed but I want to find a way to access it so that it can clone the bullet(which can be fired again, destroyed and so on...)

Please advise on what I can do to fix this code, or any alternatives in order to prevent such errors from happening in the future. Any feedback would be appreciated :) ...

Thanks in advance!

1
Where exactly is your exception thrown from? Could you include the stacktrace? Also !attention! you do not want to start a new Coroutine every frame in Update!derHugo
And another question: why is the Shoot method on the bullet itself? Shouldn't it rather be on the spawner and you shoot/instantiate a bullet when pressing space?derHugo
And it sounds like as the prefab to be instantiated you used the original bullet instance from the scene -> don't! You rather want to use an actual prefab asset .. otherwise you can't instantiate something that has been destroyed ;)derHugo
Consider recycling your rapidly recreated objects rather than re-spawning them. It's nicer to the memory heap and will prove more efficient when the player gets that gattling cannon upgrade. Simply move that bullet that went out of frameMickyD
Yes because now we know for sure that the error is that bullet is destroyed and you try to use it as parameter for Instantiate .. which means as said you are using an actual scene object as original but you rather want to create a prefab from it and use that one insteadderHugo

1 Answers

1
votes

So as already mentioned the main issue was you used the original scene instance as original bullet for the Instantiate but then destroyed that original.

If you are going to use destroy and instantiate you rather want a prefab asset as bullet original.

However, after also what MickyD mentioned: You will always have one single bullet at a time anyway so you probably don't have to destroy it at all. Simply reuse the very same bullet instance.

And then regarding the Shoot and your Coroutine in Update this is how I would rather do it

The bullet itself doesn't do anything at all and is rather only passive.

And then do

public class BulletSpawner : MonoBehaviour
{
    // Now again the one from the Scene!
    public Rigidbody2D bullet; 

    // Yes if Start is IEnumerator it is automatically run as Coroutine by Unity
    IEnumerator Start ()
    {
        // Disable the Rigidbody of the bullet so it doesn't fall down
        bullet.enabled = false;

        // Huh?! This is okey in an IEnumerator as long as you yield inside
        while(true)
        {
            // wait until Space is pressed
            yield return new WaitUntil (() => Input.GetKeyDown(KeyCode.Space));

            // enable the Rigidbody
            bullet.enabled = true;
            // shoot the bullet
            bullet.AddForce(new Vector2(Power, Power - bullet.gravityScale), ForceMode2D.Impulse);

            // Now wait until it is out of bounds
            //TODO: .. or it hits something ;)
            yield return new WaitUntil (() => bullet.position.magnitude > 100f /*TODO || hasHitSomething */);

            // Then reset it's movement
            bullet.velocity = Vector2.zero;
            bullet.angularVelocity = 0;
            // Reset position
            bullet.position = Vector2.zero;
            bullet.rotation = 0;
            // disable the Rigidbody for the next iteration
            bullet.enabled = false;

            // Disable the entire object
            bullet.gameObject.SetActive(false);

            // Wait for the cooldown
            yield return new WaitForSeconds (3);

            // enable the object (but not yet the Rigidbody)
            bullet.gameObject.SetActive(true);
        }
    }
}

for checking the hasHitSomething I would put a dedicated component on the bullet like e.g.

public class Bullet : MonoBehaviour
{
    // Basically simply forward your OnCollisionEnter2D to an event
    // so others can listen to it and react
    public UnityEvent<Collision2D> OnCollided;

    private void OnCollisionEnter2D(Collision2D collision)
    {
        OnCollided.Invoke(collision);
    }
}

and then in the BulletSpawner add

private void HandleCollision(Collision2D collision)
{
    // maybe add some filters

    hasCollided = true;
}

bool hasHitSomething;

 IEnumerator Start ()
 {
     // Hook up ONCE to the event in the beginning
     bullet.GetComponent<Bullet>().OnCollided.AddListener(HandleCollision);

     ...

         // Wait until one of the conditions is true
         WaitUntil (() => bullet.position.magnitude > 100f || hasHitSomething);

         // reset the flag along with the bullet values
         hasHitSomething = false;

     ...