2012-12-31 65 views
4

我正在开发一个简单的游戏,使用CNA的XNA库。在下面的代码片段中,我得到了收藏已被修改,其中项目未被修改

集合被修改;枚举操作可能不会执行。

错误在第二个foreach循环的顶部。在我(相对有限)的C#经验中,当在循环期间尝试修改底层集合时会发生这种情况。但是,据我所见,我不以任何方式修改enemy_positions集合。此代码中的所有集合都是List<Vector2>类型。

这里发生了什么?

//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; 

回答

6

的线条:

List<Vector2> tmp_bullets = bullet_i_position; 
List<Vector2> tmp_enemy = enemy_positions; 

不是克隆名单,他们只是建立一个本地引用相同的列表。直接的解决办法是改变这些两行:

List<Vector2> tmp_bullets = new List<Vector2>(bullet_i_position); 
List<Vector2> tmp_enemy = new List<Vector2>(enemy_positions); 

但这将是每一个方法被调用时,这将是可怕的垃圾收集(特别是游戏)分配一个新的列表,所以更好的解决方案是删除你的foreach循环,并将其替换为for循环。这是有效的,因为只有在使用枚举数迭代集合时才会发生该异常。同样的问题不适用于常规循环。例如:

for (int i = bullet_i_position.Count - 1; i >= 0; i--) 
{ 
    Vector2 bullet = bullet_i_position[i]; 

    // ... 
} 

同样对于反向迭代的原因在于,移除元素而定期迭代意味着你已移除元件之后跳过元件(beause索引下移通过1)

+0

感谢您的即时解决方案。另外关于循环的额外细节也很有意义,所以我将在未来改变我的做法(我来自PHP背景,我不必考虑循环的类型)。 –

+0

是的,唯一需要这样做的时候是删除元素,因为当枚举时从枚举器/迭代器中删除元素时,大多数托管语言都不喜欢它。除此之外,'foreach'循环一直工作。 –

+0

我使用的另一个解决方案是使用'ToArray'而不是创建一个新列表。 – ashes999

0
List<Vector2> tmp_enemy = enemy_positions; 

不制作enemy_positions的副本并将其分配给tmp_enemy。相反,tmp_enemy指向enemy_positions。因此,tmp_enemy的任何变化都会反映在enemy_positions中。 如果你想使一个实际的副本,这是一个更好的办法:

List<Vector2> tmp_enemy = new List<Vector2>(enemy_positions); 
0

如其他人所说的,你遇到了参考和值类型之间的区别

我会建议你更换的foreach与循环下面

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--; 
     } 
} 

你需要考虑如果子弹在上面的例子中,同时将拿出两

与2个敌人相撞会发生什么类似的东西
+0

Robert Rouhani建议的反向迭代过程要优雅得多;改变for循环内迭代器的值通常被认为是不好的做法,并且需要重构。 – 2012-12-31 13:12:09

+0

这是非常真实的,虽然你仍然需要小心,你不要太早删除对象 – RoughPlace