/// <summary> /// SAT(分離軸定理)による衝突の検出 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="info"></param> bool CheckCollisionWithSAT(Convex a, Convex b, ref SATResult result) { // 2つの凸包を分離する分離線が存在する場合、それは2つの凸包のいずれかの辺に平行である。 // これが凸包同士の分離軸定理なので、これをもとに衝突判定を行い、それを解決するための情報を集める。 // そのためにまずは最も重なりの少ない分離軸を探索する。 // その後、その分離軸に対して最も深くめり込んでいる頂点を処理する。 result.penetration = LARGE; result.edgeNodeA = null; result.edgeNodeB = null; for (int i = 0, j = a.collisionNodes.Count - 1; i < a.collisionNodes.Count; j = i++) { Node edgeNodeA = a.collisionNodes[j]; Node edgeNodeB = a.collisionNodes[i]; Vector2 pa = edgeNodeA.position; Vector2 pb = edgeNodeB.position; Vector2 axis = new Vector2(pb.y - pa.y, pa.x - pb.x).normalized; float aMin, aMax, bMin, bMax, signedAmount; ProjectConvexToAxis(a, axis, out aMin, out aMax); ProjectConvexToAxis(b, axis, out bMin, out bMax); MeasureOverlappedSignedAmount(aMin, aMax, bMin, bMax, out signedAmount); if (signedAmount <= 0f) { // 重なりが存在しない場合はその範囲に分離線が定義できてしまうので、衝突しない。 return(false); } if (signedAmount < result.penetration) { // 重なりが最も小さい場合はその量と辺を構成するノードを記録する。 result.penetration = signedAmount; result.edgeNodeA = edgeNodeA; result.edgeNodeB = edgeNodeB; result.axis = axis; } } return(true); }
/// <summary> /// 最も浅いノードを返す。 /// </summary> /// <param name="convex"></param> /// <param name="result"></param> void FindShallowestPenetratedNode(Convex convex, ref SATResult result) { Vector2 pa = result.edgeNodeA.position; Vector2 pb = result.edgeNodeB.position; float minDistance = LARGE; Node bestNode = null; for (int i = 0; i < convex.collisionNodes.Count; ++i) { Node n = convex.collisionNodes[i]; float distance = Utilities.DistanceToSegment(n.position, pa, pb); if (distance < minDistance) { minDistance = distance; bestNode = n; } } result.penetratedNode = bestNode; }
/// <summary> /// 最も深くめり込んでいるノードを返す。 /// </summary> /// <param name="convex"></param> /// <param name="result"></param> void FindDeepestPenetratedNode(Convex convex, ref SATResult result) { Vector2 pa = result.edgeNodeA.position; Vector2 pb = result.edgeNodeB.position; Vector2 mid = (pa + pb) * 0.5f; float minDistance = LARGE; Node bestNode = null; for (int i = 0; i < convex.collisionNodes.Count; ++i) { // 軸方向に射影した場合に、最も小さいものが一番めり込んでいる。 Node n = convex.collisionNodes[i]; float distance = Vector2.Dot(n.position - mid, result.axis); if (distance < minDistance) { minDistance = distance; bestNode = n; } } result.penetratedNode = bestNode; }