文章目录

  • 前言
  • 一、AABB(轴对齐包围盒)
    • 1、基本概念
    • 2、数学表示
    • 3、Unity中的实现
    • 4、实际应用示例
  • 二、OBB(有向包围盒)
    • 1、Physics.ComputePenetration (Unity 物理引擎)
      • 1.1 基本概念
      • 1.2 Unity中的实现
      • 1.3 实际应用示例
    • 2、OBB (SAT) 手动实现
      • 2.1 基本概念
      • 2.2 数学表示
      • 2.3 Unity中的实现
      • 2.4 实际应用示例
    • 3、如何选择?
  • 三、选择建议:
  • 专栏推荐
  • 完结

前言

在Unity游戏开发中,碰撞检测是至关重要的功能。为了优化性能,开发者通常使用包围盒(Bounding Box)技术作为初步的碰撞检测手段。本文将深入探讨两种核心包围盒技术:AABB(轴对齐包围盒)和OBB(有向包围盒)。

一、AABB(轴对齐包围盒)

1、基本概念

AABB(Axis-Aligned Bounding Box)是最简单的包围盒形式,其特点是:

  • 始终与世界坐标系的X/Y/Z轴对齐

  • 不随物体旋转而改变方向

  • 计算简单,性能高效

2、数学表示

一个AABB可以用两个点表示:

  • 最小点(min):包含X/Y/Z的最小值

  • 最大点(max):包含X/Y/Z的最大值

或者用中心点和半尺寸表示:

  • 中心点(center)

  • 半尺寸(extents):从中心到各边界的距离

3、Unity中的实现

Unity内置了Bounds结构体来处理AABB:

// 创建AABB
Bounds bounds = new Bounds(center, size);// 检测AABB相交
bool isIntersecting = bounds.Intersects(otherBounds);

4、实际应用示例

// 获取预制体的AABB包围盒
Bounds GetPrefabBounds(GameObject prefab)
{// 优先使用渲染器包围盒Renderer renderer = prefab.GetComponentInChildren<Renderer>();if (renderer != null) return renderer.bounds;// 其次尝试碰撞器Collider collider = prefab.GetComponentInChildren<Collider>();if (collider != null) return collider.bounds;// 默认返回单位大小return new Bounds(Vector3.zero, Vector3.one);
}/// <summary>
/// AABB碰撞检测,检查新物体是否与已放置物体发生重叠
/// </summary>
/// <param name="position">新物体的中心位置</param>
/// <param name="halfExtents">新物体的半尺寸(从中心到各边界的距离)</param>
/// <param name="existingBounds">已放置物体的包围盒列表</param>
/// <returns>true表示有重叠,false表示无重叠</returns>
bool CheckAABBOverlap(Vector3 position, Vector3 halfExtents, List<Bounds> existingBounds)
{// 1. 创建新物体的包围盒// Bounds构造函数需要中心点和完整尺寸,所以将halfExtents乘以2Bounds newBounds = new Bounds(position, halfExtents * 2);// 2. 遍历所有已放置物体的包围盒foreach (var bounds in existingBounds){// 3. 检查新物体与当前已放置物体是否相交if (bounds.Intersects(newBounds)){// 4. 如果相交则立即返回true(发生重叠)return true;}}// 5. 遍历结束未发现重叠,返回falsereturn false;
}

调用

// 存储已放置物体的位置和大小(用于碰撞检测)
List<Bounds> placedBounds = new List<Bounds>();
//预制体
public GameObiect prefab;Vector3 position = 预制体准备放置的位置;
// 获取预制体大小
Bounds bounds = GetPrefabBounds(prefab);//获取包围盒(Bounds)的半尺寸
halfExtents = bounds.extents;// 检查是否与其他物体重叠
if (CheckOverlap(position, halfExtents, placedBounds)) return;// 实例化预制体
GameObject propInstance = Instantiate(prefab, position, Quaternion.identity);// 添加到已放置列表
placedBounds.Add(new Bounds(position, halfExtents * 2));

它没有考虑物体的旋转。当物体旋转后,使用 Bounds.Intersects() 进行轴对齐包围盒检测会导致误判。

二、OBB(有向包围盒)

1、Physics.ComputePenetration (Unity 物理引擎)

1.1 基本概念

Physics.ComputePenetration 是 Unity 物理引擎提供的一个方法,用于精确计算两个碰撞体之间的穿透情况。它比简单的 Bounds.Intersects 或 Collider.bounds 检测更准确,特别是对于旋转后的物体或非轴对齐的碰撞体。

1.2 Unity中的实现

