IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> ??UNITY实战进阶-三维AABB包围盒详解-6 -> 正文阅读

[游戏开发]??UNITY实战进阶-三维AABB包围盒详解-6

  • 前言

? ? ? ? 碰撞检测问题在虚拟现实、计算机辅助设计与制造、游戏、机器人等方面都有着广泛的应用,而包围盒算法是进行碰撞检测的重要方法之一。

? ? ? ? 而常见的包围盒有:

  1. AABB包围盒(Axis-aligned bounding box)
  2. 包围球(Sphere)
  3. OBB包围盒(Oriented bounding box)
  4. 凸包包围盒(Convex Hull)
  5. ...

?在Unity中的Collider包含:


  • 介绍

????????在游戏中,为了简化物体之间的碰撞检测运算,通常会对物体创建一个规则的几何外形将其包围。故AABB包围盒被称为轴对齐包围盒
????????AABB包围盒构造比较简单存储空间小,但紧密性差,尤其对不规则几何形体,冗余空间很大,当对象旋转时,无法对其进行相应的旋转(使用OBB包围盒)。
? ? ? ?从算法角度来看,只需要2个点pointMinpointMax即可描述AABB包围盒。

AABB包围盒与OBB包围盒的最直接的区别就是:
1.AABB包围盒是不可以旋转的
2.OBB包围盒是可以旋转的,也就是有向的。?


  • 二维场景中AABB包围盒

? ? ? ? 二维场景中的面片碰撞如下图所示:

我们将蓝黄两个面片各自的4个角投影到XY轴上
蓝色面片的Y轴方向最大点坐标为Y1,最小点坐标Y2,X轴方向最小点坐标X1,最大点坐标X2
黄色面片的Y轴方向最大点坐标为Y3,最小点坐标Y4,X轴方向最小点坐标X3,最大点坐标X4

????????图中红色区域为各轴上的的重叠部分。
????????可以看出,AABB碰撞检测具有如下规则:
????????蓝色面片与黄色面片分别沿两个坐标轴的投影,只有在两个坐标轴都发生重叠的情况下,两个物体才意味着发生了碰撞。


  • 三维场景中AABB包围盒

????????三维场景中物体的AABB包围盒是一个六面体,对于二维坐标系来讲只是多了一个Z轴
????????所以实际上在三维场景中物体的AABB碰撞检测依然可以采用四个点信息的判定来实现。

? ? ? ? 即:从A物体的八个顶点与B物体的八个顶点分别选出两个最大与最小的顶点进行对比。

?????????如上图所示:只要确定了图中黑色点部分的坐标,就可以确定八个顶点的全部信息了。

?代码中定义接口:

 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();


 }

  • 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();
        }

    }

AABBDisable属性为在视图窗口无法修改此值

画线:使用Unity.Debug.DrawLine使用:

#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

  • ? 效果

当然你也可以是用Unity封装好的UnityEngine.Bounds


  • ?注意事项

????????由于是在Update中每一帧去检测碰撞,所以当物体在某一帧移速过快,超过包围盒的距离,就会导致碰撞不产生。

? ? ? ?可以参考:Discrete?离散检测、Continuous?连续检测


?如果对你有帮助的话,能否关注一波

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-10-11 17:50:07  更:2021-10-11 17:52:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 2:46:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码