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