/// <summary> /// Returns an array of objects that intersect with the specified bounds, if any. Otherwise returns an empty array. See also: IsColliding. /// </summary> /// <param name="checkBounds">Bounds to check. Passing by ref as it improves performance with structs.</param> /// <param name="result">List result.</param> /// <returns>Objects that intersect with the specified bounds.</returns> public void GetColliding(ref LRect checkBounds, List <ColliderProxy> result) { // Are the input bounds at least partially in this node? if (!bounds.Overlaps(checkBounds)) { return; } // Check against any objects in this node for (int i = 0; i < objects.Count; i++) { if (objects[i].Bounds.Overlaps(checkBounds)) { result.Add(objects[i].Obj); } } // Check children if (children != null) { for (int i = 0; i < NUM_CHILDREN; i++) { children[i].GetColliding(ref checkBounds, result); } } }
public bool CheckCollision(ref LRect checkBounds, FuncCollision callback) { // Are the input bounds at least partially in this node? if (!bounds.Overlaps(checkBounds)) { return(false); } // Check against any objects in this node for (int i = 0; i < objects.Count; i++) { var o = objects[i]; if (o.Bounds.Overlaps(checkBounds)) { callback(o.Obj); } } // Check children if (children != null) { for (int i = 0; i < NUM_CHILDREN; i++) { if (children[i].CheckCollision(ref checkBounds, callback)) { return(true); } } } return(false); }
public void CheckCollision(ColliderProxy obj, ref LRect checkBounds) { // Are the input bounds at least partially in this node? if (!bounds.Overlaps(checkBounds)) { return; } // Check against any objects in this node for (int i = 0; i < objects.Count; i++) { var o = objects[i]; if (!ReferenceEquals(o.Obj, obj) && BoundsQuadTree.FuncCanCollide(o.Obj, obj) && o.Bounds.Overlaps(checkBounds) ) { BoundsQuadTree.funcOnCollide(obj, o.Obj); } } // Check children if (children != null) { for (int i = 0; i < NUM_CHILDREN; i++) { children[i].CheckCollision(obj, ref checkBounds); } } }
public LRect GetBounds() { //TODO var col = collider; var tran = transform; var type = (EShape2D)col.TypeId; switch (type) { case EShape2D.Circle: { var radius = ((CCircle)col).radius; return(LRect.CreateRect(tran.pos, new LVector2(radius, radius))); } case EShape2D.AABB: { var halfSize = ((CAABB)col).size; return(LRect.CreateRect(tran.pos, halfSize)); } case EShape2D.OBB: { var radius = ((COBB)col).radius; return(LRect.CreateRect(tran.pos, new LVector2(radius, radius))); } } Debug.LogError("No support type" + type); return(new LRect()); }
/// <summary> /// Check if the specified bounds intersect with anything in the tree. See also: GetColliding. /// </summary> /// <param name="checkBounds">Bounds to check.</param> /// <returns>True if there was a collision.</returns> public bool IsColliding(ColliderProxy obj, ref LRect checkBounds) { // Are the input bounds at least partially in this node? if (!bounds.Overlaps(checkBounds)) { return(false); } // Check against any objects in this node for (int i = 0; i < objects.Count; i++) { var o = objects[i]; if (!ReferenceEquals(o.Obj, obj) && o.Bounds.Overlaps(checkBounds)) { return(true); } } // Check children if (children != null) { for (int i = 0; i < NUM_CHILDREN; i++) { if (children[i].IsColliding(obj, ref checkBounds)) { return(true); } } } return(false); }
/// <summary> /// Private counterpart to the public <see cref="Remove(ColliderProxy, LRect)"/> method. /// </summary> /// <param name="obj">Object to remove.</param> /// <param name="objBounds">3D bounding box around the object.</param> /// <returns>True if the object was removed successfully.</returns> bool SubRemove(ColliderProxy obj, LRect objBounds) { bool removed = false; for (int i = 0; i < objects.Count; i++) { if (ReferenceEquals(objects[i].Obj, obj)) { removed = objects.Remove(objects[i]); break; } } if (!removed && children != null) { int bestFitChild = BestFitChild(objBounds.center); removed = children[bestFitChild].SubRemove(obj, objBounds); } if (removed && children != null) { // Check if we should merge nodes now that we've removed an item if (ShouldMerge()) { Merge(); } } return(removed); }
/// <summary> /// Returns an array of objects that intersect with the specified bounds, if any. Otherwise returns an empty array. See also: IsColliding. /// </summary> /// <param name="collidingWith">list to store intersections.</param> /// <param name="checkBounds">bounds to check.</param> /// <returns>Objects that intersect with the specified bounds.</returns> public void GetColliding(List <ColliderProxy> collidingWith, LRect checkBounds) { //#if UNITY_EDITOR // For debugging //AddCollisionCheck(checkBounds); //#endif rootNode.GetColliding(ref checkBounds, collidingWith); }
// #### PRIVATE METHODS #### /// <summary> /// Used for visualising collision checks with DrawCollisionChecks. /// Automatically removed from builds so that collision checks aren't slowed down. /// </summary> /// <param name="checkBounds">bounds that were passed in to check for collisions.</param> #if UNITY_EDITOR void AddCollisionCheck(LRect checkBounds) { lastBoundsCollisionChecks.Enqueue(checkBounds); if (lastBoundsCollisionChecks.Count > numCollisionsToSave) { lastBoundsCollisionChecks.Dequeue(); } }
/// <summary> /// Check if the specified bounds intersect with anything in the tree. See also: GetColliding. /// </summary> /// <param name="checkBounds">bounds to check.</param> /// <returns>True if there was a collision.</returns> public bool IsColliding(ColliderProxy obj, LRect checkBounds) { //#if UNITY_EDITOR // For debugging //AddCollisionCheck(checkBounds); //#endif return(rootNode.IsColliding(obj, ref checkBounds)); }
public void Init(ColliderPrefab prefab, LVector2 pos, LFloat y, LFloat deg) { this.Prefab = prefab; _bound = prefab.GetBounds(); Transform2D = new CTransform2D(pos, y, deg); unchecked { Id = autoIncId++; } }
/// <summary> /// Removes the specified object at the given position. Makes the assumption that the object only exists once in the tree. /// </summary> /// <param name="obj">Object to remove.</param> /// <param name="objBounds">3D bounding box around the object.</param> /// <returns>True if the object was removed successfully.</returns> public bool Remove(ColliderProxy obj, LRect objBounds) { if (!Encapsulates(bounds, objBounds)) { return(false); } return(false); }
/// <summary> /// Add an object. /// </summary> /// <param name="obj">Object to add.</param> /// <param name="objBounds">3D bounding box around the object.</param> /// <returns>True if the object fits entirely within this node.</returns> public bool Add(ColliderProxy obj, LRect objBounds) { if (!Encapsulates(bounds, objBounds)) { return(false); } SubAdd(obj, objBounds); return(true); }
public void Init(ColliderPrefab prefab, CTransform2D trans) { this.Prefab = prefab; _bound = prefab.GetBounds(); Transform2D = trans; _prePos = Transform2D.pos; _preDeg = Transform2D.deg; unchecked { Id = autoIncId++; } }
public void Init(ColliderPrefab prefab, CTransform2D trans) { this.Prefab = prefab; _bound = prefab.GetBounds(); MaxSideSize = LMath.Max(_bound.halfSize.x, _bound.halfSize.y); Transform2D = trans; _prePos = Transform2D.pos; _preDeg = Transform2D.deg; unchecked { Id = autoIncId++; } }
public void QueryRegion(int layerType, LVector2 pos, LVector2 size, LVector2 forward, FuncCollision callback) { Debug.Trace($"QueryRegion layerType:{layerType} pos:{pos} size:{size} forward:{forward} "); tempCallback = callback; _tempSize = size; _tempForward = forward; _tempPos = pos; var radius = size.magnitude; var checkBounds = LRect.CreateRect(pos, new LVector2(radius, radius)); GetBoundTree(layerType).CheckCollision(ref checkBounds, _CheckRegionOBB); }
/// <summary> /// Removes the specified object at the given position. Makes the assumption that the object only exists once in the tree. /// </summary> /// <param name="obj">Object to remove.</param> /// <param name="objBounds">3D bounding box around the object.</param> /// <returns>True if the object was removed successfully.</returns> public bool Remove(ColliderProxy obj, LRect objBounds) { bool removed = rootNode.Remove(obj, objBounds); // See if we can shrink the octree down now that we've removed the item if (removed) { Count--; Shrink(); } return(removed); }
public void UpdateObj(ColliderProxy obj, LRect bound) { for (int i = 0; i < objects.Count; i++) { if (ReferenceEquals(objects[i].Obj, obj)) { objects[i] = new OctreeObject() { Obj = obj, Bounds = bound }; } } }
/// <summary> /// Removes the specified object at the given position. Makes the assumption that the object only exists once in the tree. /// </summary> /// <param name="obj">Object to remove.</param> /// <param name="objBounds">3D bounding box around the object.</param> /// <returns>True if the object was removed successfully.</returns> public bool Remove(ColliderProxy obj, LRect objBounds) { Debug.Trace($"ColliderProxy Add { obj.Id} objBounds {objBounds}"); bool removed = rootNode.Remove(obj, objBounds); // See if we can shrink the octree down now that we've removed the item if (removed) { Count--; Shrink(); } return(removed); }
/// <summary> /// Add an object. /// </summary> /// <param name="obj">Object to add.</param> /// <param name="objBounds">3D bounding box around the object.</param> public void Add(ColliderProxy obj, LRect objBounds) { // Add object or expand the octree until it can be added int count = 0; // Safety check against infinite/excessive growth while (!rootNode.Add(obj, objBounds)) { Debug.LogError("Grow"); Grow(objBounds.center - rootNode.Center); if (++count > 20) { Debug.LogError("Aborted Add operation as it seemed to be going on forever (" + (count - 1) + ") attempts at growing the octree."); return; } } Count++; }
/// <summary> /// Draws node boundaries visually for debugging. /// Must be called from OnDrawGizmos externally. See also: DrawAllObjects. /// </summary> /// <param name="depth">Used for recurcive calls to this method.</param> public void DrawBoundQuadTreeNode(float depth = 0) { float tintVal = depth / 7; // Will eventually get values > 1. Color rounds to 1 automatically Gizmos.color = new Color(tintVal, 0, 1.0f - tintVal); LRect thisBounds = CreateLRect(Center, new LVector2(adjLength, adjLength)); Gizmos.DrawWireCube(thisBounds.center.ToLVector3().ToVector3(), thisBounds.size.ToLVector3().ToVector3()); if (children != null) { depth++; for (int i = 0; i < NUM_CHILDREN; i++) { children[i].DrawBoundQuadTreeNode(depth); } } Gizmos.color = Color.white; }
public void UpdateObj(ColliderProxy obj, LRect bound) { var node = GetNode(obj); if (node == null) { Add(obj, bound); } else { if (!node.ContainBound(bound)) { Remove(obj); Add(obj, bound); } else { node.UpdateObj(obj, bound); } } }
public void UpdateObj(ColliderProxy obj, LRect bound) { Debug.Trace($"ColliderProxy UpdateObj { obj.Id} objBounds {bound}"); var node = GetNode(obj); if (node == null) { Add(obj, bound); } else { if (!node.ContainBound(bound)) { Remove(obj); Add(obj, bound); } else { node.UpdateObj(obj, bound); } } }
/* * /// <summary> * /// Get the total amount of objects in this node and all its children, grandchildren etc. Useful for debugging. * /// </summary> * /// <param name="startingNum">Used by recursive calls to add to the previous total.</param> * /// <returns>Total objects in this node and its children, grandchildren etc.</returns> * public int GetTotalObjects(int startingNum = 0) { * int totalObjects = startingNum + objects.Count; * if (children != null) { * for (int i = 0; i < 8; i++) { * totalObjects += children[i].GetTotalObjects(); * } * } * return totalObjects; * } */ // #### PRIVATE METHODS #### /// <summary> /// Set values for this node. /// </summary> /// <param name="baseLengthVal">Length of this node, not taking looseness into account.</param> /// <param name="minSizeVal">Minimum size of nodes in this octree.</param> /// <param name="loosenessVal">Multiplier for baseLengthVal to get the actual size.</param> /// <param name="centerVal">Centre position of this node.</param> void SetValues(LFloat baseLengthVal, LFloat minSizeVal, LFloat loosenessVal, LVector2 centerVal) { BaseLength = baseLengthVal; minSize = minSizeVal; looseness = loosenessVal; Center = centerVal; adjLength = looseness * baseLengthVal; // Create the bounding box. LVector2 size = new LVector2(adjLength, adjLength); bounds = CreateLRect(Center, size); LFloat quarter = BaseLength / 4; LFloat childActualLength = (BaseLength / 2) * looseness; LVector2 childActualSize = new LVector2(childActualLength, childActualLength); childBounds = new LRect[NUM_CHILDREN]; childBounds[0] = CreateLRect(Center + new LVector2(-quarter, -quarter), childActualSize); childBounds[1] = CreateLRect(Center + new LVector2(quarter, -quarter), childActualSize); childBounds[2] = CreateLRect(Center + new LVector2(-quarter, quarter), childActualSize); childBounds[3] = CreateLRect(Center + new LVector2(quarter, quarter), childActualSize); }
public void CheckCollision(ref LRect checkBounds, FuncCollision callback) { rootNode.CheckCollision(ref checkBounds, callback); }
public void CheckCollision(LVector2 pos, LFloat radius, FuncCollision callback) { var rect = LRect.CreateRect(pos, new LVector2(radius, radius)); rootNode.CheckCollision(rect, callback); }
public void CheckCollision(ColliderProxy obj, LRect checkBounds) { rootNode.CheckCollision(obj, ref checkBounds); }
public bool ContainBound(LRect bound) { return(Encapsulates(bounds, bound)); }
public void CheckCollision(ColliderProxy obj, FuncCollision callback) { var checkBounds = LRect.CreateRect(obj.pos, new LVector2(obj.MaxSideSize, obj.MaxSideSize)); rootNode.CheckCollision(ref checkBounds, callback); }
/// <summary> /// Private counterpart to the public Add method. /// </summary> /// <param name="obj">Object to add.</param> /// <param name="objBounds">3D bounding box around the object.</param> void SubAdd(ColliderProxy obj, LRect objBounds) { // We know it fits at this level if we've got this far // We always put things in the deepest possible child // So we can skip some checks if there are children aleady if (!HasChildren) { // Just add if few objects are here, or children would be below min size if (objects.Count < NUM_OBJECTS_ALLOWED || (BaseLength / 2) < minSize) { OctreeObject newObj = new OctreeObject { Obj = obj, Bounds = objBounds }; objects.Add(newObj); #if SHOW_NODES obj.UnityTransform?.SetParent(monoTrans, true); #endif obj2Node[obj] = this; return; // We're done. No children yet } // Fits at this level, but we can go deeper. Would it fit there? // Create the 8 children int bestFitChild; if (children == null) { Split(); if (children == null) { Debug.LogError("Child creation failed for an unknown reason. Early exit."); return; } // Now that we have the new children, see if this node's existing objects would fit there for (int i = objects.Count - 1; i >= 0; i--) { OctreeObject existingObj = objects[i]; // Find which child the object is closest to based on where the // object's center is located in relation to the octree's center bestFitChild = BestFitChild(existingObj.Bounds.center); // Does it fit? if (Encapsulates(children[bestFitChild].bounds, existingObj.Bounds)) { children[bestFitChild] .SubAdd(existingObj.Obj, existingObj.Bounds); // Go a level deeper objects.Remove(existingObj); // Remove from here } } } } // Handle the new object we're adding now int bestFit = BestFitChild(objBounds.center); if (Encapsulates(children[bestFit].bounds, objBounds)) { children[bestFit].SubAdd(obj, objBounds); } else { // Didn't fit in a child. We'll have to it to this node instead OctreeObject newObj = new OctreeObject { Obj = obj, Bounds = objBounds }; objects.Add(newObj); obj2Node[obj] = this; } }
/// <summary> /// Checks if outerBounds encapsulates innerBounds. /// </summary> /// <param name="outerBounds">Outer bounds.</param> /// <param name="innerBounds">Inner bounds.</param> /// <returns>True if innerBounds is fully encapsulated by outerBounds.</returns> static bool Encapsulates(LRect outerBounds, LRect innerBounds) { return(outerBounds.Contains(innerBounds.min) && outerBounds.Contains(innerBounds.max)); }