2D空間的OBB碰撞實現

OBB全稱Oriented bounding box,方向包圍盒算法。其表現效果和Unity的BoxCollider並沒有二致。因爲3D空間的OBB須要多考慮一些狀況算法

這裏僅關注2D空間下的OBB。ide

 

實現效果:測試

 

 

網上有許多OBB的講解,其具體步驟也未必同樣,我是這麼作的this

在兩個凸多邊形中找到一根軸,凸多邊形全部在這根軸上的投影點不產生相交,則這兩個凸多邊形不相交。spa

這根軸通常取每一個邊的垂線,逐個投影進行測試。debug

 

這裏先上一個BOX的版本,以下圖:code

 能夠看見在右側方塊的投影軸上,獲得了非相交結果orm

 

Box版本代碼以下:blog

using UnityEngine; public class OBB : MonoBehaviour { public bool enableDebug; public int debug_axisIndex; int mDebugInternalAxisIndex; public Vector2 size; public Color gizmosColor = Color.white; Vector2 P0 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(-size * 0.5f); } } Vector2 P1 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(size.x * 0.5f, -size.y * 0.5f, 0)); } } Vector2 P2 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(size * 0.5f); } } Vector2 P3 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(-size.x * 0.5f, size.y * 0.5f, 0)); } } public bool Intersects(OBB other) { var axis1 = (P1 - P0).normalized; var axis2 = (P3 - P0).normalized; var axis3 = (other.P1 - other.P0).normalized; var axis4 = (other.P3 - other.P0).normalized; mDebugInternalAxisIndex = 0; var isNotIntersect = false; isNotIntersect |= ProjectionIsNotIntersect(this, other, axis1); isNotIntersect |= ProjectionIsNotIntersect(this, other, axis2); isNotIntersect |= ProjectionIsNotIntersect(this, other, axis3); isNotIntersect |= ProjectionIsNotIntersect(this, other, axis4); return isNotIntersect ? false : true; } bool ProjectionIsNotIntersect(OBB x, OBB y, Vector2 axis) { var x_p0 = Vector3.Project(x.P0, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P0, axis), axis)); var x_p1 = Vector3.Project(x.P1, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P1, axis), axis)); var x_p2 = Vector3.Project(x.P2, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P2, axis), axis)); var x_p3 = Vector3.Project(x.P3, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P3, axis), axis)); var y_p0 = Vector3.Project(y.P0, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P0, axis), axis)); var y_p1 = Vector3.Project(y.P1, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P1, axis), axis)); var y_p2 = Vector3.Project(y.P2, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P2, axis), axis)); var y_p3 = Vector3.Project(y.P3, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P3, axis), axis)); var xMin = Mathf.Min(x_p0, x_p1, x_p2, x_p3); var xMax = Mathf.Max(x_p0, x_p1, x_p2, x_p3); var yMin = Mathf.Min(y_p0, y_p1, y_p2, y_p3); var yMax = Mathf.Max(y_p0, y_p1, y_p2, y_p3); if (enableDebug) { if (debug_axisIndex == mDebugInternalAxisIndex) { Debug.DrawRay(Vector3.Project(x.P0, axis), Vector3.one * 0.1f); Debug.DrawRay(Vector3.Project(x.P2, axis), Vector3.one * 0.1f); Debug.DrawRay(Vector3.Project(y.P0, axis), Vector3.one * 0.1f, Color.white * 0.9f); Debug.DrawRay(Vector3.Project(y.P2, axis), Vector3.one * 0.1f, Color.white * 0.9f); Debug.DrawRay(Vector3.zero, Vector3.one * 0.1f, Color.black); Debug.DrawRay(Vector3.zero, axis, Color.yellow); Debug.DrawRay(xMin * Vector3.right, Vector3.one * 0.1f, Color.blue); Debug.DrawRay(xMax * Vector3.right, Vector3.one * 0.1f, Color.cyan); Debug.DrawRay(yMin * Vector3.right, Vector3.one * 0.1f, Color.red * 0.5f); Debug.DrawRay(yMax * Vector3.right, Vector3.one * 0.1f, Color.red * 0.5f); Debug.Log("(yMin >= xMin && yMin <= xMax): " + (yMin >= xMin && yMin <= xMax) + " frame count: " + Time.frameCount); Debug.Log("(yMax >= xMin && yMax <= xMax): " + (yMax >= xMin && yMax <= xMax) + " frame count: " + Time.frameCount); Debug.Log("(xMin >= yMin && xMin <= yMax): " + (xMin >= yMin && xMin <= yMax) + " frame count: " + Time.frameCount); Debug.Log("(xMax >= yMin && xMax <= yMax): " + (xMax >= yMin && xMax <= yMax) + " frame count: " + Time.frameCount); } mDebugInternalAxisIndex++; } if (yMin >= xMin && yMin <= xMax) return false; if (yMax >= xMin && yMax <= xMax) return false; if (xMin >= yMin && xMin <= yMax) return false; if (xMax >= yMin && xMax <= yMax) return false; return true; } void OnDrawGizmos() { var cacheMatrix = Gizmos.matrix; var cacheColor = Gizmos.color; Gizmos.matrix = transform.localToWorldMatrix; Gizmos.color = gizmosColor; Gizmos.DrawWireCube(Vector3.zero, new Vector3(size.x, size.y, 1f)); Gizmos.color = cacheColor; Gizmos.matrix = cacheMatrix; } }
Obb.cs

 

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { public OBB a; public OBB b; void Update() { var isIntersects = a.Intersects(b); if (isIntersects) { a.gizmosColor = Color.red; b.gizmosColor = Color.red; } else { a.gizmosColor = Color.white; b.gizmosColor = Color.white; } } }
Test.cs

 

