❤️UNITY explicación práctica avanzada y detallada del cuadro delimitador tridimensional AABB-6

  • Prefacio

        Los problemas de detección de colisiones se utilizan ampliamente en realidad virtual, diseño y fabricación asistidos por computadora, juegos, robots, etc., y el algoritmo del cuadro delimitador es uno de los métodos importantes para la detección de colisiones.

        Los cuadros delimitadores comunes incluyen:

  1. Cuadro delimitador AABB (cuadro delimitador alineado con el eje)
  2. Esfera
  3. Cuadro delimitador OBB (cuadro delimitador orientado)
  4. Cuadro delimitador de casco convexo (casco convexo)
  5. ...

 Collider en Unity contiene:


  • introducir

        En los juegos, para simplificar la operación de detección de colisiones entre objetos, generalmente se crea una forma geométrica regular para rodear el objeto. Por lo tanto, el cuadro delimitador AABB se denomina cuadro delimitador alineado con el eje .
        El cuadro delimitador AABB tiene una estructura relativamente simple y un espacio de almacenamiento pequeño , pero su compacidad es pobre . Especialmente para formas geométricas irregulares, el espacio redundante es grande . Cuando el objeto gira, no se puede girar en consecuencia (usando el cuadro delimitador OBB).
       Desde una perspectiva algorítmica, solo se necesitan 2 puntos, pointMin y pointMax, para describir el cuadro delimitador AABB.

La diferencia más directa entre el cuadro delimitador AABB y el cuadro delimitador OBB es:
1. El cuadro delimitador AABB no se puede girar
2. El cuadro delimitador OBB se puede girar, es decir, es direccional. 


  • Cuadro delimitador AABB en escena 2D

        La colisión de parches en una escena bidimensional se muestra en la siguiente figura:

