using UnityEngine; public class CornerCheat : MonoBehaviour { public CircleCollider2D circle; public Rigidbody2D body; EdgeCollider2D edge; [Range(0, .2f)] public float distanceThreshold = .03f; [Range(0, 1)] public float dotThreshold = .96f; [Range(0, 1)] public float lookahead = 1; [Range(0, 1)] public float velocityTweak = .5f; int mask; void Start() { mask = LayerMask.GetMask("Wall"); } void FixedUpdate() { // find any nearby EdgeCollider2D if (edge == null) edge = Physics2D.OverlapCircle(body.position, circle.radius + distanceThreshold, mask) as EdgeCollider2D; if (edge == null) return; // use the current velocity to look ahead where this object will be next frame var center = body.position + body.velocity * (Time.fixedDeltaTime * lookahead); // find the nearest point on the edge collider, unity provides a edge.ClosestPoint() but it was giving inaccurate answers var nearest = FindClosest(center); // work out the distance from the outer radius of the ball to the edge, if it's too far away we bail var distance = Mathf.Abs(Vector2.Distance(nearest, center) - circle.radius); if (distance > distanceThreshold) { edge = null; return; } // figure out the normal var normal = (center - nearest).normalized; // now, we work out if we're at either of the ends // given the way i'm calculating the normal, this breaks once we're "past" the ends var distanceToFirst = Vector2.Distance(edge.points[0], nearest); var distanceToLast = Vector2.Distance(edge.points[edge.pointCount - 1], nearest); var isFirst = distanceToFirst < .01f; var isLast = distanceToLast < .01f; // if we're on the first or last points, round the normal so it's either fully on X or y if (isFirst || isLast) normal = SnapNormal(normal); // get the tangent by rotating the normal 90 degrees var tangent = R90(normal); // calculate the dot product to figure how perpendicular we are to the tangent var dot = Vector2.Dot(tangent, body.velocity.normalized); // if the dot is below some threshold, we're probably just bouncing off the wall not sliding, so we bail if (Mathf.Abs(dot) < dotThreshold) return; // if the dot is negative, we're sliding the "other" way, so we need to flip the tangent if (dot < 0) tangent *= -1; // store the speed we're moving at var speed = body.velocity.magnitude; // then lerp between the current velocity and the corrected velocity scaled by the speed body.velocity = Vector2.Lerp(body.velocity, tangent * speed, velocityTweak); } Vector2 FindClosest(Vector2 center) { var closest = Vector2.zero; var minDistance = float.MaxValue; foreach (var point in edge.points) { var d = Vector2.Distance(point, center); if (d > minDistance) continue; closest = point; minDistance = d; } return closest; } // rotates a vector 90 degrees static Vector2 R90(Vector2 v) { var tmp = v.y; v.y = -v.x; v.x = tmp; return v; } static Vector2 SnapNormal(Vector2 raw) { return Mathf.Abs(raw.x) > Mathf.Abs(raw.y) ? new Vector2(Mathf.Sign(raw.x), 0) : new Vector2(0, Mathf.Sign(raw.y)); } }