增長了一個debug開關,能夠單獨查看每一個軸的映射信息。ip

 

 

那麼下面是凸多邊形的版本,垂線經過叉乘獲取:

 

腳本以下:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class OBB : MonoBehaviour { public Vector2[] points = new Vector2[0]; public int debug_Index; int mDebug_Index; public Color gizmosColor = Color.white; public bool Intersects(OBB other) { var isNotIntersect = false; mDebug_Index = 0; for (int i = 1; i <= points.Length; i++) { var p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - 1]); var p1 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i % points.Length]); var axis = Vector3.Cross((p1 - p0), Vector3.forward).normalized; isNotIntersect |= ProjectionIsNotIntersect(this, other, axis); mDebug_Index++; } return isNotIntersect ? false : true; } bool ProjectionIsNotIntersect(OBB x, OBB y, Vector2 axis) { float xMin, xMax, yMin, yMax; GetMinMax(x.transform.localToWorldMatrix, x.points, axis, out xMin, out xMax); GetMinMax(y.transform.localToWorldMatrix, y.points, axis, out yMin, out yMax); if (yMin >= xMin && yMin <= xMax) return false; if (yMax >= xMin && yMax <= xMax) return false; if (xMin >= yMin && xMin <= yMax) return false; if (xMax >= yMin && xMax <= yMax) return false; return true; } void GetMinMax(Matrix4x4 matrix, Vector2[] points, Vector2 projectAxis, out float min, out float max) { min = float.MaxValue; max = float.MinValue; for (int i = 0; i < points.Length; i++) { var p = matrix.MultiplyPoint3x4(points[i]); var projectValue = Vector3.Project(p, projectAxis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(p, projectAxis), projectAxis)); if (projectValue > max) max = projectValue; } for (int i = 0; i < points.Length; i++) { var p = matrix.MultiplyPoint3x4(points[i]); var projectValue = Vector3.Project(p, projectAxis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(p, projectAxis), projectAxis)); if (projectValue < min) min = projectValue; } } void OnDrawGizmos() { var cacheColor = Gizmos.color; Gizmos.color = gizmosColor; for (int i = 1; i <= points.Length; i++) { var p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - 1]); var p1 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i % points.Length]); Gizmos.DrawLine(p0, p1); } Gizmos.color = cacheColor; } }
OBB.cs

 

 

到這裏就結束了,若是是一個具體形狀和點進行比較方法實際上是很是多的,而相似這樣的凸包之間進行相交測試OBB卻是一個蠻實用的辦法。

相關文章
相關標籤/搜索