public static bool Physics.ComputePenetration(Collider colliderA,      // 第一个碰撞体Vector3 positionA,       // 第一个碰撞体的位置Quaternion rotationA,    // 第一个碰撞体的旋转Collider colliderB,      // 第二个碰撞体Vector3 positionB,       // 第二个碰撞体的位置Quaternion rotationB,    // 第二个碰撞体的旋转out Vector3 direction,   // 穿透方向(从A指向B)out float distance       // 穿透深度
);

返回值

  • true → 两个碰撞体发生穿透

  • false → 两个碰撞体没有穿透

1.3 实际应用示例

bool CheckOverlap(Vector3 position, Quaternion rotation, Vector3 size, List<GameObject> placedObjects)
{// 创建一个临时 BoxCollider 用于检测GameObject tempObj = new GameObject("TempCollider");BoxCollider testCollider = tempObj.AddComponent<BoxCollider>();testCollider.size = size; // 注意:size 是完整尺寸(不是 halfExtents)// 检查与所有已放置物体的碰撞foreach (GameObject obj in placedObjects){Collider[] objColliders = obj.GetComponentsInChildren<Collider>();foreach (Collider col in objColliders){Vector3 dir;float dist;if (Physics.ComputePenetration(testCollider, position, rotation,col, col.transform.position, col.transform.rotation,out dir, out dist)){Destroy(tempObj);return true; // 发生重叠}}}Destroy(tempObj);return false; // 无重叠
}

调用

// 修改为存储实际物体
List<GameObject> placedObjects = new List<GameObject>();if (CheckOverlap(position, rotation, halfExtents * 2, placedObjects)) return;GameObject propInstance = Instantiate(prefab, position, rotation);// 实例化后添加
placedObjects.Add(propInstance);

2、OBB (SAT) 手动实现

2.1 基本概念

OBB(Oriented Bounding Box)是更精确的包围盒:

  • 可以随物体旋转/缩放

  • 方向与物体的本地坐标轴一致

  • 使用分离轴定理(SAT)进行相交检测

2.2 数学表示

一个OBB需要存储:

  • 中心点(center)

  • 半尺寸(extents)

  • 旋转(rotation)

2.3 Unity中的实现

Unity没有直接提供OBB结构,但可以自定义实现:

using UnityEngine;/// <summary>
/// 有向包围盒(Oriented Bounding Box)结构体
/// </summary>
public struct OBB
{public Vector3 center;    // 包围盒中心点public Vector3 extents;   // 包围盒半尺寸(从中心到各边的距离)public Quaternion rotation; // 包围盒旋转/// <summary>/// 检测两个OBB是否相交/// </summary>/// <param name="other">另一个OBB</param>/// <returns>true表示相交,false表示不相交</returns>public bool Intersects(OBB other){// 使用分离轴定理(SAT)进行相交检测return SATIntersection(this, other);}/// <summary>/// 分离轴定理(SAT)实现/// </summary>private static bool SATIntersection(OBB a, OBB b){// 获取两个OBB的旋转矩阵Matrix4x4 aRot = Matrix4x4.Rotate(a.rotation);Matrix4x4 bRot = Matrix4x4.Rotate(b.rotation);// 需要测试的15条分离轴:// 6条来自两个OBB的局部坐标轴// 9条来自两两轴的叉积Vector3[] axes = new Vector3[15];// A的3个局部轴(X/Y/Z)axes[0] = aRot.GetColumn(0).normalized; // A的X轴axes[1] = aRot.GetColumn(1).normalized; // A的Y轴axes[2] = aRot.GetColumn(2).normalized; // A的Z轴// B的3个局部轴(X/Y/Z)axes[3] = bRot.GetColumn(0).normalized; // B的X轴axes[4] = bRot.GetColumn(1).normalized; // B的Y轴axes[5] = bRot.GetColumn(2).normalized; // B的Z轴// 计算A和B各轴的叉积(9条轴)// 这些轴是两个OBB各边平面法线的方向axes[6] = Vector3.Cross(axes[0], axes[3]).normalized;  // A.X × B.Xaxes[7] = Vector3.Cross(axes[0], axes[4]).normalized;  // A.X × B.Yaxes[8] = Vector3.Cross(axes[0], axes[5]).normalized;  // A.X × B.Zaxes[9] = Vector3.Cross(axes[1], axes[3]).normalized;  // A.Y × B.Xaxes[10] = Vector3.Cross(axes[1], axes[4]).normalized; // A.Y × B.Yaxes[11] = Vector3.Cross(axes[1], axes[5]).normalized; // A.Y × B.Zaxes[12] = Vector3.Cross(axes[2], axes[3]).normalized; // A.Z × B.Xaxes[13] = Vector3.Cross(axes[2], axes[4]).normalized; // A.Z × B.Yaxes[14] = Vector3.Cross(axes[2], axes[5]).normalized; // A.Z × B.Z// 在每条分离轴上做投影测试foreach (var axis in axes){// 忽略长度接近0的无效轴(叉积结果可能是零向量)if (axis.sqrMagnitude < 0.001f) continue;// 如果在当前轴上投影不重叠,则存在分离轴,OBB不相交if (!OverlapOnAxis(a, b, axis))return false;}// 所有轴上都重叠,则OBB相交return true;}/// <summary>/// 检测两个OBB在指定轴上的投影是否重叠/// </summary>private static bool OverlapOnAxis(OBB a, OBB b, Vector3 axis){// 计算两个OBB在当前轴上的投影半径float aProj = GetProjectionRadius(a, axis);float bProj = GetProjectionRadius(b, axis);// 计算两个中心点在当前轴上的距离float centerDistance = Mathf.Abs(Vector3.Dot(axis, b.center - a.center));// 如果中心距离小于投影半径之和,则投影重叠return centerDistance <= (aProj + bProj);}/// <summary>/// 计算OBB在指定轴上的投影半径/// </summary>private static float GetProjectionRadius(OBB obb, Vector3 axis){// 获取OBB的旋转矩阵Matrix4x4 rot = Matrix4x4.Rotate(obb.rotation);// 计算OBB三个轴向的向量(考虑半尺寸)Vector3 xAxis = rot.GetColumn(0) * obb.extents.x;Vector3 yAxis = rot.GetColumn(1) * obb.extents.y;Vector3 zAxis = rot.GetColumn(2) * obb.extents.z;// 投影半径 = 各轴在测试轴上投影长度的绝对值之和return Mathf.Abs(Vector3.Dot(xAxis, axis))+ Mathf.Abs(Vector3.Dot(yAxis, axis))+ Mathf.Abs(Vector3.Dot(zAxis, axis));}
}

