Reputation: 553
I am developing a simple game with the XNA library for C#. In the following code snippet I am getting the
Collection was modified; enumeration operation may not execute.
error at the top of the second foreach loop. In my (relatively limited) experience of C# this occurs when attempting to modify the underlying collection during a loop. However, as far as I can see I am not modifying the enemy_positions collection in any way. All the collections in this code are of the type List<Vector2>
.
What is happening here?
//defines collision behaviour when enemy is hit
int be_no = 0;
List<Vector2> tmp_bullets = bullet_i_position;
List<Vector2> tmp_enemy = enemy_positions;
foreach (Vector2 bullet in bullet_i_position)
{
//get bullet collision box
Rectangle bullet_col = new Rectangle(Convert.ToInt32(bullet.X - 12), Convert.ToInt32(bullet.Y - 12), 25, 26);
int en_no = 0;
foreach (Vector2 enemy in enemy_positions)
{
//get enemy collsion box
en_box = new Rectangle(Convert.ToInt32(enemy.X), Convert.ToInt32(enemy.Y), 75, 75);
if (temp_r.Intersects(en_box))
{
//remove all colliding elements
tmp_enemy.RemoveAt(en_no);
tmp_bullets.RemoveAt(be_no);
bullet_direction.RemoveAt(be_no);
}
en_no++;
}
be_no++;
}
//update actual lists
bullet_i_position = tmp_bullets;
enemy_positions = tmp_enemy;
Upvotes: 4
Views: 618
Reputation: 1121
as others have mentioned you have encountered the difference between reference and value types
i would suggest you replace the foreach loops with something similar to the below
for(int i = 0; i<bullet_i_position.count;i++)
{
bool hascollided = false;
for(int j = 0; j<enemy_positions.count;j++)
{
if(collisionOccurs)
{
hasCollided = true;
enemy_positions.RemoveAt(j);
j--;
}
}
if(hasCollided)
{
bullet_i_position.RemoveAt(i);
i--;
}
}
you need to consider what would happen if the bullet collided with 2 enemies at the same time in the above example it would take out both
Upvotes: 0
Reputation:
List<Vector2> tmp_enemy = enemy_positions;
Does not make a copy of enemy_positions and assign it to tmp_enemy. Instead, tmp_enemy points to enemy_positions. Thus, any change in tmp_enemy is reflected in enemy_positions. If you want to make an actual copy, this is a better approach:
List<Vector2> tmp_enemy = new List<Vector2>(enemy_positions);
Upvotes: 0
Reputation: 14678
The lines:
List<Vector2> tmp_bullets = bullet_i_position;
List<Vector2> tmp_enemy = enemy_positions;
are not cloning the list, they're just creating a local reference to the same list. The direct solution would be to change these two lines to:
List<Vector2> tmp_bullets = new List<Vector2>(bullet_i_position);
List<Vector2> tmp_enemy = new List<Vector2>(enemy_positions);
But this will be allocating a new list every time the method is called, which is going to be terrible for garbage collection (especially in a game), so the better solution is to remove your foreach loops and replace them with reverse for loops. This works because you only get that exception when iterating a collection with enumerators. The same problem does not apply to regular for loops. For example:
for (int i = bullet_i_position.Count - 1; i >= 0; i--)
{
Vector2 bullet = bullet_i_position[i];
// ...
}
Also the reason for the reverse iteration is that removing an element while regularly iterating means that you'll skip the element after a removed element (beause the indexes shift down by 1)
Upvotes: 6