Proyectamos las cuatro esquinas de los dos parches azul y amarillo en el eje XY. La
coordenada del punto máximo en la dirección del eje Y del parche azul es Y1, la coordenada del punto mínimo Y2, la coordenada del punto mínimo X1 en el eje X dirección, y la coordenada del punto máximo X2 en el parche amarillo
. La coordenada del punto máximo en la dirección del eje Y del parche es Y3, la coordenada del punto mínimo es Y4, la coordenada del punto mínimo en la dirección del eje X es X3, y la coordenada del punto máximo es X4

        El área roja en la figura es la parte superpuesta en cada eje.
        Se puede ver que la detección de colisiones AABB tiene las siguientes reglas:
        el parche azul y el parche amarillo se proyectan a lo largo de dos ejes de coordenadas respectivamente. Solo cuando ambos ejes de coordenadas se superponen, los dos objetos significan que se ha producido una colisión.


  • Cuadro delimitador AABB en escena 3D

        El cuadro delimitador AABB de un objeto en una escena tridimensional es un hexaedro. Para el sistema de coordenadas bidimensional, solo hay un eje Z más. Por lo tanto, de hecho, la detección de colisión AABB de un objeto en una escena
        tridimensional La escena todavía se puede implementar utilizando la determinación de información de cuatro puntos.

        Es decir: seleccione los dos vértices más grandes y más pequeños de los ocho vértices del objeto A y los ocho vértices del objeto B para comparar.

         Como se muestra en la imagen de arriba: siempre que se determinen las coordenadas de la parte del punto negro en la imagen, se puede determinar toda la información de los ocho vértices.

 Defina la interfaz en código:

 public interface IMathAABB
 {
     Vector3 MinVector { get; }

     Vector3 MaxVector { get; }

     Vector3 Center { get; }

     Vector3[] Corners { get; }

     /// <summary>
     /// Gets the center point of the bounding box.
     /// </summary>
     /// <returns>获取中心点</returns>
     Vector3 GetCenter();

     /// <summary>
     ///  Near face, specified counter-clockwise looking towards the origin from the positive z-axis.
     ///  verts[0] : left top front
     ///  verts[1] : left bottom front
     ///  verts[2] : right bottom front
     ///  verts[3] : right top front
     ///  Far face, specified counter-clockwise looking towards the origin from the negative z-axis.
     ///  verts[4] : right top back
     ///  verts[5] : right bottom back
     ///  verts[6] : left bottom back
     ///  verts[7] : left top back
     /// </summary>
     /// <returns>获取包围盒八个顶点信息</returns>
     void GetCorners();

     /// <summary>
     /// Tests whether this bounding box intersects the specified bounding object.
     /// </summary>
     /// <returns>判断两个包围盒是否碰撞</returns>
     bool Intersects(IMathAABB aabb);

     /// <summary>
     /// check whether the point is in.
     /// </summary>
     /// <returns>返回这个点是否在包围盒中</returns>
     bool ContainPoint(Vector3 point);

     /// <summary>
     /// Sets this bounding box to the smallest bounding box
     /// that contains both this bounding object and the specified bounding box.
     /// </summary>
     /// <returns>生成一个新的包围盒 同时容纳两个包围盒,新的包围盒: min各轴要是其他两个最小的那个,max各轴要是其他两个最大的那个</returns>
     void Merge(IMathAABB box);

     /// <summary>
     /// Sets this bounding box to the specified values.
     /// </summary>
     /// <param name="min"></param>
     /// <param name="max"></param>
     /// <returns>设置</returns>
     void SetMinMax(Vector3 min, Vector3 max);

     /// <summary>
     /// reset min and max value.
     /// </summary>
     /// <returns>重置</returns>
     void ResetMinMax();

     bool IsEmpty();


 }

  • Clase AABBCC

  public class AABBCC : MonoBehaviour, IMathAABB
    {
        //修改此值控制m_CalcMin
        [SerializeField]
        private Vector3 m_Min = -Vector3.one;

        //修改此值控制m_CalcMax
        [SerializeField]
        private Vector3 m_Max = Vector3.one;

        [SerializeField, AABBDisable]
        private Vector3 m_Center = Vector3.zero;

        //保存包围盒八个顶点
        [SerializeField, AABBDisable]
        private Vector3[] m_Corners = new Vector3[8];

        [SerializeField]
        private Transform Target;

        public Vector3 MinVector
        {
            get
            {
                return m_RealCalcMin;
            }
        }

        public Vector3 MaxVector
        {
            get
            {
                return m_RealCalcMax;
            }
        }

        public Vector3[] Corners
        {
            get
            {
                return m_Corners;
            }
        }

        public Vector3 Center
        {
            get
            {
                return m_Center;
            }
        }

        /// <summary>
        /// 实际计算的最小值
        /// </summary>
        private Vector3 m_RealCalcMin;

        /// <summary>
        /// 实际计算的最大值
        /// </summary>
        private Vector3 m_RealCalcMax;

        /// <summary>
        /// 防止在update之前产生碰撞
        /// </summary>
        private void Awake()
        {
            UpdatePosition();
        }

        // Update is called once per frame
        private void Update()
        {
            UpdatePosition();
        }


        /// <summary>
        /// 更新位置
        /// </summary>
        private void UpdatePosition()
        {
            // position
            if (Target != null)
            {
                SetMinMax(m_Min * 0.5f + Target.position, m_Max * 0.5f + Target.position);
            }
            else
            {
                SetMinMax(m_Min * 0.5f + transform.position, m_Max * 0.5f + transform.position);
            }
        }

        public Vector3 GetCenter()
        {
            m_Center.x = 0.5f * (m_RealCalcMin.x + m_RealCalcMax.x);
            m_Center.y = 0.5f * (m_RealCalcMin.y + m_RealCalcMax.y);
            m_Center.z = 0.5f * (m_RealCalcMin.z + m_RealCalcMax.z);
            return m_Center;
        }

        public void GetCorners()
        {
            // 朝着Z轴正方向的面
            // 左上顶点坐标
            m_Corners[0].Set(m_RealCalcMin.x, m_RealCalcMax.y, m_RealCalcMax.z);
            // 左下顶点坐标
            m_Corners[1].Set(m_RealCalcMin.x, m_RealCalcMin.y, m_RealCalcMax.z);
            // 右下顶点坐标
            m_Corners[2].Set(m_RealCalcMax.x, m_RealCalcMin.y, m_RealCalcMax.z);
            // 右上顶点坐标
            m_Corners[3].Set(m_RealCalcMax.x, m_RealCalcMax.y, m_RealCalcMax.z);

            // 朝着Z轴负方向的面
            // 右上顶点坐标
            m_Corners[4].Set(m_RealCalcMax.x, m_RealCalcMax.y, m_RealCalcMin.z);
            // 右下顶点坐标.
            m_Corners[5].Set(m_RealCalcMax.x, m_RealCalcMin.y, m_RealCalcMin.z);
            // 左下顶点坐标.
            m_Corners[6].Set(m_RealCalcMin.x, m_RealCalcMin.y, m_RealCalcMin.z);
            // 左上顶点坐标.
            m_Corners[7].Set(m_RealCalcMin.x, m_RealCalcMax.y, m_RealCalcMin.z);
        }

        public bool Intersects(IMathAABB aabb)
        {
            //就是各轴 互相是否包含,(aabb 包含  当前包围盒)||  (当前的包围盒 包含 aabb)
            return ((m_RealCalcMin.x >= aabb.MinVector.x && m_RealCalcMin.x <= aabb.MaxVector.x) || (aabb.MinVector.x >= m_RealCalcMin.x && aabb.MinVector.x <= m_RealCalcMax.x)) &&
                   ((m_RealCalcMin.y >= aabb.MinVector.y && m_RealCalcMin.y <= aabb.MaxVector.y) || (aabb.MinVector.y >= m_RealCalcMin.y && aabb.MinVector.y <= m_RealCalcMax.y)) &&
                   ((m_RealCalcMin.z >= aabb.MinVector.z && m_RealCalcMin.z <= aabb.MaxVector.z) || (aabb.MinVector.z >= m_RealCalcMin.z && aabb.MinVector.z <= m_RealCalcMax.z));
        }

        public bool ContainPoint(Vector3 point)
        {
            if (point.x < m_RealCalcMin.x) return false;
            if (point.y < m_RealCalcMin.y) return false;
            if (point.z < m_RealCalcMin.z) return false;
            if (point.x > m_RealCalcMax.x) return false;
            if (point.y > m_RealCalcMax.y) return false;
            if (point.z > m_RealCalcMax.z) return false;
            return true;
        }

        public void Merge(IMathAABB box)
        {
            // 计算新的最小点坐标
            m_RealCalcMin.x = Mathf.Min(m_RealCalcMin.x, box.MinVector.x);
            m_RealCalcMin.y = Mathf.Min(m_RealCalcMin.y, box.MinVector.y);
            m_RealCalcMin.z = Mathf.Min(m_RealCalcMin.z, box.MinVector.z);

            // 计算新的最大点坐标
            m_RealCalcMax.x = Mathf.Max(m_RealCalcMax.x, box.MaxVector.x);
            m_RealCalcMax.y = Mathf.Max(m_RealCalcMax.y, box.MaxVector.y);
            m_RealCalcMax.z = Mathf.Max(m_RealCalcMax.z, box.MaxVector.z);

            GetCenter();
            GetCorners();
        }

        public void SetMinMax(Vector3 min, Vector3 max)
        {
            this.m_RealCalcMin = min;
            this.m_RealCalcMax = max;
            GetCenter();
            GetCorners();
        }

        public bool IsEmpty()
        {
            return m_RealCalcMin.x > m_RealCalcMax.x || m_RealCalcMin.y > m_RealCalcMax.y || m_RealCalcMin.z > m_RealCalcMax.z;
        }

        public void ResetMinMax()
        {
            m_RealCalcMin.Set(-1, -1, -1);
            m_RealCalcMax.Set(1, 1, 1);
            GetCenter();
            GetCorners();
        }

    }