2.4 实际应用示例

/// <summary>
/// 检查新物体是否与已放置物体发生OBB重叠
/// </summary>
/// <param name="position">新物体的中心位置</param>
/// <param name="halfExtents">新物体的半尺寸</param>
/// <param name="rotation">新物体的旋转</param>
/// <param name="existingOBBs">已放置物体的OBB列表</param>
/// <returns>true表示有重叠,false表示无重叠</returns>
bool CheckOverlap(Vector3 position, Vector3 halfExtents, Quaternion rotation, List<OBB> existingOBBs)
{// 创建新物体的OBBOBB newOBB = new OBB(){center = position,    // 设置中心点extents = halfExtents, // 设置半尺寸rotation = rotation   // 设置旋转};// 遍历所有已放置物体的OBBforeach (var obb in existingOBBs){// 如果与任一已放置物体相交,返回trueif (newOBB.Intersects(obb))return true;}// 没有发现重叠return false;
}

调用

// 存储已放置物体的OBB列表
List<OBB> placedOBBs = new List<OBB>();// 尝试在指定位置放置物体
if (!CheckOverlap(position, halfExtents, rotation, placedOBBs))
{// 如果没有重叠,则实例化新物体Instantiate(prop.prefab, position, rotation);// 将新物体的OBB添加到已放置列表placedOBBs.Add(new OBB(){center = position,    // 记录中心位置extents = halfExtents, // 记录半尺寸rotation = rotation   // 记录旋转});
}

3、如何选择?

✅ 使用 OBB (SAT) 的情况

  • 需要检测 大量简单形状(如地牢墙壁、道具摆放)。

  • 在 非物理系统 中使用(如自定义碰撞逻辑)。

  • 追求 极致性能(如子弹碰撞检测)。

✅ 使用 Physics.ComputePenetration 的情况

  • 需要检测 复杂形状(如 MeshCollider)。

  • 需要 穿透信息(如角色控制器解决卡顿)。

  • 已在使用 Unity 物理系统(如 Rigidbody 物体)。

三、选择建议:

  • 对静态物体或简单碰撞使用AABB

  • 对动态旋转物体使用OBB

  • 可采用两阶段检测:先用AABB快速筛选剔除绝对不相交的物体,再用OBB精确判断


专栏推荐

