无法从XNA中的列表中删除项目

时间:2014-03-31 20:10:57

标签: c# xna

我正在使用Flappy Bird克隆作为练习,因为我最近开始使用XNA进行编程,而且我遇到了这个我无法理解的错误。

我在Update()函数中包含了以下代码,其中的工作是在管道离开屏幕时删除管道,以免在创建更多管道时向左无限移动:

//Pipe despawner
foreach (var pipe in pipes)
{
   if (pipe.position1.X <= -180)
   {
         pipes.Remove(pipe);
   }
}

游戏运行良好,直到第一个管道离屏,调试器暂停并通过以下消息发出信号的这部分信号:

An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

Additional information: Colección modificada; puede que no se ejecute la operación de enumeración.

对不起第二部分是西班牙语,它是我系统的语言,但希望你知道如何解决这个问题。

我相信我可以简单地不包括这部分代码,考虑到游戏的简单性,结果是无限产生的管道对性能产生的小收费,但我宁愿不采用这种做法我一开始学习游戏编程。

5 个答案:

答案 0 :(得分:3)

问题是您在枚举集合时修改集合(通过删除管道)。这将始终在.NET中引发异常(如果您在多线程环境中执行此操作,则需要注意,因为您可以获得一些由于它而无法重现的异常)

解决问题的一种简单方法是在枚举时保留“kill”列表:

列出killList = new List();

foreach (var pipe in pipes)
{
   if (pipe.position1.X <= -180)
   {
      killList.Add(pipe)
   }
}

更容易:

IEnumerable<Pipe> killList = pipes.Where (p => p.position1.X < -100);

无论哪种方式,枚举此 new 集合,并从主要元素中删除匹配元素(从而避免错误条件):

foreach (Pipe p in killList)
   pipes.Remove(p);

你完成了!再次,请注意线程。如果要在另一个线程中创建新管道,则可能很容易与此管道“发生碰撞”并导致异常。如果是这种情况,请确保在这些代码段周围加上锁。

注意,如果使用LINQ方法,实际上可以内联“killList”:

foreach (Pipe p in pipes.Where(p => p.Position1.X <= -100))
   pipes.Remove(p);

正如@ rot13建议的那样,你也可以运行RemoveAll,从“Where”语句中传递谓词,如:

pipes.RemoveAll(p => p.Position1.X <= -100);

这很简单:)

答案 1 :(得分:3)

您无法从当前正在使用foreach进行迭代的集合中删除项目。

使用for循环并自行调整索引,或使用第二个集合来处理要删除的管道:

List<Pipe> piesToRemove = new List<Pipe>();
// First you flag every pipes you need to remove
foreach (var pipe in pipes)
{
   if (pipe.position1.X <= -180)
   {
         pipesToRemove.Add(pipe);
   }
}

//Now you remove them from your original collection
foreach (var pipe in pipesToRemove)
{
    pipes.Remove(pipe);
}

答案 2 :(得分:3)

您可能只想将它们移动到屏幕的另一侧并向上/向下移动它们,而不是移除管道。

答案 3 :(得分:1)

它说你在循环时不能修改管道。尝试这样的事情:

        //Pipe despawner
        var unusedPipes = new List<Pipe>();

        foreach (var pipe in pipes)
        {
            if (pipe.position1.X <= -180)
            {
                unusedPipes.Add(pipe);
            }
        }

        foreach (var unusedPipe in unusedPipes)
        {
            pipes.Remove(unusedPipe);
        }

我只是猜测管道是一个管道列表。

答案 4 :(得分:1)

其他答案完全有效,可以解决您的问题。

但是,我认为您应该知道在迭代期间不需要创建第二个集合来删除对象。

我们可以使用反向迭代从您的集合中删除对象。请尝试以下方法:

foreach (Pipe pipe in pipes.Reverse<Pipe>())
{
    pipes.Remove(pipe);
}

上面的代码假设您的集合是通用List:

List<Pipe> pipes = new List<Pipe>();

修改

以下是我正在进行的游戏中的示例:

private List<Debris> listOfDebris = new List<Debris>();

foreach (Debris debris in listOfDebris.Reverse<Debris>())
{
    CalculateDebrisLocation(debris);

    // Remove debris if it travels outside level boundries
    if (!debris.Rect.Intersects(currentLevel.MapRect))
        listOfDebris.Remove(debris);
}