La propiedad AABBDisable no puede modificar este valor en la ventana de visualización.

Dibujar líneas: usando Unity.Debug.DrawLine usando:

#if UNITY_EDITOR
            // draw lines
            Debug.DrawLine(Corners[0], Corners[1], m_DebugLineColor);
            Debug.DrawLine(Corners[1], Corners[2], m_DebugLineColor);
            Debug.DrawLine(Corners[2], Corners[3], m_DebugLineColor);
            Debug.DrawLine(Corners[3], Corners[0], m_DebugLineColor);

            Debug.DrawLine(Corners[4], Corners[5], m_DebugLineColor);
            Debug.DrawLine(Corners[5], Corners[6], m_DebugLineColor);
            Debug.DrawLine(Corners[6], Corners[7], m_DebugLineColor);
            Debug.DrawLine(Corners[7], Corners[4], m_DebugLineColor);

            Debug.DrawLine(Corners[0], Corners[7], m_DebugLineColor);
            Debug.DrawLine(Corners[1], Corners[6], m_DebugLineColor);
            Debug.DrawLine(Corners[2], Corners[5], m_DebugLineColor);
            Debug.DrawLine(Corners[3], Corners[4], m_DebugLineColor);
#endif

  •   Efecto

Por supuesto, también puedes usar UnityEngine.Bounds empaquetado por Unity.


  •  Precauciones

        Dado que la colisión se detecta en cada cuadro en Actualización , cuando el objeto se mueve demasiado rápido en un cuadro determinado y excede la distancia del cuadro delimitador, no se producirá la colisión.

       Puede consultar:  Detección discreta discreta ,  Detección continua continua


 Si te ayuda ¿puedes seguirme?

Supongo que te gusta

Origin blog.csdn.net/flj135792468/article/details/120654391
Recomendado
Clasificación