Unity3D数学第四篇:射线与碰撞检测(交互基础篇)

Unity3D数学第一篇:向量与点、线、面(基础篇)

Unity3D数学第二篇:旋转与欧拉角、四元数(核心变换篇)

Unity3D数学第三篇:坐标系与变换矩阵(空间转换篇)

Unity3D数学第四篇:射线与碰撞检测(交互基础篇)

Unity3D数学第五篇:几何计算与常用算法(实用算法篇)

在前几篇中,我们学习了如何用向量表示位移和方向,如何用四元数处理旋转,以及如何在不同坐标系之间进行转换。现在,我们将这些知识融会贯通,来解决 3D 游戏中最常见的交互问题:物体之间是否接触?在哪里接触? 这就是射线检测 (Raycasting)碰撞检测 (Collision Detection) 的核心任务。

1. 射线的概念与作用:精确的“探针”

在 3D 空间中,一条射线 (Ray) 是一个具有起点 (Origin)方向 (Direction) 的无限长直线。你可以把它想象成一束激光,从一个点笔直地射向某个方向。

Unity 中使用 UnityEngine.Ray 结构体来表示射线:

C#

// Unity
// 定义一条从世界坐标 (0, 0, 0) 向上发射的射线
Ray myRay = new Ray(Vector3.zero, Vector3.up);// 定义一条从当前物体位置向前发射的射线
Ray forwardRay = new Ray(transform.position, transform.forward);
1.1 为什么需要射线?

在游戏开发中,射线是进行精确、点对点检测的利器,尤其适用于以下场景:

  • 鼠标/触控拾取: 点击屏幕上的 3D 物体。

  • 射击游戏: 判断子弹是否命中敌人或障碍物。

  • AI 视线/障碍物检测: AI 角色判断前方是否有障碍物或敌人。

  • 交互高亮: 当玩家准星对准某个可互动物体时,高亮显示。

  • 地形检测: 角色在空中时,检测下方是否有地面。

  • 物理模拟辅助: 在自定义物理行为中,判断某个方向上是否有阻碍。

1.2 Physics.Raycast():发射射线进行检测

Unity 的 Physics 类提供了多种射线检测方法,其中最常用的是 Physics.Raycast()

C#

// Unity
// Physics.Raycast 的最常用重载
public static bool Raycast(Ray ray,                     // 要发射的射线out RaycastHit hitInfo,      // 输出参数,用于存储碰撞信息float maxDistance = Mathf.Infinity, // 射线的最大检测距离int layerMask = DefaultRaycastLayers // 要检测的物理层
);
  • 返回值 bool 如果射线击中任何碰撞体,返回 true;否则返回 false

  • Ray ray 要发射的射线实例,包含起点和方向。

  • out RaycastHit hitInfo 这是一个输出参数,如果射线击中物体,所有关于碰撞的详细信息都会填充到这个 RaycastHit 结构体中。

  • maxDistance 射线的最大检测距离。只检测这个距离范围内的碰撞。默认为无限远 (Mathf.Infinity)。

  • layerMask 一个位掩码 (bitmask),用于指定射线只检测哪些物理层 (Layer) 上的物体。这是非常重要的性能优化逻辑控制手段。

1.3 RaycastHit 结构体:碰撞的详细信息

Physics.Raycast() 返回 true 时,RaycastHit 结构体中包含了以下有用的信息:

  • hitInfo.collider:被射线击中的 Collider 组件。

  • hitInfo.transform:被射线击中的 GameObject 的 Transform 组件。

  • hitInfo.gameObject:被射线击中的 GameObject

  • hitInfo.point:射线与碰撞体相交的世界坐标点

  • hitInfo.normal:碰撞点处被击中表面的法线向量(一个单位向量,垂直于表面,指向外)。

  • hitInfo.distance:从射线起点到碰撞点的距离

示例:鼠标点击拾取物体

C#

// Unity
void Update() {// 检查鼠标左键是否按下if (Input.GetMouseButtonDown(0)) {// 1. 从摄像机向鼠标位置发射一条射线// Camera.main.ScreenPointToRay() 内部处理了屏幕坐标到世界射线的转换Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit; // 用于存储碰撞信息// 2. 发射射线,检测最远 100 米范围内的所有碰撞体if (Physics.Raycast(ray, out hit, 100f)) {// 射线击中了物体Debug.Log("你点击了: " + hit.collider.gameObject.name);Debug.Log("点击点世界坐标: " + hit.point);Debug.Log("击中表面的法线: " + hit.normal);// 示例:给被点击的物体一个力Rigidbody hitRb = hit.collider.GetComponent<Rigidbody>();if (hitRb != null) {// 沿着击中点的法线方向施加一个力hitRb.AddForce(hit.normal * 100f, ForceMode.Impulse);}} else {// 没有击中任何物体Debug.Log("你点击了空地。");}}
}
1.4 Physics.RaycastAll():获取所有碰撞

