public bool FindLedgeAtPoint(Vector3 start, Vector3 dir, float maxDistance, float deltaHeight, out LedgeInfo ledgeInfo) { int notPlayerLayer = ~(1 << 8); // Horizontal check // RaycastHit hHit; if (!Physics.Raycast(start, dir, out hHit, maxDistance, notPlayerLayer, QueryTriggerInteraction.Ignore)) { goto NoLedge; } bool isMoving = hHit.collider.CompareTag("MovingPlatform"); if (hHit.collider.CompareTag("Freeclimb")) { ledgeInfo = new LedgeInfo(LedgeType.Free, new Vector3(hHit.point.x, start.y, hHit.point.z), -hHit.normal, hHit.collider); return(true); } // Vertical check // RaycastHit vHit; start = hHit.point + (dir * minDepth); start.y += deltaHeight; if (!Physics.Raycast(start, Vector3.down, out vHit, deltaHeight, notPlayerLayer, QueryTriggerInteraction.Ignore)) { goto NoLedge; } Vector3 rightNormal = vHit.normal - Vector3.Scale(vHit.normal, UMath.MakeXYZPositive(-hHit.normal)); // Check angle is shallow enough sideways (think shimmying up when going sideways) // float angleRight = Vector3.Angle(Vector3.up, rightNormal); if (angleRight > maxRightAngle) { goto NoLedge; } // Check angle is shallow enough straight in front (think grabbing bottom of slope) // Vector3 ledgeRight = Vector3.Cross(Vector3.up, hHit.normal).normalized; Vector3 forwardNormal = vHit.normal - Vector3.Scale(vHit.normal, UMath.MakeXYZPositive(ledgeRight)); float angleForward = Vector3.Angle(Vector3.up, forwardNormal); if (angleForward > maxForwardAngle) { goto NoLedge; } // Resolve so Lara grabs correct point on a slope float offset = minDepth * Mathf.Tan(angleForward * Mathf.Deg2Rad); Vector3 ledgePoint = new Vector3(hHit.point.x, vHit.point.y - offset, hHit.point.z); // Check nothing blocking vertically // start = ledgePoint + hHit.normal * 0.1f - Vector3.up * 0.1f; if (Physics.Raycast(start, Vector3.up, 0.2f, notPlayerLayer, QueryTriggerInteraction.Ignore)) { goto NoLedge; } start = ledgePoint + hHit.normal * 0.1f + Vector3.up * 0.1f; if (Physics.Raycast(start, Vector3.down, 0.2f, notPlayerLayer, QueryTriggerInteraction.Ignore)) { goto NoLedge; } // Check minimum depth start = ledgePoint + Vector3.up * 0.1f + hHit.normal * 0.1f; if (Physics.Raycast(start, -hHit.normal, minDepth + 0.1f, notPlayerLayer, QueryTriggerInteraction.Ignore)) { goto NoLedge; } ledgeInfo = new LedgeInfo(LedgeType.Normal, ledgePoint, -hHit.normal, vHit.normal, hHit.collider, angleForward, angleRight); return(true); NoLedge: ledgeInfo = new LedgeInfo(); return(false); }