地址
【unity游戏开发入门到精通——C#篇】
【unity游戏开发入门到精通——unity通用篇】
【unity游戏开发入门到精通——unity3D篇】
【unity游戏开发入门到精通——unity2D篇】
【unity实战】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架/工具集开发】
【unity游戏开发——模型篇】
【unity游戏开发——InputSystem】
【unity游戏开发——Animator动画】
【unity游戏开发——UGUI】
【unity游戏开发——联网篇】
【unity游戏开发——优化篇】
【unity游戏开发——shader篇】
【unity游戏开发——编辑器扩展】
【unity游戏开发——热更新】
【unity游戏开发——网络】

完结

好了,我是向宇,博客地址:https://xiangyu.blog.csdn.net,如果学习过程中遇到任何问题,也欢迎你评论私信找我。

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/92551.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/92551.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/92551.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Numpy科学计算与数据分析专题

Numpy科学计算与数据分析 1. Numpy入门&#xff1a;数组操作与科学计算基础 2. Numpy入门&#xff1a;多平台安装与基础环境配置 3. Numpy数组创建与应用入门 4. Numpy数组属性入门&#xff1a;形状、维度与大小 5. Numpy数组索引与切片入门 6. Numpy数组操作入门&#xff1a;…

齐护机器人小智AI_MCP图形化编程控制Arduino_ESP32

齐护机器人小智AI_MCP图形化编程控制Arduino_ESP32 齐护AiTall在项目实践里&#xff0c;我们常常期望达成这样一种场景&#xff1a;借助智能体&#xff08;例如小智 AI&#xff09;来远程操控其他开发板上的设备&#xff0c;这类似于智能家居系统中智能音箱与各类家电的互动模式…

CPO-SVM分类预测+特征贡献SHAP分析,通过特征贡献分析增强模型透明度,Matlab代码实现,引入SHAP方法打破黑箱限制,提供全局及局部双重解释视角

代码功能 该Matlab代码实现了一个基于CPO-SVM冠豪猪算法优化支持向量机的数据分类模型&#xff0c;结合了SHAP可解释性分析&#xff0c;CPO选择最佳的SVM参数c和g。 SVM模型有两个非常重要的参数C与gamma。其中 C是惩罚系数&#xff0c;即对误差的宽容度。c越高&#xff0c;说明…

Failed to restart docker.service: Unit docker.service is masked.

docker.service 被标记为 "masked" 意味着 systemd 已阻止该服务被启动或运行。这通常发生在 Docker Desktop 安装过程中,因为它使用自己的服务管理机制。以下是解决方法: 解决方案: 解除服务的 mask 状态: bash sudo systemctl unmask docker.service sudo sys…

2025 蓝桥杯C/C++国B 部分题解

P12836 [蓝桥杯 2025 国 B] 翻倍 题目描述 给定 nnn 个正整数 A1,A2,…,AnA_1, A_2, \ldots, A_nA1​,A2​,…,An​&#xff0c;每次操作可以选择任意一个数翻倍。 请输出让序列单调不下降&#xff0c;也就是每个数都不小于上一个数&#xff0c;最少需要操作多少次&#xff1f;…

os标准库

os标准库os包提供了操作系统函数&#xff0c;但和操作系统无关。 os包的接口规定为在所有操作系统中都是一致的。 设计为Unix风格的。1. 权限说明 os标准库有大量的文件操作&#xff0c;在创建文件等操作中&#xff0c;需要指的perm。 在go语言中perm是一个uint32类型 在go语言…

QtC++ 中使用 qtwebsocket 开源库实现基于websocket的本地服务开发详解

前言 当前实时通信功能越来越受到重视&#xff0c;无论是在线聊天、实时数据监控还是多人协作工具&#xff0c;都离不开高效、稳定的实时通信技术。WebSocket 作为一种全双工通信协议&#xff0c;为实时通信提供了良好的解决方案。而在 QtC 开发环境中&#xff0c;qtwebsocket …

小程序实时保存优化

背景。避免数据存储后丢失。要求实时保存。问题&#xff1a;保存时出现卡断&#xff0c;输入的内容会被抹除。问题原因。输入频繁速度块&#xff0c;会影响cpu处理速度。解决方案。用户停止输入500ms后开始保存&#xff0c;否则不保存。这里是保存方法&#xff1a;当500ms以内有…

国产化Excel处理组件Spire.XLS教程:使用 C# 将 DataTable 导出为 Excel 文件

在 C# 中将 DataTable 导出为 Excel 文件&#xff0c;是 .NET 开发中常见的任务&#xff0c;广泛应用于报表生成、日志导出、系统间数据共享等场景。通过使用独立的组件库&#xff0c;开发者可以轻松将 DataTable 数据写入 Excel 文件&#xff0c;并应用格式设置&#xff0c;生…

C语言学习笔记——编译和链接