如果一条射线可能穿透多个物体(例如,穿透性子弹),或者你需要检测射线路径上所有的物体,可以使用 Physics.RaycastAll()。它会返回一个 RaycastHit 数组。

C#

// Unity
// 发射一条射线,获取所有碰撞信息
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit[] hits = Physics.RaycastAll(ray, 10f); // 检测 10 米内所有碰撞// 遍历所有碰撞结果
foreach (RaycastHit hit in hits) {Debug.Log("命中物体: " + hit.collider.gameObject.name + ",距离: " + hit.distance);
}
// 注意:RaycastAll 返回的结果是按距离从近到远排序的。

1.5 物理层 (LayerMask) 的重要性

layerMask 参数是进行射线检测时非常重要的优化手段。它允许你只检测特定层级上的物体

  • 设置层级: 在 Unity Editor 中,选择一个 GameObject,在 Inspector 面板的顶部 Layer 下拉菜单中选择或添加层级。

  • 创建 LayerMask:

    C#

    // Unity
    int layerToDetect = LayerMask.GetMask("Ground", "Enemy"); // 只检测 "Ground" 和 "Enemy" 层
    // 或者通过位运算
    int groundLayer = LayerMask.NameToLayer("Ground");
    int enemyLayer = LayerMask.NameToLayer("Enemy");
    int combinedLayerMask = (1 << groundLayer) | (1 << enemyLayer); // 将两个层的位进行或运算// 发射射线时使用 LayerMask
    if (Physics.Raycast(ray, out hit, 100f, layerToDetect)) {// ...
    }
  • 忽略特定层级:

    C#

    // 检测除了 "Player" 层以外的所有层
    int ignorePlayerLayer = ~LayerMask.GetMask("Player");
    if (Physics.Raycast(ray, out hit, 100f, ignorePlayerLayer)) {// ...
    }
  • 性能考量: 合理使用 layerMask 可以极大地减少射线检测的计算量,因为它避免了对不相关层级上的所有碰撞体进行不必要的几何测试。这是优化复杂场景性能的关键一环。


2. 碰撞检测器 (Colliders):3D 物体的“形状”

射线检测是“点-线”与“面”的碰撞。而更广义的碰撞检测,则是两个或多个碰撞体 (Collider) 之间的相互接触。

Unity 的碰撞检测系统依赖于添加到 GameObject 上的 Collider 组件。这些组件定义了物体的物理形状,即使物体的网格模型非常复杂,碰撞体通常是简化的几何形状,以提高性能。

2.1 常见碰撞体类型

Unity 提供了多种内置的碰撞体类型:

  • BoxCollider (盒子碰撞体):

    • 最简单、计算最快的碰撞体之一。

    • 适用于大部分矩形、立方体或近似形状的物体(墙壁、箱子)。

    • 可以通过 sizecenter 属性调整大小和中心偏移。

  • SphereCollider (球体碰撞体):

    • 计算也非常高效。

    • 适用于球形或近似球形的物体(球、角色头部、一些圆形投掷物)。

    • 通过 radiuscenter 调整。

  • CapsuleCollider (胶囊体碰撞体):

    • 圆柱体两端带半球的形状。

    • 非常适合角色控制器:因为它可以很好地模拟人体形状,并且在斜坡上滚动而不是卡住,方便处理角色移动。

    • 通过 radiusheightdirection (X, Y, Z 轴) 调整。

  • MeshCollider (网格碰撞体):

    • 直接使用物体的网格模型作为碰撞形状。

    • 最精确,但也是性能开销最大的碰撞体类型。

    • 警告: 除非必要,不建议在移动的物体上使用 MeshCollider,尤其是不勾选 Convex 选项的非凸 MeshCollider。它会显著增加物理计算量。

    • 通常用于: 不动的复杂地形、大型建筑等作为静态碰撞体。

    • Convex 属性: 勾选后,Unity 会计算一个凸包 (Convex Hull) 作为碰撞体。这会大大降低计算复杂度,允许 MeshCollider 与其他 MeshCollider 发生碰撞(否则两个非凸 MeshCollider 无法互相碰撞)。

  • WheelCollider (车轮碰撞体): 专门用于模拟车辆车轮的物理行为,具有悬挂、摩擦等特性。

  • TerrainCollider (地形碰撞体): 专门用于 Unity 地形 (Terrain) 组件的碰撞检测。

