前言
作為碰撞盒的檢測,OBB是一個常用的辦法。網上有很多文章是介紹原理的,具體可以查一下。 以下給出OBB的Unity實現版本。親測可跑。
參考 :www.tuicool.com/articles/IN…
效果
實現
基礎:對象擁有BoxCollider控件(用作包圍盒)和MeshRenderer控件(模型)
步驟:1.更新BoxCollider 2.更新AABB
更新BoxCollider。這個只需要在編輯器中更新一下即可,不需要運行時不斷更新。
using UnityEngine; public class UpdateBoxColliderHelp { private Transform m_Transform; private MeshRenderer m_MeshRenderer; private BoxCollider m_BoxCollider; public UpdateBoxColliderHelp(Transform transform) { m_Transform = transform; m_MeshRenderer = m_Transform.GetComponent<MeshRenderer>(); m_BoxCollider = m_Transform.GetComponent<BoxCollider>(); UpdateBoxCollider(); } public void UpdateBoxCollider() { if (m_MeshRenderer == null || m_BoxCollider == null) { Debug.Log(string.Format("對象{0}沒有指定控件,跳過。", m_Transform.name)); return; } Vector3 position = m_Transform.position; Vector3 scale = m_Transform.localScale; Quaternion rotation = m_Transform.rotation; m_Transform.position = Vector3.zero; m_Transform.localScale = Vector3.one; m_Transform.rotation = new Quaternion(0,0,0,1); m_BoxCollider.size = m_MeshRenderer.bounds.size; m_BoxCollider.center = m_MeshRenderer.bounds.center; m_Transform.position = position; m_Transform.localScale = scale; m_Transform.rotation = rotation; Debug.Log(string.Format("對象{0}的BoxCollider更新完畢。", m_Transform.name)); } }
OBB的實現
using System; using UnityEngine; public class OBBRect { public Transform m_Transform; public BoxCollider m_BoxCollider; private double m_Rotation; public Vector2 m_Extents; public Vector2[] m_Axiss; public OBBRect(Transform transform) { m_Transform = transform; m_BoxCollider = m_Transform.GetComponent<BoxCollider>(); m_Axiss = new Vector2[2]; SetExtents(); } private void SetExtents() { Quaternion rotation = m_Transform.rotation; m_Transform.rotation = new Quaternion(0, 0, 0, 1); Vector3 center = m_BoxCollider.center; Vector3 size = m_BoxCollider.size / 2; Vector3 Point1 = new Vector3(center.x + size.x, center.y, center.z - size.z); Vector3 Point2 = new Vector3(center.x - size.x, center.y, center.z + size.z); Point1 = m_Transform.localToWorldMatrix.MultiplyPoint3x4(Point1); Point2 = m_Transform.localToWorldMatrix.MultiplyPoint3x4(Point2); m_Extents = new Vector2(Mathf.Abs(Point1.x - Point2.x)/2,Mathf.Abs(Point2.z - Point1.z)/2); m_Transform.rotation = rotation; } public float dot(Vector2 a, Vector2 b) { return Mathf.Abs(a.x * b.x + a.y * b.y); } public float getProjectionRadius(Vector2 axis) { return (m_Extents.x * dot(m_Axiss[0], axis) + m_Extents.y * dot(m_Axiss[1], axis)); } public void Update() { m_Rotation = m_Transform.eulerAngles.y * Math.PI / 180; m_Axiss[0] = new Vector2( (float) Math.Cos(m_Rotation), -(float) Math.Sin(m_Rotation)); m_Axiss[1] = new Vector2(-m_Axiss[0].y, m_Axiss[0].x); } public bool intersects(OBBRect other) { Update(); other.Update(); Vector2 distanceVector = new Vector2(m_Transform.position.x - other.m_Transform.position.x, m_Transform.position.z - other.m_Transform.position.z); Vector2[] checkObbVector2s = { m_Axiss[0], m_Axiss[1], other.m_Axiss[0], other.m_Axiss[1], }; for (int index = 0; index < checkObbVector2s.Length; index++) { Vector2 curVector2 = checkObbVector2s[index]; if ((getProjectionRadius(curVector2) + other.getProjectionRadius(curVector2)) <= dot(distanceVector, curVector2)) { return false; } } return true; } }
注意點
1.先更新包圍盒,所有計算(寬高這些)都是基於包圍盒的數據。
2.該算法是做到模型忽略Y軸進行檢測,需要的話可以自己補充一下。思路:多一個軸向計算。
3.計算Sin值和參考文章不一樣,這里是使用-Sin才得到正確的數值。原因我也還沒想到。。。
原文連接:https://juejin.cn/post/6844903857206591501