Re-added collision demo (#642)

Improved collision performance
This commit is contained in:
JanFokke
2019-12-26 22:20:43 +10:00
committed by Dylan Wilson
parent dfa3bce162
commit c360b66535
7 changed files with 349 additions and 90 deletions
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace MonoGame.Extended.Collisions
{
@@ -11,7 +12,7 @@ namespace MonoGame.Extended.Collisions
public const int DefaultMaxObjectsPerNode = 25;
protected List<Quadtree> Children = new List<Quadtree>();
protected List<QuadtreeData> Contents = new List<QuadtreeData>();
protected HashSet<QuadtreeData> Contents = new HashSet<QuadtreeData>();
/// <summary>
/// Creates a quad tree with the given bounds.
@@ -44,8 +45,7 @@ namespace MonoGame.Extended.Collisions
/// <returns>Returns the targets of objects found.</returns>
public int NumTargets()
{
Reset();
List<QuadtreeData> dirtyItems = new List<QuadtreeData>();
var objectCount = 0;
// Do BFS on nodes to count children.
@@ -56,23 +56,28 @@ namespace MonoGame.Extended.Collisions
var processing = process.Dequeue();
if (!processing.IsLeaf)
{
foreach (var child in processing.Children) process.Enqueue(child);
foreach (var child in processing.Children)
{
process.Enqueue(child);
}
}
else
{
var contents = processing.Contents;
foreach (var data in contents)
foreach (var data in processing.Contents)
{
if (!data.Flag)
if (data.Dirty == false)
{
objectCount++;
data.Flag = true;
data.MarkDirty();
dirtyItems.Add(data);
}
}
}
}
Reset();
for (var i = 0; i < dirtyItems.Count; i++)
{
dirtyItems[i].MarkClean();
}
return objectCount;
}
@@ -90,11 +95,14 @@ namespace MonoGame.Extended.Collisions
return;
}
if (IsLeaf && Contents.Count >= MaxObjectsPerNode) Split();
if (IsLeaf && Contents.Count >= MaxObjectsPerNode)
{
Split();
}
if (IsLeaf)
{
Contents.Add(data);
AddToLeaf(data);
}
else
{
@@ -113,52 +121,12 @@ namespace MonoGame.Extended.Collisions
{
if (IsLeaf)
{
var removeIndex = -1;
for (int i = 0, size = Contents.Count; i < size; i++)
{
if (Contents[i].Target == data.Target)
{
removeIndex = i;
break;
}
}
if (removeIndex != -1)
{
Contents.RemoveAt(removeIndex);
}
data.RemoveParent(this);
Contents.Remove(data);
}
else
{
foreach (var quadTree in Children)
{
quadTree.Remove(data);
}
}
Shake();
}
/// <summary>
/// Resets all QuadtreeData.Flag to false.
/// </summary>
/// <remarks>
/// Used internally to query and count contents without duplicates.
/// </remarks>
public void Reset()
{
if (IsLeaf)
for (int i = 0, size = Contents.Count; i < size; i++)
{
var quadTreeData = Contents[i];
quadTreeData.Flag = false;
Contents[i] = quadTreeData;
}
else
{
for (int i = 0, size = Children.Count; i < size; i++)
Children[i].Reset();
throw new InvalidOperationException($"Cannot remove from a non leaf {nameof(Quadtree)}");
}
}
@@ -169,6 +137,8 @@ namespace MonoGame.Extended.Collisions
{
if (!IsLeaf)
{
List<QuadtreeData> dirtyItems = new List<QuadtreeData>();
var numObjects = NumTargets();
if (numObjects == 0)
{
@@ -182,28 +152,41 @@ namespace MonoGame.Extended.Collisions
{
var processing = process.Dequeue();
if (!processing.IsLeaf)
{
foreach (var subTree in processing.Children)
{
process.Enqueue(subTree);
}
}
else
{
foreach (var data in processing.Contents)
{
if (!data.Flag)
if (data.Dirty == false)
{
Contents.Add(data);
data.Flag = true;
AddToLeaf(data);
data.MarkDirty();
dirtyItems.Add(data);
}
}
}
}
Children.Clear();
}
for (var i = 0; i < dirtyItems.Count; i++)
{
dirtyItems[i].MarkClean();
}
}
}
private void AddToLeaf(QuadtreeData data)
{
data.AddParent(this);
Contents.Add(data);
}
/// <summary>
/// Splits a quadtree into quadrants.
/// </summary>
@@ -230,14 +213,22 @@ namespace MonoGame.Extended.Collisions
Children[i].CurrentDepth = CurrentDepth + 1;
}
for (int i = 0, size = Contents.Count; i < size; ++i)
foreach (QuadtreeData contentQuadtree in Contents)
{
for (int j = 0; j < Children.Count; j++)
foreach (Quadtree childQuadtree in Children)
{
Children[j].Insert(Contents[i]);
childQuadtree.Insert(contentQuadtree);
}
}
Clear();
}
private void Clear()
{
foreach (QuadtreeData quadtreeData in Contents)
{
quadtreeData.RemoveParent(this);
}
Contents.Clear();
}
@@ -248,25 +239,28 @@ namespace MonoGame.Extended.Collisions
/// <returns>A unique list of targets intersected by area.</returns>
public List<QuadtreeData> Query(IShapeF area)
{
Reset();
return QueryWithoutReset(area);
var recursiveResult = new List<QuadtreeData>();
QueryWithoutReset(area, recursiveResult);
for (var i = 0; i < recursiveResult.Count; i++)
{
recursiveResult[i].MarkClean();
}
return recursiveResult;
}
private List<QuadtreeData> QueryWithoutReset(IShapeF area)
private void QueryWithoutReset(IShapeF area, List<QuadtreeData> recursiveResult)
{
var result = new List<QuadtreeData>();
if (!NodeBounds.Intersects(area)) return result;
if (!NodeBounds.Intersects(area))
return;
if (IsLeaf)
{
for (int i = 0, size = Contents.Count; i < size; i++)
foreach (QuadtreeData quadtreeData in Contents)
{
if (Contents[i].Bounds.Intersects(area)
&& !Contents[i].Flag)
if (quadtreeData.Dirty == false && quadtreeData.Bounds.Intersects(area))
{
result.Add(Contents[i]);
Contents[i].Flag = true;
recursiveResult.Add(quadtreeData);
quadtreeData.MarkDirty();
}
}
}
@@ -274,13 +268,9 @@ namespace MonoGame.Extended.Collisions
{
for (int i = 0, size = Children.Count; i < size; i++)
{
var recurse = Children[i].QueryWithoutReset(area);
result.AddRange(recurse);
Children[i].QueryWithoutReset(area, recursiveResult);
}
}
return result;
}
}
}