2.2 碰撞体属性
  • Is Trigger (是否为触发器):

    • 勾选: 碰撞体变为触发器。触发器只检测物体的进入、停留、离开,但不产生物理反弹或阻碍。它只会触发 OnTriggerEnter/Stay/Exit 回调。

    • 不勾选: 碰撞体是实体碰撞体。它会产生物理交互,阻碍其他物体,并触发 OnCollisionEnter/Stay/Exit 回调。

    • 应用:

      • 触发器: 区域进入检测(如进入一个房间触发剧情)、拾取物品(进入拾取范围)。

      • 实体碰撞体: 玩家与墙壁的碰撞、子弹击中敌人产生反弹或伤害。

2.3 刚体 (Rigidbody) 的作用

要使碰撞体能够进行物理模拟(受力、重力、反弹、滑动等),它必须依附于一个带有 Rigidbody (刚体) 组件的 GameObject。

  • 没有 Rigidbody 的碰撞体:

    • 只能作为静态碰撞体运动学碰撞体 (Kinematic Collider)。它们不会响应物理力,但可以被带有 Rigidbody 的物体撞击或被射线检测到。

    • 适用于地面、墙壁、不可移动的障碍物等。

  • Rigidbody 的碰撞体:

    • 会受到物理引擎的控制,响应重力、力、扭矩等。

    • 动态碰撞体

    • 适用于玩家角色、箱子、球等需要物理模拟的物体。

    • Is Kinematic 属性: 勾选后,刚体将不受物理引擎控制,但仍然可以通过 transform 手动移动它,且它会影响其他非运动学刚体。这是一种特殊的运动学刚体,常用于需要手动控制但又要参与物理交互的物体(如可移动的平台)。


3. 碰撞回调函数:响应碰撞事件

当两个带有碰撞体的物体发生碰撞或触发事件时,Unity 会调用相应的回调函数。这些函数需要在你的脚本中定义。

3.1 碰撞事件 (Collision Events)

当两个实体碰撞体发生物理接触时触发(至少一个物体需要有 RigidbodyIs Trigger 未勾选)。

  • OnCollisionEnter(Collision collision) 首次接触时调用。

  • OnCollisionStay(Collision collision) 接触过程中每帧调用。

  • OnCollisionExit(Collision collision) 停止接触时调用。

Collision 参数: 包含了碰撞的详细信息,例如:

  • collision.gameObject:与当前物体发生碰撞的另一个 GameObject。

  • collision.collider:与当前物体发生碰撞的另一个 Collider。

  • collision.contacts:一个 ContactPoint 数组,包含所有接触点的详细信息(位置、法线)。

  • collision.relativeVelocity:碰撞发生时的相对速度。

示例:物体碰撞反弹

C#

