- Home /
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Good day everyone. I'm new on unity. I'm trying to create a "light gun shooter" game. I have 17 positions where enemies may apear and a List to save each enemy instanciated by a prefab. The problem is that when I try to delete the GameObject of the list it seems to be out of the range. I've been trying and it looks like the GameObject is not correctly being added to the List. This is part of my code from the EnemySpawner:
public class EnemySpawner : MonoBehaviour
{
public GameObject enemy;
public GameObject enemySpawner;
public Vector2[] position;
List<GameObject> enemies = new List<GameObject>();
static int enemiesCount = 0;
bool existGO = false;
int randomPos;
float nextSpawn = 4f;
void Update()
{
if (Time.time > nextSpawn)
{
if (nextSpawn <= 15)
{
nextSpawn = Time.time + Random.Range(2f,3f);
} else if (nextSpawn > 15 && nextSpawn <= 30)
{
nextSpawn = Time.time + Random.Range(1.5f,2.5f);
} else
{
nextSpawn = Time.time + Random.Range(0.7f,1.7f);
}
randomPos = Random.Range(0,(position.Length - 1));
for (int i = 0 ; i < enemiesCount ; i++)
{
if (enemies[i].transform.position == new Vector3 (position[randomPos].x,position[randomPos].y,0))
{
existGO = true;
}
}
if (existGO == false)
{
GameObject myEnemy = Instantiate(enemy);
myEnemy.transform.SetParent(enemySpawner.transform, false);
myEnemy.transform.position = new Vector3(position[randomPos].x, position[randomPos].y, 0);
enemies.Add(myEnemy);//This Seems Not To Be Working Well
enemiesCount += 1;
Debug.Log(enemiesCount); //Works fine
existGO = false;
}
}
}
public void RemoveEnemy(GameObject enemigo)
{
Debug.Log(enemies[0].transform.position);//Out Of Range
for (int i = 0 ; i < enemiesCount ; i++)
{
Debug.Log(enemies[i].transform.position);//Out Of Range
if (enemies[i].transform.position == enemigo.transform.position)//Out Of Range
{
enemiesCount -= 1;
enemies.RemoveAt(i);
Destroy(enemigo);
}
}
}
}
Thanks to everyone who can help me.
This will not solve the problem but maybe point you the correct way to debug the problem..
Why do you use private static enemiesCount? And should there only be one Spawner in the scene (it seems like there cloud be multiple) Basically what you do is that if you have multiple spawners then you can share this common counter, but it really will show the aggregate number of enemies in all of your enemy lists and there is no way to use it with individual enemy list. When you iterate the list you can use enemies.Count
to get the list size. So in your Debug.Log(enemiesCount); use rather Debug.Log(enemies.Count)
; to get the actual list size.
instead of using enemiesCount you should be using the built in enemies.Count, that will show how many elements are in your list, without you having to manually update it. Another solution seeing as it is static would be to increment and decrement it in OnStart() and OnDestroy() or OnEnable() OnDisable(), but the 1st solution makes this one unnecessary.
Using Debug.Log(enemies.Contains(myEnemy)); will let you know if it has been added to the list.
Try comparing enemiesCount to enemies.Count also Debug.Log(enemies[i].transform.position + "i value: "+ i); will show what iteration of the loop you are on when it fails.
Answer by Eno-Khaon · Jun 01, 2021 at 07:50 PM
When looping through a List<> with the intent to remove elements, you should start at the end and work your way to the front. This way, you don't unintentionally skip elements or loop beyond the end of the List when you actually remove the contents.
for(int i = enemies.Count - 1; i >= 0; i--)
{
// Removing contents is safe
}
The alternative would be along the lines of modifying the iterator (i) and final count (enemiesCount) to carefully ensure that you don't skip elements or bypass the end of the List. Starting at the end and going backwards is also computationally more efficient, however, since the starting point (enemiesCount - 1), iterator (i), and ending point (0) will never have to change during the loop.
Edit: Changed from "enemiesCount" to "enemies.Count" primarily because an instanced List with static quantity makes no sense.
Answer by jackmw94 · Jun 01, 2021 at 05:36 PM
I think this is happening because you remove an enemy during your for-loop.
Say your enemies list contains enemies called A, B and C, your enemiesCount will equal 3. When you go to remove B you'll specify a loop that iterates 3 times: first run it'll pass by A, second run it'll find B and remove it causing your list to only have 2 elements in, third run you expect it to be comparing against C but at this point i == 2 and therefore will be out of range.
Luckily lists have a 'Remove' function that takes the object you're trying to remove. This function returns the index of the object it just removed, or -1 if it didn't remove an object. So - although this won't happen in your current setup - if it didn't remove an object you can detect this by checking if the index is < 0.
Although should be the fix to your problem, read both the comments as they're super useful to help you solve this yourself next time.