internal List<BackgroundLayer> DeserializeBackgroundLayers(JArray layers, Camera2D ownerCamera, BoundingRectangle ownerBounds) { List<BackgroundLayer> result = new List<BackgroundLayer>(layers.Count); foreach (var layerData in layers) { BackgroundLayer layer = new BackgroundLayer(ownerCamera, ownerBounds); string resourceName = (string)layerData["resourceName"]; BackgroundScrollDirection direction = (BackgroundScrollDirection)(int)layerData["scrollDirection"]; float scrollRate = (float)layerData["scrollRate"]; layer.BackgroundTextureResourceName = resourceName; layer.ScrollDirection = direction; layer.ScrollRate = scrollRate; result.Add(layer); } return result; }
/// <summary> /// Returns a rectangle that can contain all the given positionables in /// an enumerable. /// </summary> /// <param name="positionables">The enumerable containing the positionables.</param> /// <returns></returns> public static BoundingRectangle GetBoundsOfPositionables(this IEnumerable<IPositionable2> positionables) { if (!positionables.Any()) { return BoundingRectangle.NaN; } // so there's really no elegant initial value for result, except the // bounds of First()... var first = positionables.First(); BoundingRectangle result = new BoundingRectangle(first.Position, first.Size + first.Position); foreach (var positionable in positionables.Skip(1)) // ...so we need to treat it specially { BoundingRectangle bounds = new BoundingRectangle(positionable.Position, positionable.Size + positionable.Position); if (bounds.Left < result.Left) { result.Width += (bounds.X + result.X); result.X = bounds.X; } else if (bounds.Right > result.Right) { result.Width = (bounds.X + bounds.Width); } if (bounds.Top < result.Top) { result.Height = (bounds.Y + result.Y); result.Y = bounds.Y; } else if (bounds.Bottom > result.Bottom) { result.Height = (bounds.Y + bounds.Height); } } return result; }
/// <summary> /// Resolves a collision between a rectangle and the sloped side of /// this triangle. /// </summary> /// <param name="that">The rectangle to resolve.</param> /// <param name="intersection"> /// The intersection between the rectangle and the bounds of this triangle. /// </param> /// <returns> /// The distance to move the rectangle by to resolve this collision. /// </returns> private Vector2 ResolveSlopeCollision(BoundingRectangle that, Vector2 intersection) { Vector2 topCenter = (HorizontalSlopedSide == HorizontalDirection.Left) ? that.TopRight : that.TopLeft; Vector2 bottomCenter = (HorizontalSlopedSide == HorizontalDirection.Left) ? that.BottomRight : that.BottomLeft; Vector2 pointOnSlope = GetPointOnSlope((HorizontalSlopedSide == HorizontalDirection.Left) ? that.Right : that.Left); if (pointOnSlope == Vector2.Zero) { return Vector2.Zero; } if (SlopedSides == RtSlopedSides.TopLeft || SlopedSides == RtSlopedSides.TopRight) { if (bottomCenter.Y > pointOnSlope.Y) { // The bottom-center point is below the slope, resolution needed return new Vector2(0f, -(bottomCenter.Y - pointOnSlope.Y)); } } else if (SlopedSides == RtSlopedSides.BottomLeft || SlopedSides == RtSlopedSides.BottomRight) { if (topCenter.Y < pointOnSlope.Y) { // The top-center point is above the slope, resolution needed return new Vector2(0f, pointOnSlope.Y - topCenter.Y); } } return Vector2.Zero; }
private bool IsSlopeCollision(BoundingRectangle rect, Vector2 intersect) { if (Math.Abs(intersect.X) < Math.Abs(intersect.Y)) { if (VerticalSlopedSide == VerticalDirection.Up) { return intersect.Y < 0f; } else { return intersect.Y > 0f; } } else { if (HorizontalSlopedSide == HorizontalDirection.Left) { return intersect.X < 0f; } else if (HorizontalSlopedSide == HorizontalDirection.Right) { return intersect.X > 0f; } } return false; }
/// <summary> /// Determines if a given rectangle intersects this triangle. /// </summary> /// <param name="rect">The rectangle to check for intersection.</param> /// <returns> /// True if any part of the rectangle intersects this right triangle, /// false if otherwise. /// </returns> public bool Intersects(BoundingRectangle rect) { return rect.Intersects(this); }
/// <summary> /// Returns the depth of the intersection between this triangle and a /// given rectangle. /// </summary> /// <param name="that">The given rectangle.</param> /// <returns>A vector representing the collision depth.</returns> public Vector2 GetIntersectionDepth(BoundingRectangle that) { return GetCollisionResolution(that); }
/// <summary> /// Initializes a new instance of the <see cref="RightTriangle" /> class. /// </summary> /// <param name="bounds"> /// The rectangle that forms the bounds of the triangle. /// </param> /// <param name="slopedSides">Which sides of the triangle are sloped.</param> public RightTriangle(BoundingRectangle bounds, RtSlopedSides slopedSides) { Bounds = bounds; SlopedSides = slopedSides; }
/// <summary> /// Gets the minimum distance to move a given rectangle such that it /// will no longer be intersecting this right triangle. /// </summary> /// <param name="rect">The rectangle to resolve.</param> /// <returns> /// The minimum distance to move the rectangle, which can be directly /// applied to the rectangle's position. /// </returns> /// <remarks> /// This method resolves collision by first determining if the /// bottom-center point (for TopLeft/TopRight triangles) or the /// top-center point (for BottomLeft/BottomRight /// triangles) is within the bounds. If it is, the method treats it as /// a slope collision by checking if the point is between /// the slope and the top. If it isn't, the collision is treated as a /// collision between two rectangles. /// </remarks> public Vector2 GetCollisionResolution(BoundingRectangle rect) { // Vector2 rectCollisionPoint = (this.SlopedSides == // RtSlopedSides.TopLeft || this.SlopedSides == // RtSlopedSides.TopRight) ? rect.BottomCenter : rect.TopCenter; Vector2 rectCollisionPoint = Vector2.Zero; if (SlopedSides == RtSlopedSides.TopLeft) { rectCollisionPoint = rect.BottomRight; } else if (SlopedSides == RtSlopedSides.TopRight) { rectCollisionPoint = rect.BottomLeft; } else if (SlopedSides == RtSlopedSides.BottomLeft) { rectCollisionPoint = rect.TopRight; } else if (SlopedSides == RtSlopedSides.BottomRight) { rectCollisionPoint = rect.TopLeft; } if (!Bounds.IntersectsIncludingEdges(rectCollisionPoint)) { return Bounds.GetCollisionResolution(rect); } else { return ResolveSlopeCollision(rect, Bounds.GetCollisionResolution(rect)); } }
/// <summary> /// Returns a value indicating whether a bounding rectangle is entirely /// contained within this rectangle. /// </summary> /// <param name="that">The other rectangle.</param> /// <returns> /// True if the other rectangle is entirely contained within this one, /// false if otherwise. /// </returns> public bool Within(BoundingRectangle that) { return (that.Left >= Left) && (that.Right <= Right) && (that.Top >= Top) && (that.Bottom <= Bottom); }
/// <summary> /// Returns a value indicating whether another rectangle intersects or /// is tangent to this one. /// </summary> /// <param name="that">The other rectangle.</param> /// <returns> /// True if the other rectangle intersects or is tangent to this one, /// false if otherwise. /// </returns> public bool IntersectsIncludingEdges(BoundingRectangle that) { if (that.Right < Left || that.Left > Right) { return false; } else if (that.Bottom < Top || that.Top > Bottom) { return false; } return true; }
/// <summary> /// Determines if a given rectangle is intersecting this rectangle. /// </summary> /// <param name="that">The rectangle to check.</param> /// <returns> /// True if any part of the other rectangle is intersecting this /// rectangle, false if otherwise. /// </returns> public bool Intersects(BoundingRectangle that) { if (that.Right <= Left || that.Left >= Right) { return false; } else if (that.Bottom <= Top || that.Top >= Bottom) { return false; } return true; }
/// <summary> /// Returns the intersection depth between a given rectangle and this one. /// </summary> /// <param name="that">The rectangle to check.</param> /// <returns> /// The intersection depth between the other rectangle and this one. If /// the rectangles aren't intersecting, a vector with NaN components is returned. /// </returns> /// <remarks> /// This method determines the sign of either component of the result /// by determining the "direction" of the intersection - for example, /// if the right edge of the other rectangle is to the left of the /// center of this rectangle, the "direction" is to the left, and since /// left is negative X, the resulting X component will be negative. /// </remarks> public Vector2 GetIntersectionDepth(BoundingRectangle that) { IntersectionCallCount++; Vector2 result = new Vector2(float.NaN); Vector2 thisCenter = Center; Vector2 thatCenter = that.Center; Vector2 thisHalfSize = Size / 2f; Vector2 thatHalfSize = that.Size / 2f; Vector2 combinedSizes = thisHalfSize + thatHalfSize; // X-axis checks float xDistanceBetweenCenters = thatCenter.X - thisCenter.X; float absXDistance = Math.Abs(xDistanceBetweenCenters); float signXDistance = (xDistanceBetweenCenters >= 0f) ? 1f : -1f; if (absXDistance < combinedSizes.X) { result.X = signXDistance * (combinedSizes.X - Math.Abs(xDistanceBetweenCenters)); } // Y-axis checks float yDistanceBetweenCenters = thatCenter.Y - thisCenter.Y; float absYDistance = Math.Abs(yDistanceBetweenCenters); float signYDistance = (yDistanceBetweenCenters >= 0f) ? 1f : -1f; if (absYDistance < combinedSizes.Y) { result.Y = signYDistance * (combinedSizes.Y - Math.Abs(yDistanceBetweenCenters)); } return result; // WYLO: We're returning wrong results here, or at least results that // we didn't get before. If a sprite and tile have the same X // position and same width, this method returns 0 for X, where the // old one returned 16. Maybe we can apply the sign(a)*(b-|a|) to // Right and Left instead? Do some math and find out. }
/// <summary> /// Returns the distance to move a given rectangle in order to properly /// resolve a collision. /// </summary> /// <param name="that">The rectangle to check.</param> /// <returns> /// A value that can be applied to the position of the rectangle to /// move it the minimum distance such that it won't be intersecting /// this one. /// </returns> /// <remarks> /// Internally, this method uses the GetIntersectionDepth() method to /// determine the intersection depth. It then determines which of the /// two axes has the smallest absolute value and zeroes out the other. /// This way of collision resolution is called the "shallowest edge" /// method, and it allows quick determination of which axis to resolve along. /// </remarks> public Vector2 GetCollisionResolution(BoundingRectangle that) { Vector2 intersection = GetIntersectionDepth(that); return GetCollisionResolution(intersection); }
/// <summary> /// Gets a position to place another rectangle at such that both this /// and that's Center lines are equal. /// </summary> /// <param name="that">The rectangle to align.</param> /// <returns>A position to move the rectangle to.</returns> public Vector2 GetCenterAlignedInColumnPosition(BoundingRectangle that) { float halfWidth = that.Width / 2f; return new Vector2(Center.X - halfWidth, that.Y); }
/// <summary> /// Initializes a new instance of the <see cref="BackgroundLayer" /> class. /// </summary> /// <param name="ownerCamera"> /// The camera of the section that contains this layer. /// </param> /// <param name="sectionBounds"> /// The bounds of the section that contains this layer. /// </param> public BackgroundLayer(Camera2D ownerCamera, BoundingRectangle sectionBounds) { camera = ownerCamera; this.sectionBounds = sectionBounds; }