using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace CollisionExample { class QuadTree { const int MAX_DEPTH = 4; private QuadTreeNode mRoot; private int mMinWidth; /// /// Create an empty quadtree that will store rectangles that fit entirely within /// the Rectangle defined by worldsize /// /// The size of the world this quadtree can represent public QuadTree(Rectangle worldsize) { mRoot = new QuadTreeNode(worldsize, null); mMinWidth = worldsize.Width / (int) Math.Pow(4, MAX_DEPTH - 1); } /// /// Update the location of a world element within the quadtree /// /// The object whose location we wish to update /// A pointer to the quadtree node where this object currently lives /// Pointer to the node in the quadtree where the element lives after the update public QuadTreeNode UpdateLocation(WorldObject worldObj, QuadTreeNode tree) { if (tree != null) { Rectangle aabb = worldObj.AABB; if (tree.Area.Contains(aabb) && AddToThisNode(tree, aabb)) return tree; tree.RemoveElement(worldObj); while (tree.Parent != null && !tree.Area.Contains(aabb)) { tree = tree.Parent; } return insert(tree, worldObj); } return insert(worldObj); } /// /// Insert a world element into the quadtree /// /// World element to insert /// The quadtree node where the element was placed (used for changing the /// location of the quadtree element public QuadTreeNode insert(WorldObject elem) { return insert(mRoot, elem); } /// /// Find a list of all world elements in the quadtree that intersect with a given region /// /// The region to check for intersection /// (Output parameter) the list of all objects whose /// AABB intersects this region public void FindIntersects(Rectangle region, ref List intersects) { intersects.Clear(); FindIntersects(mRoot, ref intersects, region); } /// /// Helper method to find a list of all world elements in the quadtree that intersect /// a given region /// /// Root of the tree to search /// (output parameter) list of all objects who AABB intersect this region /// Region to check for intersection private void FindIntersects(QuadTreeNode root, ref List intersects, Rectangle r) { if (root != null) { foreach (WorldObject o in root.mElements) { if (o.AABB.Intersects(r)) { intersects.Add(o); } } int midWidth = root.Area.Width / 2; int midHeight = root.Area.Height / 2; if ((r.Left < root.Area.Left + midWidth) && (r.Top < root.Area.Top + midHeight)) { FindIntersects(root.mUpperLeft, ref intersects, r); } if ((r.Right > root.Area.Left + midWidth) && (r.Top < root.Area.Top + midHeight)) { FindIntersects(root.mUpperRight, ref intersects, r); } if ((r.Left < root.Area.Left + midWidth) && (r.Bottom > root.Area.Top + midHeight)) { FindIntersects(root.mLowerLeft, ref intersects, r); } if ((r.Right > root.Area.Left + midWidth) && (r.Bottom > root.Area.Top + midHeight)) { FindIntersects(root.mLowerRight, ref intersects, r); } } } /// /// Helper method to insert a world element into a quadtree /// /// Root of the tree to insert the element into /// Element to insert /// Maximum depth to insert the element /// A pointer to the quadtree node where the element is inserted (for moving /// or removing the element at a later time) private QuadTreeNode insert(QuadTreeNode root, WorldObject elem) { Rectangle elemAABB = elem.mBoundingBox.AABB; if (AddToThisNode(root, elemAABB)) { root.mElements.Add(elem); return root; } int midWidth = root.Area.Width / 2; int midHeight = root.Area.Height / 2; int centerX = root.Area.Left + midWidth; int centerY = root.Area.Top + midHeight; if (elemAABB.Left < centerX && elemAABB.Top < centerY) { if (root.mUpperLeft == null) { root.mUpperLeft = new QuadTreeNode(new Rectangle(root.Area.X, root.Area.Y, midWidth, midHeight), root); } return insert(root.mUpperLeft, elem); } else if (elemAABB.Left > centerX && elemAABB.Top < centerY) { if (root.mUpperRight == null) { root.mUpperRight = new QuadTreeNode(new Rectangle(root.Area.X+midWidth, root.Area.Y, root.Area.Width - midWidth, midHeight), root); } return insert(root.mUpperRight, elem); } else if (elemAABB.Left < centerX) { if (root.mLowerLeft == null) { root.mLowerLeft = new QuadTreeNode(new Rectangle(root.Area.X, root.Area.Y+midHeight, midWidth, root.Area.Height - midHeight), root); } return insert(root.mLowerLeft, elem); } else { if (root.mLowerRight == null) { root.mLowerRight = new QuadTreeNode(new Rectangle(root.Area.X+midWidth, root.Area.Y + midHeight, root.Area.Width - midWidth, root.Area.Height - midHeight), root); } return insert(root.mLowerRight, elem); } } /// /// Helper method to determine if a region should be added to the root of a given /// tree. Region is added to the root if it does not fit completely within one /// of the four quadrants of the area stored at the root. /// /// Root of the tree to check /// Region to check /// If the region should be added to the root of the given tree private bool AddToThisNode(QuadTreeNode root, Rectangle region) { int midWidth = root.Area.Width / 2; int midHeight = root.Area.Height / 2; int centerX = root.Area.Left + midWidth; int centerY = root.Area.Top + midHeight; return root.Area.Width <= mMinWidth || ((region.Left < centerX && region.Right > centerX) || (region.Top < centerY && region.Bottom > centerY)); } } public class QuadTreeNode { public QuadTreeNode(Rectangle area, QuadTreeNode parent) { Area = area; mElements = new List(10); Parent = parent; } public Rectangle Area { get; set; } public QuadTreeNode mUpperLeft; public QuadTreeNode mUpperRight; public QuadTreeNode mLowerRight; public QuadTreeNode mLowerLeft; public QuadTreeNode Parent; public List mElements; internal void RemoveElement(WorldObject worldObj) { mElements.Remove(worldObj); } } }