public static Collision ConvexConvexIntersection(ConvexCollider a, ConvexCollider b) { Vector2 ret = Vector2.zero; int count = 0; for (int i = 0; i < a.len; i++) { Vector2 w = a.gameObject.transform.LocalToWorldPoint(a.points [i]); if (b.IsPointInCollider(w)) { ret += w; count++; } } for (int i = 0; i < b.len; i++) { Vector2 w = b.gameObject.transform.LocalToWorldPoint(b.points [i]); if (a.IsPointInCollider(w)) { ret += w; count++; } } if (count > 0) { Collision s = new Collision(); s.point = ret / count; s.normal = a.GetNormal(s.point); return(s); } return(null); }
public static Collision CircleConvexIntersection(CircleCollider a, ConvexCollider b, bool ImCircle = true) { Vector2 localPoint = b.gameObject.transform.WorldToLocalPoint(a.gameObject.transform.position); Vector2 ret = Vector2.zero; Vector2 nor = Vector2.zero; int count = 0; float area = 0; float realRadA = a.gameObject.transform.LocalToWorldLength(a.radius); float radALocalToB = b.gameObject.transform.WorldToLocalLength(realRadA); float sqrRadALocalToB = radALocalToB * radALocalToB; Vector2 a0 = localPoint - b.points [b.len - 1]; float proj0 = Vector2.Dot(a0, b.deltas[0]) / b.lengths [0]; float rej0 = Math.Abs(Vector2.Cross(a0, b.deltas [0]) / b.lengths [0]); float rejection = 0; if (rej0 <= radALocalToB && 0 <= proj0 && proj0 <= b.lengths [0]) { //ret += b.points [b.len - 1] + b.deltas [0] * MyMath.Clamp(proj0 / b.lengths [0], 0, 1); Vector2 vrej = Vector2.Reject(a0, b.deltas [0]); float vrejMag = vrej.magnitude; float worldVrejMag = b.gameObject.transform.LocalToWorldLength(vrejMag); float darea = (float)(Math.PI * (realRadA - worldVrejMag) * worldVrejMag * Math.Sqrt(1.0 - worldVrejMag * worldVrejMag / realRadA / realRadA)); nor += Vector2.Rotate(b.deltas[0].normalized, MyMath.FloatPIPer2) * darea; ret += (localPoint - vrej) * darea; area += darea; rejection = Math.Max(rejection, realRadA - worldVrejMag); count++; } else if (Vector2.SqrDistance(b.points [0], localPoint) <= sqrRadALocalToB) { count++; float y = radALocalToB - Vector2.Distance(b.points [0], localPoint); Vector2 delta = b.points [0] - localPoint; Vector2 proji = Vector2.Project(b.deltas [0], delta); Vector2 projiPlus1 = Vector2.Project(b.deltas [1], delta); Vector2 reji = b.deltas [0] - proji; Vector2 rejiPlus1 = b.deltas [1] - projiPlus1; float darea = 0.5f * y * y * (reji.magnitude / proji.magnitude + rejiPlus1.magnitude / projiPlus1.magnitude); rejection = Math.Max(rejection, realRadA - b.gameObject.transform.LocalToWorldLength(delta.magnitude)); ret += b.points [1] * darea; nor += (Vector2.Reject(a0, b.deltas [1]) + Vector2.Reject(localPoint - b.points [0], b.deltas [1])).normalized * darea; area += darea; } for (int i = 1; i < b.len - 1; i++) { Vector2 ai = localPoint - b.points [i - 1]; float proj = Vector2.Dot(ai, b.deltas [i]) / b.lengths [i]; float rej = Math.Abs(Vector2.Cross(ai, b.deltas [i]) / b.lengths [i]); if (rej <= radALocalToB && 0 <= proj && proj <= b.lengths [i]) { Vector2 vrej = Vector2.Reject(ai, b.deltas [i]); float vrejMag = vrej.magnitude; float worldVrejMag = b.gameObject.transform.LocalToWorldLength(vrejMag); float darea = (float)(Math.PI * (realRadA - worldVrejMag) * worldVrejMag * Math.Sqrt(1.0 - worldVrejMag * worldVrejMag / realRadA / realRadA)); nor += Vector2.Rotate(b.deltas [i].normalized, MyMath.FloatPIPer2) * darea; ret += (localPoint - vrej) * darea; area += darea; rejection = Math.Max(rejection, realRadA - worldVrejMag); count++; } else if (Vector2.SqrDistance(b.points [i], localPoint) <= sqrRadALocalToB) { count++; float y = radALocalToB - Vector2.Distance(b.points [i], localPoint); Vector2 delta = b.points [i] - localPoint; Vector2 proji = Vector2.Project(b.deltas [i], delta); Vector2 projiPlus1 = Vector2.Project(b.deltas [i + 1], delta); Vector2 reji = b.deltas [i] - proji; Vector2 rejiPlus1 = b.deltas [i + 1] - projiPlus1; float darea = 0.5f * y * y * (reji.magnitude / proji.magnitude + rejiPlus1.magnitude / projiPlus1.magnitude); rejection = Math.Max(rejection, realRadA - b.gameObject.transform.LocalToWorldLength(delta.magnitude)); ret += b.points [i] * darea; nor += (Vector2.Reject(ai, b.deltas [i]) + Vector2.Reject(localPoint - b.points [i], b.deltas [i + 1])).normalized * darea; area += darea; } } int last = b.len - 1; Vector2 al = localPoint - b.points [last - 1]; float projl = Vector2.Dot(al, b.deltas[last]) / b.lengths [last]; float rejl = Math.Abs(Vector2.Cross(al, b.deltas [last]) / b.lengths [last]); if (rejl <= radALocalToB && 0 <= projl && projl <= b.lengths [last]) { Vector2 vrej = Vector2.Reject(al, b.deltas [last]); float vrejMag = vrej.magnitude; float worldVrejMag = b.gameObject.transform.LocalToWorldLength(vrejMag); float darea = (float)(Math.PI * (realRadA - worldVrejMag) * worldVrejMag * Math.Sqrt(1.0 - worldVrejMag * worldVrejMag / realRadA / realRadA)); nor += Vector2.Rotate(b.deltas[last].normalized, MyMath.FloatPIPer2) * darea; ret += (localPoint - vrej) * darea; area += darea; rejection = Math.Max(rejection, realRadA - worldVrejMag); count++; } else if (Vector2.SqrDistance(b.points [last], localPoint) <= sqrRadALocalToB) { count++; float y = radALocalToB - Vector2.Distance(b.points [last], localPoint); Vector2 delta = b.points [last] - localPoint; Vector2 proji = Vector2.Project(b.deltas [last], delta); Vector2 projiPlus1 = Vector2.Project(b.deltas [0], delta); Vector2 reji = b.deltas [last] - proji; Vector2 rejiPlus1 = b.deltas [0] - projiPlus1; float darea = 0.5f * y * y * (reji.magnitude / proji.magnitude + rejiPlus1.magnitude / projiPlus1.magnitude); rejection = Math.Max(rejection, realRadA - b.gameObject.transform.LocalToWorldLength(delta.magnitude)); ret += b.points [last] * darea; //nor += (Vector2.Reject (al, b.deltas [last]) + Vector2.Reject (localPoint - b.points [last], b.deltas [0])).normalized * darea; //nor += area += darea; } bool inCol = b.IsPointInCollider(a.gameObject.transform.position); if (count > 0 || (inCol && b.isTrigger && b.hacked)) { if (inCol) { area = a.area - area; } if (area <= 0) { area = MyMath.dFloat; } Collision s = new Collision(); s.point = b.gameObject.transform.LocalToWorldPoint(ret / area); s.normal = (ImCircle ? -1 : 1) * a.GetNormal(s.point); //s.normal = (ImCircle ? -1 : 1) * b.gameObject.transform.LocalToWorldDirection(nor / area).normalized; if (!ImCircle) { s.normal *= -1; } //Console.WriteLine ("normal " + s.normal); s.area = area; s.collider = ImCircle ? (Collider)b : (Collider)a; s.rejection = rejection; //Console.WriteLine ("Collide " + MainWindow.frameCount); //Console.WriteLine ("normal " + s.normal); return(s); } return(null); }