限制 For 循环中的列表迭代(最接近玩家)
Limiting List Iterations in a For Loop (Closest To Player)
我有一个 List<Collider> colliders
用于瓦片地图。
我想到的一种方法是检查所有碰撞器位置的完整列表,与玩家的位置进行比较,然后将最近的碰撞器添加到临时列表以加快迭代速度。仅每 100 毫秒执行一次此操作将减少性能损失。但我想还有比这更好的方法,对吧?
我在这里阅读了一个不同的 post 以进行碰撞优化,它提到使用 "CPU Budget",我打算为此和其他人实施。我还没有读过线程。
较短的问题:如何将最大迭代次数限制为仅距离玩家最近的对撞机?
您可以尝试类似的方法:
foreach (Collider collider in colliders.OrderBy(c => c.Position - player.Position))
{
// The tiles closest to the player should be at the start
if (collider.Rectangle.Intersects(player.Rectangle))
{
// Do whatever you want to do here
Break;
}
}
不要忘记在代码顶部添加 using System.Linq;
。
我发现在这种情况下使用 List.Where() 效果最好。 100,000 个碰撞器列表(有些极端,但在 512x512 地图上很可能),使用:
foreach (TestCollider test in integers.Where(c => c.IsInRange() == true)) { }
使用 Stopwatch
,迭代 1,000 次,range
为 20:
- 10,000 个列表:始终为 0 毫秒
- 50,000 个列表:平均 2 毫秒。
- 100,000 个列表:平均 4 毫秒。
- 250,000 人名单:平均 12 毫秒。
如果有人有任何改进建议,我很乐意更新。
为了减少要检查的碰撞器的数量,您可以预先将它们从候选列表中排除。将碰撞器保留在 Dictionary colliders 中,同时创建第二个 Dictionary > collidersByChunks。作为第二个字典中的键,使用块的坐标,作为值,使用碰撞器的子列表。像这样:
class ColliderManager
{
Dictionary<TKey, Collider> colliders;
Dictionary<Vector2, List<TKey>> collidersByChunks;
public void AddCollider(TKey pKey, Collider pCOllider)
{
this.colliders.Add(pKey, pCollider);
foreach(Vector2 chunkCoord in this.GetChunkCoords(pCollider.Rectangle))
{
List<TKey> collidersAtChunk = null;
if(!this.collidersByChunks.TryGetValue(chunkCoord, out collidersAtChunk))
{
collidersAtChunk = new List<TKey>();
this.collidersByChunks.Add(chunkCoords, collidersAtChunk);
}
collidersAtChunk.Add(pKey, pCollider);
}
}
private Vector2[] GetChunkCoords(Rectangle pRectangle)
{
// return all chunks pRectangle intersects
}
}
检查时确定哪些块穿过扫描对象,并将其与仅来自该块的碰撞器进行比较。
我有一个 List<Collider> colliders
用于瓦片地图。
我想到的一种方法是检查所有碰撞器位置的完整列表,与玩家的位置进行比较,然后将最近的碰撞器添加到临时列表以加快迭代速度。仅每 100 毫秒执行一次此操作将减少性能损失。但我想还有比这更好的方法,对吧?
我在这里阅读了一个不同的 post 以进行碰撞优化,它提到使用 "CPU Budget",我打算为此和其他人实施。我还没有读过线程。
较短的问题:如何将最大迭代次数限制为仅距离玩家最近的对撞机?
您可以尝试类似的方法:
foreach (Collider collider in colliders.OrderBy(c => c.Position - player.Position))
{
// The tiles closest to the player should be at the start
if (collider.Rectangle.Intersects(player.Rectangle))
{
// Do whatever you want to do here
Break;
}
}
不要忘记在代码顶部添加 using System.Linq;
。
我发现在这种情况下使用 List.Where() 效果最好。 100,000 个碰撞器列表(有些极端,但在 512x512 地图上很可能),使用:
foreach (TestCollider test in integers.Where(c => c.IsInRange() == true)) { }
使用 Stopwatch
,迭代 1,000 次,range
为 20:
- 10,000 个列表:始终为 0 毫秒
- 50,000 个列表:平均 2 毫秒。
- 100,000 个列表:平均 4 毫秒。
- 250,000 人名单:平均 12 毫秒。
如果有人有任何改进建议,我很乐意更新。
为了减少要检查的碰撞器的数量,您可以预先将它们从候选列表中排除。将碰撞器保留在 Dictionary colliders 中,同时创建第二个 Dictionary > collidersByChunks。作为第二个字典中的键,使用块的坐标,作为值,使用碰撞器的子列表。像这样:
class ColliderManager
{
Dictionary<TKey, Collider> colliders;
Dictionary<Vector2, List<TKey>> collidersByChunks;
public void AddCollider(TKey pKey, Collider pCOllider)
{
this.colliders.Add(pKey, pCollider);
foreach(Vector2 chunkCoord in this.GetChunkCoords(pCollider.Rectangle))
{
List<TKey> collidersAtChunk = null;
if(!this.collidersByChunks.TryGetValue(chunkCoord, out collidersAtChunk))
{
collidersAtChunk = new List<TKey>();
this.collidersByChunks.Add(chunkCoords, collidersAtChunk);
}
collidersAtChunk.Add(pKey, pCollider);
}
}
private Vector2[] GetChunkCoords(Rectangle pRectangle)
{
// return all chunks pRectangle intersects
}
}
检查时确定哪些块穿过扫描对象,并将其与仅来自该块的碰撞器进行比较。