How do I begin assigning an array from element 0 again after clearing?

I’m trying to make a shoot’em up, galaga-clone type game because it seemed like a simple enough starting point after the unity tutorial courses, but am running into trouble when switching out gun patterns.

Here is the code handling the firing action:

isShooting = Input.GetKeyDown(KeyCode.Space);
if (isShooting)
{
    foreach(Gun gun in guns)
    {
        gun.Shoot();
    }
}

Which works through this code where I instantiate the gun pattern and access the script for each one:

if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            currentFiringPattern = FiringPattern.Double;
            Destroy(currentGuns);
            currentGuns = Instantiate(gunsPrefabs[1], this.transform.position + new Vector3(0, 0, .75f), 
            Quaternion.identity, gameObject.transform);
            guns = transform.GetComponentsInChildren<Gun>();            
        }

It works just fine, let’s me shoot bullets, and fills the array as seen here:

Problem being, even after I clear the array, it starts assigning elements at the end of the array’s previous length, like this:

This causes my code to no longer work, and I can’t shoot bullets anymore because my firing for loop activates starting with the beginning of the array. If there are empty elements at the beginning of the array I get an error for trying to reference a destroyed object.

I tried a few different ways to clear and resize the array:

if (Input.GetKeyDown(KeyCode.C))
{
    System.Array.Clear(guns, 0, guns.Length);
    System.Array.Resize<Gun>(ref guns, 0);    
}

Which I thought worked:


While the first line (Array.Clear) removed all elements, they still showed up in the inspector. It wasn’t until I added the next (Array.Resize) that it actually cleared it to looking like an empty array in the inspector.

Alas, it didn’t help, and next time I press a key to switch gun patterns it assigns the elements to the array after the end of its previous length, leaving me with multiple empty elements at the top.
One thing that does work though, is manually dragging the new elements to the top of the array:

This allowed me to change my gun pattern as much as I wanted and no matter how long the array got, so long as I manually dragged them up in the inspector.

I feel like there’s something simple I’m missing but I also feel like I’ve tried everything over the past couple hours. How can I start over in the array each time I press a key to switch new weapons?

If you need to resize an array or add/insert elements, you should be using a List<T> instead.

Thanks for the swift reply. I saw that come up a couple times when I was researching but I don’t really get it. How can I use List<T> to solve this?

It’s effectively a wrapper around array, with methods for adding, removing, inserting, and clearing among others, and it handles all the minutia for you. List<T> is the most crucial type to understand in the core C# library.

There’s plenty of information on how to use them: Data collections - Introductory interactive tutorial - A tour of C# | Microsoft Learn

So I reworked my code using List in stead of an array. Unfortunately it’s doing the same thing.

if (Input.GetKeyDown(KeyCode.Alpha2))
        {            
            currentFiringPattern = FiringPattern.Double;
            Destroy(currentGuns);            
            currentGuns = Instantiate(gunsPrefabs[1], this.transform.position + new Vector3(0, 0, .75f), Quaternion.identity, gameObject.transform);            
            guns2 = new List<Gun>(transform.GetComponentsInChildren<Gun>());            
        }


While it’s not continuously expanding like the array was, it still assigns to the very end no matter if it’s a new list or now. I’ve tried clearing and resizing the list, but it doesn’t seem to forget the previous elements, starting me after empty elements.

if (Input.GetKeyDown(KeyCode.C))
{
    guns2.Clear();
    guns2.Capacity = 0;    
}

To add on to what spiney199 said, List is much more convenient in cases like this, as you can just call Clear() on it and it will actually clear the array to have 0 entries, while if you call Clear() on an array, it will only set the entries to null (or default values in case of value types) but the entries will remain there as you saw yourself.

Arrays can also be inconvenient when you want their size to be dynamic, because you can’t really change their size. Each time you need to resize it, you need to create a totally new array (that’s what Array.Resize does). Lists are much more convenient in that sense as you can just add or remove entries and they will automatically resize themselves.

In your example though I still find it strange how when you resized the array to 0, it still had empty entries after adding new guns to it again. I suspect there’s something incorrect in the code that adds entries to the array. Mind showing us that code?

This suggests even more that there’s something wrong in the code that adds elements to the list. We’ll need to see that code.

The only code populating the list is right there in the if statement for pressing the 2 key. if you mean the ‘Gun’ script it’s referencing, this is the whole thing:

using UnityEngine;

public class Gun : MonoBehaviour
{
    Vector3 direction;

    public Bullet bullet;


    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        direction = (transform.localRotation * Vector3.forward).normalized;
    }

    public void Shoot()
    {
        GameObject go = Instantiate(bullet.gameObject, transform.position, Quaternion.identity);
        Bullet goBullet = go.GetComponent<Bullet>();
        goBullet.transform.rotation = transform.rotation;
        goBullet.direction = direction;
    }
}

I don’t think it even knows about the list

Ok actually I think I know what’s happening. You’re populating the list with all the child gun components. However, since you’ve just destroyed the guns in the same frame, I am guessing that the “destroyed” guns still actually exist for that frame, so they are still being added to the list. Then when they are properly cleaned up in the end of the frame, the list entries become null / missing references.

You should do a for loop right after the GetComponentsInChildren and exclude any null components, like so:

for (int i = 0; i < guns.Count; i++)
{
    if (guns[i] == null)
    {
        guns.RemoveAt(i);
        i--;
    }
}

(There are nicer ways to properly populate the list than this but for now check if this fixes it)

Don’t do this. The point of using a List<T> is to add/remove elements from it. Simply .Add() a gun when you instantiate it, and .Remove() them when you destroy them, or .Clear() the list when you destroy all of them.

But what @drgrandayy said is otherwise correct. You don’t want to be destroying things and then fetching all components in the same frame, as objects aren’t destroyed until the end of frame.

I went ahead and improved the code a bit:

if (Input.GetKeyDown(KeyCode.Alpha2))
{            
    currentFiringPattern = FiringPattern.Double;
    Destroy(currentGuns);            
    currentGuns = Instantiate(gunsPrefabs[1], this.transform.position + new Vector3(0, 0, .75f), Quaternion.identity, gameObject.transform);            
    guns2.Clear();
    guns2.AddRange(currentGuns.transform.GetComponentsInChildren<Gun>());            
}

What this does is first of all, it avoids creating a new list each time and instead clears it and adds the new guns to it, like what spiney199 suggested.

And secondly, it only gets the child components of the newly instantiate currentGuns object, ensuring that the just destroyed guns are not also added to the list (and no need for extra for loops).

It worked! You guys are geniuses. Thank you so much for taking the time out of your day to help a rookie like me out with something so deceptively basic.

It worked! I probably wouldn’t have had the second round of troubles if I hadn’t tried to make a new list every time. At least I learned some valuable other things through my persistent ignorance. I wish I could mark you both with the solution. Thanks again for stopping by to give me a hand today!

We’ve all been there, glad I could be of help!