C# 用于移动和碰撞对象的性能更好的四叉树

C# 用于移动和碰撞对象的性能更好的四叉树,c#,performance,unity3d,data-structures,quadtree,C#,Performance,Unity3d,Data Structures,Quadtree,所以基本上我想制作一个场景,在这个场景中,大约有50K个小行星,它们的位置和AABB轴对齐边界框,并将它们中的每一个移动到一个随机方向,这是在开始时生成的。在移动它们之后,我必须检查是否有小行星在碰撞 我使用四叉树数据结构来插入和检查冲突。 我保留一个50K点的数组,迭代并更新它们,然后插入四叉树,再次迭代50K点,并通过QT查询是否有任何点发生碰撞 我在这里和那里读了大约两周的书,尝试了尽可能多的资源,但我无法挤出最大的性能。大多数的源代码使用C++或其他更好的性能语言,但是我需要使用C语言。

所以基本上我想制作一个场景,在这个场景中,大约有50K个小行星,它们的位置和AABB轴对齐边界框,并将它们中的每一个移动到一个随机方向,这是在开始时生成的。在移动它们之后,我必须检查是否有小行星在碰撞

我使用四叉树数据结构来插入和检查冲突。 我保留一个50K点的数组,迭代并更新它们,然后插入四叉树,再次迭代50K点,并通过QT查询是否有任何点发生碰撞

我在这里和那里读了大约两周的书,尝试了尽可能多的资源,但我无法挤出最大的性能。大多数的源代码使用C++或其他更好的性能语言,但是我需要使用C语言。 这是我的密码:

public struct Point
{
    public float x,y; 
    public int ID;

    public void MoveTowards(float posX, float posY)
    {
        position.x = position.x + posX;
        position.y = position.y + posY;
    }
}

public class Controller
{

    Point[] asteroids = new Point[50K];
    Point[] speed = new Point[50K];
    QuadTree qt = new QuadTree();

    //Runs every frame
    void Update() 
    {
        qt.ClearAllNodes();
        for loop asteroids(50K)
        {
            asteroids[i].MoveTowards(speed.x, speed.y);
            qt.Insert(astetoids[i]);
        }

        for loop asteroids(50K)
        {
            int collidingAsteroidID = qt.Query(astetoids[i]);
            if(collidingAsteroidID != -1) { 
                print(collidingAsteroidID + " is colliding with " + i); 
            }
        }
    }

}

class QuadTree 
{
    public Rectangle boundry;
    Point[] nodes;
    bool root = false;
    bool divided = false;
    int numberOfNodesInserted = 0;
    QuadTree northEast, northWest, southEast, southWest;

    public QuadTree(Rectangle boundry) 
    {
        nodes = new Point[4];
        this.boundry = boundry;
    }   

    #region Methods

    //Clear all the nodes in the Quad-Tree
    public void ClearAllNodes() 
    {
        if(numberOfNodesInserted == 0 && !root) return;
        numberOfNodesInserted = 0;
        root = false;

        if(divided) 
        {
            northEast.ClearAllNodes();
            northWest.ClearAllNodes();
            southEast.ClearAllNodes();
            southWest.ClearAllNodes();
        }
        divided = false;
    }

    public bool Insert(Point point) 
    {
        //Checking if the position is in the boundries.
        if(!boundry.Contains(point)) return false;
        if(numberOfNodesInserted < 4 && !root) 
        {
            nodes[numberOfNodesInserted] = point;
            numberOfNodesInserted++;
            return true;
        }
        else if(root)
        {
            if(northEast.Insert(point)) return true;            
            if(northWest.Insert(point)) return true;        
            if(southEast.Insert(point)) return true;
            if(southWest.Insert(point)) return true;    
        }
        else if(!root && numberOfNodesInserted == 4)
        {
            //Making this node a branch and moving all the to sub-leafs 
            root = true;
            numberOfNodesInserted = 0;

            if(!divided)
                SubDivide();

            for (int i = 0; i < 4; i++)
            {
                if(!northEast.Insert(nodes[i]))         
                if(!northWest.Insert(nodes[i]))     
                if(!southEast.Insert(nodes[i]))
                if(!southWest.Insert(nodes[i])) { }
            }

            if(!northEast.Insert(point))            
            if(!northWest.Insert(point))        
            if(!southEast.Insert(point))
            if(!southWest.Insert(point)) { }
            return true;
        }
        return false;
    }

    public int Query(Point point, float radius)
    {

        if(numberOfNodesInserted == 0 && !root) return -1;
        if(!boundry.Contains(point)) return -1;

        if(!root && numberOfNodesInserted != 0)
        {
            for (int i = 0; i < numberOfNodesInserted; i++)
            {
                if(DoOverlap(nodes[i], point, radius)) 
                    return nodes[i].ID; 
            }
        }
        else if(root && numberOfNodesInserted == 0)
        {
            int result;
            result = northEast.Query(point);
            if(result != -1)  return result;

            result = northWest.Query(point);
            if(result != -1)  return result;

            result = southEast.Query(point);
            if(result != -1)  return result;

            result = southWest.Query(point);
            if(result != -1)  return result;
        }
        return -1;
    }
    #endregion

    #region HelperMethods
    private void SubDivide() 
    {
        //Size of the sub boundries 
        if(northEast == null) 
        {   
            float x = boundry.x;
            float y = boundry.y;
            float r = boundry.radius / 2;

            northEast = new QuadTree(new Rectangle(x + r, y + r, r));
            northWest = new QuadTree(new Rectangle(x - r, y + r, r));
            southEast = new QuadTree(new Rectangle(x + r, y - r, r));
            southWest = new QuadTree(new Rectangle(x - r, y - r, r));
        } 
        divided = true; 
    }


    #endregion
}



关于您的实施:

看起来你每走一步都在重建整棵树。这有必要吗?如果移动一个点,它们通常会停留在同一个节点中,因此可以避免clearNodes和后续插入到同一节点中

其他实现:

我在Java中实现了一些空间索引,插入/更新速率约为1M点/秒,查询速率冲突检查为100000点/秒,假设每个点通常有0或1个冲突。有关3D查询,请参见一些性能度量图16b,有关更新,请参见图40b。 最快的是四叉树和最快的树。
它们都使用自发布所述的z顺序导航。其中一部分是在导航/插入/更新/删除期间计算正确的子节点。例如,在节点上调用insertelement时,它不会快速尝试所有子节点,而是“计算”哪个子节点是正确的,并直接在该子节点上调用insert

新答案及附加要求:

好的,对于50K点和120Hz,你需要每秒进行50000*120=6000000次碰撞检查。考虑到4GHz的CPU,这意味着每次碰撞检查大约有650个CPU周期。我不认为你能用四叉树或任何类似的东西做到这一点,即使是用最有效的编程语言

我只看到一种选择: 由于您使用的是二维,请尝试以下操作:按X坐标对所有点进行排序。然后穿过所有点,检查是否与X坐标上足够接近的所有点发生碰撞,以防它们造成碰撞。这种算法有一些优点:

它比空间索引对缓存更友好,缓存未命中内存访问很可能是瓶颈。 它很容易并行化排序可以大部分并行化,搜索可以大部分并行化。 它非常简单,可以在GPU上执行。
一个单一的CPU核心,这仍然可能是太慢。但使用4核机器,您可能会获得所需的帧速率。使用GPU,可能会得到比您需要的多得多的东西。然而,我没有使用GPU的经验,所以我不知道这有多容易做到

您能否提供一些数据,说明您的实现有多快,需要多快?