// Unity
void OnCollisionEnter(Collision collision) {if (collision.gameObject.CompareTag("Wall")) {Debug.Log("我撞到墙了!");// 获取撞击点的法线,用于计算反弹Vector3 collisionNormal = collision.contacts[0].normal;// 如果是球体,可以根据入射速度和法线计算反弹速度// 例如:rb.velocity = Vector3.Reflect(rb.velocity, collisionNormal);}
}
3.2 触发事件 (Trigger Events)

当一个或两个碰撞体是触发器 (Is Trigger 勾选) 时触发。这些事件不会产生物理阻碍,只用于检测进入/离开某个区域。

  • OnTriggerEnter(Collider other) 首次进入触发器时调用。

  • OnTriggerStay(Collider other) 停留在触发器内每帧调用。

  • OnTriggerExit(Collider other) 离开触发器时调用。

Collider other 参数: 触发事件的另一个 Collider 组件。

示例:拾取物品

C#

// Unity (挂在拾取物品上,并勾选其 Collider 的 Is Trigger)
void OnTriggerEnter(Collider other) {if (other.CompareTag("Player")) {Debug.Log("玩家进入了拾取区域,拾取物品!");// 销毁物品或添加到玩家背包Destroy(gameObject);}
}
3.3 碰撞矩阵 (Collision Matrix)

在 Unity 的 Project Settings -> Physics (或 Physics 2D) 中,有一个 Layer Collision Matrix (层碰撞矩阵)。这个矩阵允许你精确控制哪些层之间的物体可以相互碰撞或触发事件。

  • 作用: 这是一个重要的性能优化逻辑隔离工具。你可以禁用不必要的层间碰撞,从而减少物理引擎的计算量。

  • 示例:

    • 禁用“Player”层与“SelfProjectile”(玩家发射的子弹)层的碰撞,避免玩家被自己的子弹击中。

    • 禁用“UI”层与“Environment”层的碰撞,防止射线检测 UI 时错误地击中背景物体。


4. 其他高级碰撞检测方法

除了 Physics.Raycast 和碰撞回调,Unity 还提供了更灵活的检测方法。

4.1 Physics.SphereCast() / BoxCast() / CapsuleCast():带厚度的射线

这些方法就像发射一个有形状的“探针”,而不是一条无限细的射线。它们检测一个球体、盒子或胶囊体沿着一个方向扫过时是否与任何物体发生碰撞。

C#

// Unity
// Physics.SphereCast 示例:检查前方是否能通过
public float sphereRadius = 0.5f;
public float maxDistance = 1f;
public LayerMask obstacleLayer;void Update() {// 从当前位置向前发射一个球体RaycastHit hit;if (Physics.SphereCast(transform.position, sphereRadius, transform.forward, out hit, maxDistance, obstacleLayer)) {Debug.Log("前方有障碍物被球体检测到: " + hit.collider.gameObject.name);// 通常用于角色控制器,避免卡住}
}
  • 应用:

    • 角色控制器: 检测角色前方是否有空间移动,避免穿墙。

    • 扫雷: 坦克或大型载具在移动前检测路径是否畅通。

    • AoE 技能预判: 技能释放前检测范围内是否有敌人。

4.2 Physics.OverlapSphere() / OverlapBox() / OverlapCapsule():范围检测

这些方法检测一个特定形状的区域内,当前有哪些碰撞体存在。它们不涉及运动,只是一个静态的区域查询。

C#

// Unity
// Physics.OverlapSphere 示例:检测半径内所有敌人
public float detectionRadius = 5f;
public LayerMask enemyLayer;void Update() {if (Input.GetKeyDown(KeyCode.Space)) {// 在当前位置周围检测半径 5 米内的所有敌人Collider[] hitColliders = Physics.OverlapSphere(transform.position, detectionRadius, enemyLayer);if (hitColliders.Length > 0) {Debug.Log("检测到附近的敌人:");foreach (Collider collider in hitColliders) {Debug.Log(collider.gameObject.name);// 示例:对敌人施加效果// collider.GetComponent<EnemyHealth>().TakeDamage(10);}} else {Debug.Log("附近没有敌人。");}}
}
  • 应用:

    • AoE 技能: 施放范围伤害技能时,检测范围内的所有目标。

    • 收集物品: 玩家进入特定范围自动拾取。

    • AI 索敌: AI 在自身周围检测是否有敌人进入其警戒范围。

4.3 物理查询优化:非分配版本 (NonAlloc)

Physics.OverlapSpherePhysics.RaycastAll 这样的方法,每次调用时都会分配内存来创建新的数组。如果这些操作在 UpdateFixedUpdate 中频繁调用,可能会导致内存垃圾 (Garbage Collection - GC) 产生,从而引发卡顿。

Unity 提供了这些方法的 NonAlloc 版本,它们接受一个预先创建好的数组作为参数,将结果填充到该数组中,从而避免了运行时内存分配

C#

// Unity (NonAlloc 示例)
private Collider[] _overlapResults = new Collider[50]; // 预先分配一个足够大的数组void FixedUpdate() {// 检测周围敌人,并避免 GCint numColliders = Physics.OverlapSphereNonAlloc(transform.position, detectionRadius, _overlapResults, enemyLayer);for (int i = 0; i < numColliders; i++) {Debug.Log("NonAlloc 命中敌人: " + _overlapResults[i].gameObject.name);}
}

对于性能敏感的游戏,在物理查询频繁的地方使用 NonAlloc 方法是非常重要的优化手段


5. 可视化调试:Debug.DrawLine 与 Gizmos

在开发和调试射线与碰撞检测时,将它们可视化出来至关重要。Unity 提供了 Debug.DrawLineGizmos 来帮助你。

5.1 Debug.DrawLine() / Debug.DrawRay()

这些方法允许你在 Scene 视图中绘制临时的线条和射线,方便你观察它们的起点、方向和长度。只在运行游戏时可见。

C#

// Unity
void Update() {// 绘制一条从当前位置向前延伸 10 米的红线Debug.DrawLine(transform.position, transform.position + transform.forward * 10f, Color.red);// 绘制一条射线 (与 Debug.DrawLine 类似,但更强调起点和方向)Debug.DrawRay(transform.position, transform.forward * 10f, Color.blue);// 绘制射线检测结果RaycastHit hit;if (Physics.Raycast(transform.position, transform.forward, out hit, 10f)) {Debug.DrawLine(transform.position, hit.point, Color.green); // 命中部分绿色Debug.DrawRay(hit.point, hit.normal, Color.yellow); // 绘制法线}
}
5.2 OnDrawGizmos()

OnDrawGizmos() 是一个特殊的 Unity 回调函数,它允许你在 Scene 视图中绘制持久的调试辅助线。即使游戏不运行,你也可以看到它们。

  • Gizmos.color 设置绘制颜色。

  • Gizmos.DrawWireSphere() / DrawSphere() 绘制空心/实心球体。

  • Gizmos.DrawWireCube() / DrawCube() 绘制空心/实心立方体。

  • Gizmos.DrawRay() 绘制射线。

  • Gizmos.matrix 用于在特定变换空间中绘制 Gizmos,例如绘制一个物体局部坐标系下的盒子。

示例:可视化检测范围

C#

// Unity
public float detectionRadius = 5f;
public float raycastDistance = 10f;// 在 Scene 视图中绘制辅助线,无论游戏是否运行
void OnDrawGizmos() {// 绘制球形检测范围Gizmos.color = Color.yellow;Gizmos.DrawWireSphere(transform.position, detectionRadius);// 绘制前方射线检测路径Gizmos.color = Color.red;Gizmos.DrawRay(transform.position, transform.forward * raycastDistance);
}

OnDrawGizmosSelected() 只有当 GameObject 被选中时才绘制 Gizmos。这对于避免 Scene 视图过于混乱非常有用。

合理使用 Debug.DrawLineGizmos,能够让你清晰地看到射线、碰撞体、检测范围等在 3D 空间中的实际表现,从而大大提高调试效率。


6. 常见面试题与深入思考

6.1 面试问答

  1. 问:请解释 Physics.Raycast()Physics.OverlapSphere() 的主要区别和各自的典型应用场景。

    • 考察点: 对射线检测和范围检测不同用途的理解。

    • 解析:

      • Physics.Raycast()

        • 区别: 从一个点向一个方向发射一条无限细的“线”,检测它是否与第一个遇到的碰撞体相交。

        • 应用: 鼠标点击拾取、射击游戏子弹命中、AI 视线检测、单点障碍物检测。它适合点对点的精确打击或探测。

      • Physics.OverlapSphere()

        • 区别: 在一个给定的中心点周围检测一个球形区域内所有存在的碰撞体。它不涉及运动或方向,只是一个静态的范围查询。

        • 应用: 范围伤害(AoE)技能、AI 索敌(检测附近敌人)、拾取区域检测。它适合检测一个区域内的所有目标。

  2. 问:Collider 组件的 Is Trigger 属性有什么作用?当两个碰撞体发生接触时,何时会触发 OnCollisionEnter,何时会触发 OnTriggerEnter

    • 考察点: 对碰撞体模式(实体/触发器)和物理回调的理解。

    • 解析:

      • Is Trigger 作用: 勾选后,Collider 会变为触发器。触发器只检测物体的进入、停留、离开,但不产生物理阻碍或反弹。未勾选时,是实体碰撞体,会参与物理模拟,产生阻碍和反弹。

      • OnCollisionEnter 触发条件:

        • 两个都是实体碰撞体

        • 至少一个碰撞体挂载了 Rigidbody 组件。

        • 它们之间发生了物理接触(碰撞)。

      • OnTriggerEnter 触发条件:

        • 至少一个碰撞体勾选了 Is Trigger

        • 至少一个碰撞体挂载了 Rigidbody 组件。

        • 它们之间发生了接触(穿越)。

      • 总结: OnCollision 处理物理世界的“撞击”,OnTrigger 处理逻辑世界的“进入/离开区域”。

  3. 问:在频繁进行物理查询(例如在 UpdateFixedUpdate 中)时,你如何优化内存分配以避免 GC 抖动?请举例说明。

    • 考察点:NonAlloc 方法的理解和性能优化意识。

    • 解析:

      • Physics.RaycastAll()Physics.OverlapSphere() 等方法在每次调用时会创建新的数组来存储结果,这会导致内存垃圾 (GC Allocations) 的产生。当这些操作频繁执行时,GC 会导致游戏卡顿。

      • 为了避免 GC 抖动,应该使用这些方法的 NonAlloc 版本Physics.RaycastNonAlloc()Physics.OverlapSphereNonAlloc() 等。

      • 优化方法: 预先在类中声明一个足够大的数组(例如 private Collider[] _resultsBuffer = new Collider[50];),然后在每次查询时将结果填充到这个预分配的数组中,而不是让 Unity 每次都创建新数组。这样可以消除运行时内存分配。

      • 示例: int numHits = Physics.OverlapSphereNonAlloc(transform.position, radius, _resultsBuffer, layerMask);

  4. 问:你如何可视化调试射线和碰撞范围,以便在 Scene 视图中看到它们?

    • 考察点: 对 Unity 调试工具的掌握。

    • 解析:

      • Debug.DrawLine() / Debug.DrawRay() 用于在运行时(游戏模式下)在 Scene 视图中绘制临时的线条或射线。它们在下一帧会被清除,适合观察动态的轨迹。

      • OnDrawGizmos() / OnDrawGizmosSelected() 用于在 Unity Editor 的 Scene 视图中绘制持久的辅助图形 (Gizmos),即使游戏没有运行。OnDrawGizmos() 始终绘制,OnDrawGizmosSelected() 仅当 GameObject 被选中时绘制。适合可视化碰撞体范围、检测半径、AI 视野等。

      • 通过设置 Gizmos.color 可以改变绘制颜色,Gizmos.DrawWireSphere()Gizmos.DrawRay() 等方法用于绘制不同形状。

6.2 深入思考:物理查询的几何原理

虽然 Unity 封装了大部分底层细节,但理解物理查询的几何原理可以帮助你更好地解决问题:

  • 射线与平面相交: 几何上,这涉及到解一个线性方程组,找到直线与平面的交点。

  • 射线与球体相交: 这通常归结为解一个二次方程。球体的公式是 (x−cx)2+(y−cy)2+(z−cz)2=r2,射线的参数方程是 P=O+tD。将射线方程代入球体方程,会得到一个关于参数 t 的二次方程,解出 t 即可得到交点。

  • AABB 盒(轴对齐包围盒)碰撞: 这是游戏中最常用的包围盒类型。两个 AABB 盒碰撞的判断非常高效,只需要判断它们在 X、Y、Z 三个轴上的投影是否重叠。如果所有轴都重叠,则发生碰撞。BoxCollider 在底层就是使用 AABB 或 OBB (Oriented Bounding Box) 进行优化计算的。

了解这些几何原理,可以帮助你在需要实现自定义碰撞检测(例如非标准形状的区域检测、射线穿透不规则多边形)时,能够从数学层面构建解决方案。


总结与展望

本篇教程带你深入了解了 3D 游戏开发中不可或缺的射线与碰撞检测

  • 我们学习了射线 (Ray) 的概念、如何利用 Physics.Raycast() 进行精确的单点检测,以及如何使用 RaycastHit 获取详细的碰撞信息。

  • 深入理解了 Unity 中各种碰撞体 (Collider) 的类型(Box, Sphere, Capsule, Mesh 等)及其应用场景,尤其是 Is Trigger 属性的妙用。

  • 掌握了刚体 (Rigidbody) 在物理模拟中的关键作用,以及碰撞体与刚体的搭配规则。

  • 学会了如何利用 OnCollisionOnTrigger 回调来响应物理事件和逻辑事件。

  • 探索了更高级的碰撞检测方法,如带厚度的射线 (*Cast)范围检测 (Overlap*),以及**NonAlloc 版本**进行性能优化的重要性。

  • 强调了使用 Debug.DrawLineGizmos 进行可视化调试的重要性。

射线与碰撞检测是构建游戏交互的核心,无论你的游戏类型如何,它们都将是你最常使用的工具之一。

在下一篇《几何计算与常用算法(实用算法篇)》中,我们将脱离 Unity 的特定组件,回归 3D 数学本身,探讨一些更通用的几何计算方法和实用算法,例如向量投影、点到线/平面的距离、插值以及其他在游戏逻辑中广泛应用的数学技巧。

现在,你对 Unity 的射线和碰撞系统是否已经有了更清晰和全面的认识?在你的开发经历中,有没有遇到过哪些复杂的碰撞检测问题?

Unity3D数学第一篇:向量与点、线、面(基础篇)

Unity3D数学第二篇:旋转与欧拉角、四元数(核心变换篇)

Unity3D数学第三篇:坐标系与变换矩阵(空间转换篇)

Unity3D数学第四篇:射线与碰撞检测(交互基础篇)

Unity3D数学第五篇:几何计算与常用算法(实用算法篇)

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

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

相关文章

数据处理和统计分析——09 数据分组

1 聚合 1.1 简介 在SQL中我们经常使用GROUP BY将某个字段&#xff0c;按不同的取值进行分组&#xff0c;在Pandas中也有groupby()函数&#xff1b;分组之后&#xff0c;每组都会有至少1条数据&#xff0c;将这些数据进一步处理返回单个值的过程就是聚合&#xff0c;比如分组之后…

【数据结构与算法】数据结构初阶:排序内容加餐(一)——快速排序:三路划分、自省排序

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题 &#x1f349;学习方向&#xff1a;C/C方向 ⭐️人生格言&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为…

MySqL(加餐)

范式第一范式数据库表的每一列都是不可分割的原子数据项&#xff0c;而不能是集合&#xff0c;数组&#xff0c;对象等非原子数据。在关系型数据库的设计中&#xff0c;满足第一范式是对关系模式的基本要求。不满足第一范式的数据库就不能被称为关系数据库。第一范式实际上只要…

【redis】基于工业界技术分享的内容总结

Redis 实践指南与核心概念 一、Java 中常用的 Redis 使用场景与实践 缓存&#xff08;Caching&#xff09; 场景&#xff1a;热点数据、频繁访问的数据&#xff0c;如商品详情、用户信息。通过缓存减少数据库压力&#xff0c;提高系统响应速度。 工业界实践&#xff1a; 淘宝…

服务端之nestJS常用异常类及封装自定义响应模块

MENU前言常用异常类&#xff08;由nestjs/common提供&#xff09;示例自定义异常&#xff08;可选&#xff09;自定义响应模块前言 在NestJS中&#xff0c;nestjs/common提供了大量的内置异常类&#xff0c;主要用于在控制器、服务等层抛出特定的HTTP错误响应。 常用异常类&…

数据链路层、NAT、代理服务、内网穿透

目录 一. 以太网 以太网帧格式 二. MAC地址 三. MTU 四. ARP协议 五. NAT NAPT 六. 代理服务器 正向代理 反向代理 七. 内网穿透 八. 内网打洞 一. 以太网 • "以太网" 不是一种具体的网络, 而是一种技术标准; 既包含了数据链路层的内 容, 也包含了一些物理层…

Rust在CentOS 6上的移植

Rust已不支持Cent OS 6 rhel是Redhat 发布的Red Hat Enterprise Linux的简称&#xff0c;使用rhel源代码编译的CentOS&#xff0c;最新的版本是CentOS 7&#xff0c;于2024年停止支持。而更古老的CentOS 6&#xff0c;则在2020年就已经结束了。 而面对如此老旧的系统&#xf…

C++音视频开发:基础面试题

音视频领域技术门槛高&#xff0c;学习资料稀缺&#xff0c;体系化书籍和开发工具有限&#xff0c;新手入门困难。音视频开发涉及众多任务&#xff1a;音频&#xff08;采集、编解码、降噪等&#xff09;、视频&#xff08;采集、编解码、图像处理&#xff09;、实时传输&#…

C++刷题 - 7.27

贪心算法的详细逻辑这个问题的最优解可以用 贪心算法 在 O(N) 时间 内解决。它的核心思想是&#xff1a;每次操作尽可能覆盖最长的连续非零区间&#xff0c;并通过数学分析发现&#xff1a;最小操作次数等于所有“上升台阶”的高度差之和。1. 直观理解假设 steps [1, 2, 3, 2,…

音频3A处理简介之AGC(自动增益控制)

在音频通话和视频会议中&#xff0c;音频自动增益控制AGC模块的主要作用&#xff1a;• 稳定音频信号的输出电平。无论麦克风采集信号的强弱&#xff08;如用户离麦克风远近程度不同&#xff09;&#xff0c;尽可能保证音频采集模块的输出音量保持相对一致&#xff0c;不会偏大…

web前端打包apk包

我用的是HBuilder工具,可视化更便捷&#xff0c;目前我这操作的apk包是不需要上架的&#xff0c;所以跟实际需要上架的可能还有些出入 首先先新建个项目&#xff0c;选择5App模式 把目前需要打包的内容上传到服务器&#xff0c;我们以嵌套的形式进行打包&#xff0c;找到index.…

Ansible提权sudo后执行报错

1.问题 配置了sudo提权信息后&#xff0c;执行ansible-play报错&#xff0c;报错信息如下&#xff1a;2.原因 sudo没有执行**/bin/sh的权限&#xff0c;而ansible脚本中依赖/bin/sh**&#xff0c;所以报错了&#xff1a; 查看日志sudo tail -f /var/log/secure3.解决方式 修改*…

.NET报表控件ActiveReports发布v19.0——正式兼容 .NET 9

ActiveReports 是一款专注于 .NET 和 .NET Core 平台的报表控件。通过拖拽式报表设计器&#xff0c;可以快速地设计 Excel表格、Word文档、图表、数据过滤、数据钻取、精准套打等类型报表&#xff0c;全面满足 WinForm、ASP.NET、ASP.NET MVC、WPF 平台中各种报表的开发需要。同…

SCI论文选词炼句

标准句子不能啰嗦&#xff1b;词不能有问题&#xff0c;得是SCI中经常出现的&#xff0c;符合上下文的。SCI论文中常出现的摸棱两可的词单词涵义例子Architecture指 整体系统设计方案&#xff0c;如网络层次结构、模块组合、激活函数选择等深度学习模型架构Structure更泛泛&…

Qt deleteLater 延迟删除原理

deleteLater 调用 事件发送 void QObject::deleteLater() {QCoreApplication::postEvent(this, new QDeferredDeleteEvent()); }首先该对象继承QObject调用deleteLater&#xff0c; 内部会发送删除事件QCoreApplication::postEvent(this, new QDeferredDeleteEvent()) 到事件循…

TypeScript SDK 升级:通过 Upload Relay 赋能更多应用

自 3 月主网上线以来&#xff0c;Walrus 开发者社区持续展现出强劲的发展势头&#xff1a; 当前 Walrus 已存储超 758 TB 数据&#xff0c;为数百个项目提供支持。在 2025 年 6 月举办的 Sui Overflow 黑客松上&#xff0c;Walrus 成为最受欢迎的数据层。该赛事共收到 599 个项…

C#线程同步(二)锁

目录 1.lock 2.Monitor 3.锁的其它要注意的问题 3.1同步对象的选择 3.2什么时候该上锁 3.3锁和原子性 3.4嵌套锁 3.5 死锁 3.6 性能 4.Mutex 5.Semaphore 1.lock 让我们先看一段代码&#xff1a; class ThreadUnsafe {static int _val1 1, _val2 1;static void G…

鸿蒙智能居家养老系统构思(续二)—— 适老化烹饪中心详细构思

一、背景在“写给华为鸿蒙智家 —— 智能居家养老系统构思”一文中&#xff0c;结合对居家养老的理解及个人体验&#xff0c;提出了基于鸿蒙OS实现居家养老系统的粗略构思。其中包含“吃得好”。当老人到了不能随性外出活动、只能在家消耗时光时&#xff0c;除了一些看看电视、…

高斯透镜公式(调整镜头与感光元件之间的距离时,使得不同距离的物体在感光元件上形成清晰的影像)

当使用定焦镜头时&#xff0c;仍然可以调整镜头与感光元件&#xff08;或胶片&#xff09;之间的距离时&#xff0c;使得不同距离的物体在感光元件上形成清晰的影像。对此可以用高斯透镜公式进行解释&#xff1a; 一、透镜成像的基本原理 在光学中&#xff0c;一个基本的公式是…

预过滤环境光贴图制作教程:第三阶段 - GGX 分布预过滤

核心目标 GGX 分布是 PBR 中模拟粗糙表面高光反射的主流模型,其核心是通过统计分布描述微表面的朝向概率。本阶段的目标是: 基于第一阶段生成的环境图集,预计算 6 个级别的 GGX 过滤结果(对应不同粗糙度); 使用蒙特卡洛采样(Monte Carlo Sampling)加速 GGX 卷积计算;…