2D粒子引擎无法处理超过450个粒子。为什么?(XNA)

3

更新; 将j = 0改为j = i,允许平稳的帧速率下最多700粒子

我正在尝试模拟具有数百个粒子的2D水,这些粒子具有声明其位置的Vector2和一个用于表示它们速度的Vector2。

当涉及到碰撞检测时,我的程序虽然仅使用勾股定理,但不喜欢使用超过450个粒子。

以下是主类中的碰撞检测代码;

        for (int i = 0; i < particleList.Count; i++)
            {
                for (int j = 0; j < particleList.Count; j++)
                {
                    if (distanceBetween(particleList[i].position, particleList[j].position) < reactDistance)
                    {
                        if (particleList[i].position.X > particleList[j].position.X) //x axis
                        {
                            particleList[i].velocity.X += repelSpeed;
                            particleList[j].velocity.X -= repelSpeed;

                            particleList[i].position.X -= attractSpeed;
                            particleList[j].position.X += attractSpeed;
                        }
                        else
                        {
                            particleList[i].velocity.X -= repelSpeed;
                            particleList[j].velocity.X += repelSpeed;

                            particleList[i].position.X += attractSpeed;
                            particleList[j].position.X -= attractSpeed;
                        }

                        if (particleList[i].position.Y > particleList[j].position.Y) //y axis
                        {
                            particleList[i].velocity.Y += repelSpeed;
                            particleList[j].velocity.Y -= repelSpeed;

                            particleList[i].position.Y -= attractSpeed;
                            particleList[j].position.Y += attractSpeed;
                        }
                        else
                        {
                            particleList[i].velocity.Y -= repelSpeed;
                            particleList[j].velocity.Y += repelSpeed;

                            particleList[i].position.Y += attractSpeed;
                            particleList[j].position.Y -= attractSpeed;
                        }
                    }
                }
            }

这里是`distanceBetween(v1, v2)`方法的代码:
        public float distanceBetween(Vector2 a, Vector2 b)
    {
        float xDist, yDist, distTo;
        if (a.X > b.X) //x axis
        {
            xDist = a.X - b.X;
        }
        else
        {
            xDist = b.X - a.X;
        }

        if (a.Y > b.Y) //y axis
        {
            yDist = a.Y - b.Y;
        }
        else
        {
            yDist = b.Y - a.Y;
        }
        distTo = (float)(Math.Sqrt((xDist * xDist) + (yDist * yDist)));
        return distTo;
    }

Vector2.Distance(v1, v2)方法不会影响性能。

如果你想知道attractSpeed是什么,它是我尝试形成水集合的努力,但我不确定如何做到。

最终,我希望得到像这样的效果:http://grantkot.com/MPM/Liquid.html


1
既然第一个循环已经检查过粒子,第二个循环是否可以从j=i开始,而不是从j=0开始? - Mike B
1
你也可以尝试并行处理来实现这个。http://msdn.microsoft.com/zh-cn/library/system.threading.tasks.parallel.aspx - Lasse
请查看我链接页面底部的示例。Lambda表达式可能非常适合您。 - Lasse
Parallel.For(0, N, i => { // Do Work. });这段代码是什么意思? 该页面没有逐步解释它实际执行的操作。 - ShadowByte
Parallel.For(start, end, i => { //i is used here just like in normal for loop. }); - Lasse
显示剩余5条评论
1个回答

2
“虽然有一些可以改进的性能方面,但最终,粒子的存储将会超过任何努力。”
“你的算法是O(n^2),因为对于每个粒子,你都要再次遍历整个粒子列表。对于n = 700,这就是循环执行了490000次。此外,许多粒子被过度检查了i次。如果你从j=i开始内部循环,你会获得明显的速度提升。”
“然而,我认为这只是一个权宜之计。你应该研究更有效的粒子存储方式,即Quadtree。”
“此外,不要为每个距离计算平方根,而是将你比较的距离平方。”
distTo = (xDist * xDist) + (yDist * yDist);
...

if(distanceBetween(particleList[i].position, particleList[j].position) < reactDistance * reactDistance)

你甚至可以在循环之前预先计算,这样就不必每次都有这个开销了。

谢谢。像Mike B建议的那样将j=i更改,现在可以实现高达700个粒子的平滑帧速率。 - ShadowByte
如果这只是一个权宜之计,我会研究一下四叉树。非常感谢。 - ShadowByte
YouTube上没有令人满意的四叉树教程。 我理解它的思想,而且它非常高效,但是没有人能够很好地解释如何编写程序,让我开始接近它。经过多次优化尝试,我的程序支持大约850个粒子以50 FPS的速度运行,这是比较舒适的。 - ShadowByte
链接的维基百科文章有一个相当不错的介绍,关于最重要的方法(插入和范围)都详细说明了伪代码。你需要填写其余部分。 - Femaref

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接