/// <summary> /// Generates an OctTree used to improve collision processing. /// </summary> /// <returns>Newly generated OctTree.</returns> private static ArrayList[,] GenerateOctTree() { // Work out a full-size bounding box that encloses all polygons. float minX = 0, minY = 0, maxX = 0, maxY = 0; foreach (CollisionPolygon prePolygon in _polygonList) { if (prePolygon.Transformation.X < minX) minX = prePolygon.Transformation.X; if (prePolygon.Transformation.Y < minY) minY = prePolygon.Transformation.Y; if (prePolygon.Transformation.X + prePolygon.BoundingWidth > maxX) maxX = prePolygon.Transformation.X + prePolygon.BoundingWidth; if (prePolygon.Transformation.Y + prePolygon.BoundingHeight > maxY) maxY = prePolygon.Transformation.Y + prePolygon.BoundingHeight; } int octTreeCellWidth = 128; int octTreeCellHeight = 128; int octTreeXCells = (int)Math.Ceiling((maxX - minX) / (float)octTreeCellWidth); int octTreeYCells = (int)Math.Ceiling((maxY - minY) / (float)octTreeCellHeight); if (_octTree == null || octTreeXCells != _octTreeXCells || octTreeYCells != _octTreeYCells) { // Generate an Oct-Tree. _octTreeXCells = octTreeXCells; _octTreeYCells = octTreeYCells; _octTree = new ArrayList[octTreeXCells, octTreeYCells]; for (int x = 0; x < octTreeXCells; x++) for (int y = 0; y < octTreeYCells; y++) _octTree[x, y] = new ArrayList(); // Generate collision rectangles. _octTreeCollisionRectangles = new CollisionRectangle[octTreeXCells, octTreeYCells]; for (int x = 0; x < octTreeXCells; x++) for (int y = 0; y < octTreeYCells; y++) _octTreeCollisionRectangles[x, y] = new CollisionRectangle(new Transformation(minX + (x * octTreeCellWidth), minY + (y * octTreeCellHeight), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f), octTreeCellWidth, octTreeCellHeight); // Place entities into Oct-Tree cells. for (int x = 0; x < octTreeXCells; x++) for (int y = 0; y < octTreeYCells; y++) { foreach (CollisionPolygon prePolygon in _polygonList) { if (_octTreeCollisionRectangles[x, y].HitTest(prePolygon) == true) _octTree[x, y].Add(prePolygon); } } } else { // Have any polygons moved? If so we need to update the cells they are in. foreach (CollisionPolygon polygon in _polygonList) { if ((!((polygon.PreviousTransformationValid == false || ((polygon.Transformation.X - polygon.PreviousTransformation.X == 0) && (polygon.Transformation.Y - polygon.PreviousTransformation.Y == 0))) && polygon.FrameCount > 0)))// || polygon.BoundingChanged == true) { _octTreeRemovals.Add(polygon); _octTreeAdditions.Add(polygon); } } } // Remove superflous ones. if (_octTreeRemovals.Count != 0) { foreach (CollisionPolygon polygon in _octTreeRemovals) { for (int x = 0; x < octTreeXCells; x++) for (int y = 0; y < octTreeYCells; y++) _octTree[x, y].Remove(polygon); } _octTreeRemovals.Clear(); } // Add aditional ones. if (_octTreeAdditions.Count != 0) { foreach (CollisionPolygon polygon in _octTreeAdditions) { for (int x = 0; x < octTreeXCells; x++) for (int y = 0; y < octTreeYCells; y++) { if (_octTreeCollisionRectangles[x, y].HitTest(polygon) == true) { _octTree[x, y].Add(polygon); } } } _octTreeAdditions.Clear(); } return _octTree; }
/// <summary> /// Attempts to respond to a collision by moving this rectangle out /// of the given rectangle. /// </summary> /// <param name="rectangle">Rectangle that this rectangle is currently penetrating.</param> public void RespondToRectangleCollision(CollisionRectangle rectangle) { // Truncate the positions of the entitys as decimal places tend // to f**k up collision response. _transformation.X = (float)Math.Truncate(_transformation.X); _transformation.Y = (float)Math.Truncate(_transformation.Y); rectangle._transformation.X = (float)Math.Truncate(rectangle._transformation.X); rectangle._transformation.Y = (float)Math.Truncate(rectangle._transformation.Y); // Really crude form of SAT collision response. // Probably a good idea to clean this up a bit, possibly implement // vector projection as well, to remove the if blocks. float rectangleWidth = rectangle._width * Math.Abs(rectangle._transformation.ScaleX), rectangleHeight = rectangle._height * Math.Abs(rectangle._transformation.ScaleY); float rectangleCenterX = rectangle._transformation.X + (rectangleWidth / 2.0f), rectangleCenterY = rectangle._transformation.Y + (rectangleHeight / 2.0f); float thisWidth = _width * Math.Abs(_transformation.ScaleX), thisHeight = _height * Math.Abs(_transformation.ScaleY); float thisCenterX = _transformation.X + (thisWidth / 2.0f), thisCenterY = _transformation.Y + (thisHeight / 2.0f); float rectangleVertexX = 0, rectangleVertexY = 0; float thisVertexX = 0, thisVertexY = 0; if (thisCenterX > rectangleCenterX) if (thisCenterY > rectangleCenterY) { // Were in the bottom-right corner of the rectangle. rectangleVertexX = rectangle._transformation.X + rectangleWidth; rectangleVertexY = rectangle._transformation.Y + rectangleHeight; thisVertexX = _transformation.X; thisVertexY = _transformation.Y; } else { // Were in the top-right corner of the rectangle. rectangleVertexX = rectangle._transformation.X + rectangleWidth; rectangleVertexY = rectangle._transformation.Y; thisVertexX = _transformation.X; thisVertexY = _transformation.Y + thisHeight; } else if (thisCenterY > rectangleCenterY) { // Were in the bottom-left corner of the rectangle. rectangleVertexX = rectangle._transformation.X; rectangleVertexY = rectangle._transformation.Y + rectangleHeight; thisVertexX = _transformation.X + thisWidth; thisVertexY = _transformation.Y; } else { // Were in the top-left corner of the rectangle. rectangleVertexX = rectangle._transformation.X; rectangleVertexY = rectangle._transformation.Y; thisVertexX = _transformation.X + thisWidth; thisVertexY = _transformation.Y + thisHeight; } float xDifference = thisVertexX - rectangleVertexX; float yDifference = thisVertexY - rectangleVertexY; if (Math.Abs(xDifference) > Math.Abs(yDifference)) _transformation.Y -= yDifference; else _transformation.X -= xDifference; }
/// <summary> /// Checks for and returns the polygon at the given point. /// </summary> /// <param name="x">Position on the x-axis.</param> /// <param name="y">Position on the y-axis.</param> /// <returns>Null if no polygon at given point, else true.</returns> public static CollisionPolygon PolygonAtPoint(int x, int y) { CollisionPolygon aPolygon = new CollisionRectangle(new Transformation(x, y, 0, 0, 0, 0, 1, 1, 1), 1, 1); foreach (CollisionPolygon polygon in _polygonList) if (aPolygon != polygon && polygon.HitTest(aPolygon) == true && polygon.Solid == true) return polygon; return null; }
public void EntityHitTestB(ScriptThread thread) { EntityNode entitya = ((NativeObject)thread.GetObjectParameter(0)).Object as EntityNode; if (entitya == null) { DebugLogger.WriteLog((thread.Process.Url != null && thread.Process.Url != "" ? thread.Process.Url : "A script") + " called EntityHitTest with an invalid object.", LogAlertLevel.Error); return; } CollisionRectangle rect = new CollisionRectangle(new Transformation(thread.GetIntegerParameter(1), thread.GetIntegerParameter(2), 0, 0, 0, 0, 1, 1, 1), thread.GetIntegerParameter(3), thread.GetIntegerParameter(4)); rect.Layers = entitya.CollisionPolygon.Layers; thread.SetReturnValue(entitya.CollisionPolygon.HitTest(rect)); }