目录1 C程序的执行流程2 翻译环境2.1 预编译2.2 编译2.2.1 词法分析2.2.2 语法分析2.2.3 语法分析2.3 汇编2.4 链接1 C程序的执行流程 用户编写好的C程序不能直接被计算机识别并执行&#xff0c;在执行前&#xff0c;要先将源文件和头文件进行编译&#xff0c;生成目标文件&am…

Flink-1.19.0源码详解9-ExecutionGraph生成-后篇

《Flink-1.19.0源码详解8-ExecutionGraph生成-前篇》前篇已从Flink集群端调度开始解析ExecutionGraph生成的源码&#xff0c;解析了ExecutionGraph的ExecutionJobVertex节点、ExecutionVertex节点、IntermediateResult数据集、IntermediateResultPartition数据集分区与封装Task…

19、阈值分割+blob分析

目录 一、仿射变换 1.变换矩阵 2.在矩阵的基础上添加各种变换形式 3.开始变换 4.计算变换矩阵参数 新算子 二、阈值分割 新算子 三、blob分析案例 1.焊点 2.石头 3.木材 4.车牌 5.骰子 新算子 一、仿射变换 1.变换矩阵 // 产生仿射变换矩阵hom_mat2d_identity…

破解 Django N+1 查询困境:使用 select_related 与 prefetch_related 实践指南

破解 Django N+1 查询困境:使用 select_related 与 prefetch_related 实践指南 开篇引入 数据库查询性能常常是 Web 应用性能瓶颈中的重中之重。Django ORM 以简洁直观的 API 层将 Python 代码与数据库打通,却也可能因默认的惰性加载带来 N+1 查询问题,造成不必要的网络往…

深入解析K-means聚类:从原理到调优实战

一、聚类分析与K-means的核心价值在无监督学习领域&#xff0c;聚类分析是探索数据内在结构的核心技术。​K-means算法因其简洁高效成为最广泛使用的聚类方法&#xff0c;在客户分群、图像压缩、生物信息学等领域应用广泛。其核心目标是将数据集划分为K个簇&#xff0c;实现“簇…

数据结构基础:哈希表、排序和查找算法

目录 一、哈希表 1.哈希算法 2.哈希碰撞 3.哈希表 4.哈希表相关操作 哈希表插入 哈希表遍历 元素查找 哈希表销毁 二、排序算法 1. 排序算法对比 2. 排序算法实现 冒泡排序 选择排序 插入排序 希尔排序 快速排序 三、查找算法 1. 查找算法对比 2. 查找算法实…

Linux内核参数调优:为K8s节点优化网络性能

在高并发微服务环境中&#xff0c;网络性能往往成为K8s集群的瓶颈。本文将深入探讨如何通过精细化的Linux内核参数调优&#xff0c;让你的K8s节点网络性能提升30%以上。引言&#xff1a;为什么网络调优如此重要&#xff1f;作为一名在生产环境中维护过数千节点K8s集群的运维工程…

全家桶” 战略如何重塑智能服务标准?无忧秘书 AI + 智脑 + 数字人协同模式的底层架构解析

在数字化浪潮的推动下&#xff0c;企业对智能化服务的需求日益增长。然而&#xff0c;单一的技术或产品往往难以满足复杂场景下的多样化需求。近年来&#xff0c;“全家桶”战略成为科技行业的一大趋势&#xff0c;通过整合多维度技术与服务&#xff0c;为企业提供全方位的支持…

前端后端之争?JavaScript和Java的特性与应用场景解析

一、名字相似&#xff0c;本质迥异 1.1 历史渊源与命名背景 在编程世界中&#xff0c;很少有两种语言像JavaScript和Java这样&#xff0c;仅仅因为名字的相似性就引发了无数初学者的困惑。然而&#xff0c;这种相似性纯属巧合——或者说是一种营销策略的产物。 JavaScript诞…

【文献分享】Machine learning models提供数据和代码

数据输入及前期信息&#xff1a;ChronoGauge 需要一个基因表达矩阵&#xff0c;其中包括来自多个时间进程 RNA-测序实验的观测数据&#xff0c;用于训练&#xff0c;并且需要有关每个基因在连续光照&#xff08;LL&#xff09;条件下经过光暗&#xff08;LD&#xff09;周期调整…

PHP MySQL Delete 操作详解

PHP MySQL Delete 操作详解 引言 在Web开发中&#xff0c;数据库是存储和管理数据的重要工具。PHP作为一种流行的服务器端脚本语言&#xff0c;与MySQL数据库结合使用可以高效地处理数据。本文将详细介绍PHP中如何使用DELETE语句删除MySQL数据库中的数据。 什么是DELETE语句&am…