ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Unity刚体Rigidbody实战:5分钟搞定物体自由落体与碰撞检测(附避坑指南)

2026/8/4 19:50:39 拓冰建站 浏览量
Unity刚体Rigidbody实战:5分钟搞定物体自由落体与碰撞检测(附避坑指南)

1. 项目概述:从“自由落体”到“真实交互”的必经之路

在Unity的世界里,让一个物体从空中掉下来,大概是每个开发者最早接触的物理模拟之一。听起来简单,不就是给个重力吗?但当你真正动手,想让一个方块砸到地面后还能弹两下,或者让一堆小球互相碰撞、堆叠出逼真的物理效果时,问题就来了:为什么我的物体穿过了地面?为什么碰撞后速度变得诡异?为什么物理表现时好时坏?这些“坑”,几乎每个Unity开发者都踩过,而解决它们的关键,就在于对Rigidbody(刚体)组件的透彻理解。

这个项目标题“Unity刚体Rigidbody实战:5分钟搞定物体自由落体与碰撞检测(附避坑指南)”,精准地戳中了从入门到进阶的痛点。它承诺的不仅是“实现”,更是“避坑”。对于新手,这意味着能快速搭建一个可运行的物理demo,建立信心;对于有一定经验的开发者,这意味着能系统性地排查那些隐藏在简单操作背后的复杂问题,比如碰撞矩阵的配置、物理材质的设置、刚体属性的细微差别对性能的影响等。Rigidbody是Unity物理引擎的基石,它赋予游戏对象模拟真实世界物理行为的能力——质量、阻力、重力、碰撞响应。而“自由落体与碰撞检测”则是这块基石上最经典、也最考验基础的两个应用场景。

接下来,我将以一个从业多年的视角,带你不仅“搞定”这个5分钟的基础操作,更深入拆解其背后的每一个参数、每一种设置,并附上我踩过无数坑才总结出的实战指南。我们会从最核心的设计思路开始,一步步拆解到代码实现和问题排查,确保你不仅能复现,更能理解为什么这么做,以及如何应对各种意外情况。

2. 核心思路与组件选型:为什么是Rigidbody+Collider?

在Unity中实现物理交互,一个最经典且必须的组合就是Rigidbody(刚体)加Collider(碰撞器)。这个组合的分工非常明确,理解这一点是避免后续一切混乱的基础。

Collider(碰撞器):它定义了游戏对象的“物理形状”。你可以把它想象成一个不可见的、坚硬的“外壳”。这个外壳告诉物理引擎:“我的物体占这么大一块空间,形状是这样的(可能是盒子、球体、胶囊体,或者更复杂的网格)。”但仅仅有形状是不够的,这个外壳本身是“静态”的,它不会对力产生反应,也不会主动移动。它只负责回答一个问题:“我有没有和别的‘外壳’碰到一起?”

Rigidbody(刚体):这才是让物体“活”起来的组件。它赋予了对象物理属性,如质量(Mass)、阻力(Drag)、角阻力(Angular Drag),并使其受到物理引擎力的影响,最典型的就是重力。更重要的是,它负责处理“碰撞响应”。当两个都带有RigidbodyCollider发生碰撞时,物理引擎会根据它们的质量、速度、碰撞角度等,计算出碰撞后的速度、旋转变化,从而实现反弹、滑动、滚动等效果。

所以,回到我们的“自由落体”场景。要让一个物体下落,它必须受到重力。在Unity中,重力是一个全局的力场(在Edit -> Project Settings -> Physics中设置),但只有添加了Rigidbody组件的对象才会受到这个力场的影响。同时,要检测它是否“碰到”地面,它和地面都必须有Collider来定义彼此的边界。而碰撞发生后,物体是停下来还是弹起来,则由Rigidbody的属性和碰撞双方的物理材质共同决定。

注意:一个常见的误解是,认为移动的物体才需要Rigidbody。实际上,对于静止的、作为碰撞边界的地面或墙壁,通常需要Collider,而不需要Rigidbody。这种物体在物理引擎中被视为“静态碰撞体”(Static Collider),它无限重,不会因碰撞而移动,但能有效地阻挡和反弹其他动态刚体。如果你给地面也加了Rigidbody,它就会变成一个“动态刚体”,可能会被掉落的物体砸得微微移动,这通常不是我们想要的效果。

方案选型考量

  1. 动态刚体(Dynamic Rigidbody):这是我们下落物体的标准选择。Is Kinematic(是否为运动学)属性为false。它完全受物理引擎控制(重力、力、碰撞)。
  2. 静态碰撞体(Static Collider):这是我们地面的标准选择。只有Collider,没有Rigidbody。性能最优,适用于永远不会移动的环境。
  3. 运动学刚体(Kinematic Rigidbody):将RigidbodyIs Kinematic设为true。此时,物理引擎不会用力和碰撞来驱动它,但它可以通过脚本直接变换位置(Transform)来移动,并且能推动其他动态刚体。它适用于由玩家直接控制(如角色控制器初期方案)、或者需要复杂脚本动画但又需要碰撞响应的物体。在我们的自由落体示例中不适用

明确了“动态刚体下落,静态碰撞体接地”这个核心组合后,我们就能避免很多初级错误,比如物体不下落(没加Rigidbody)或者碰撞没反应(没加Collider)。

3. 环境准备与基础场景搭建

理论清晰后,我们开始动手。这个过程确实可以在5分钟内完成,但每一步的设置都关乎最终效果的稳定性和真实性。

3.1 创建基础场景与对象

  1. 打开Unity,创建一个新项目(3D核心模板即可)。
  2. 在Hierarchy面板右键,创建两个3D Object:一个Cube(改名为FallingObject),一个Plane(改名为Ground)。Plane默认就是一个很大的平面,非常适合作为地面。
  3. 调整FallingObject的位置,将其Y轴坐标设为5左右,让它悬在空中。
  4. 调整Ground的位置和旋转,确保它水平放置(Rotation为0,0,0)。

3.2 添加物理组件

  1. 为地面(Ground)添加碰撞器:选中Ground,你会在Inspector面板看到它自带一个Mesh Collider。对于简单的Plane,使用Mesh Collider没问题,但对于性能要求高的场景,更推荐用Box Collider。我们可以点击Mesh Collider组件右上角的齿轮图标,选择“Remove Component”移除它,然后点击“Add Component”,搜索并添加Box Collider。你会发现Box Collider完美贴合了平面的形状,且计算效率更高。
  2. 为下落物体(FallingObject)添加刚体和碰撞器
    • 选中FallingObject,点击“Add Component”,搜索并添加Rigidbody
    • 同时,确保它有一个碰撞器。Cube默认带有一个Box Collider,这正合适,我们保留它即可。

现在,如果你点击运行,就会看到立方体直接落下,砸在平面上,然后可能轻微弹起或滑动一下,最终静止。一个最基本的自由落体与碰撞检测已经完成了!但这只是开始,效果很粗糙。我们接下来要深入每一个组件,进行精细化调整。

3.3 组件参数初解与常见坑点

  • Rigidbody组件关键参数

    • Mass(质量):默认1。质量影响惯性(改变运动状态的难易程度)和碰撞时的动量交换。一个质量很小的物体撞上一个质量很大的静态物体,几乎不会让大物体移动。注意:质量不是重量,在Unity的物理系统中,它没有单位。保持合理的相对比例更重要(比如,一个箱子质量是10,一个球质量是1)。
    • Drag(阻力) &Angular Drag(角阻力):模拟空气(或介质)阻力。Drag影响直线运动速度的衰减,Angular Drag影响旋转速度的衰减。值越大,物体停下来越快。对于从高空落下的物体,通常需要一点Drag(比如0.1)来防止它无休止地滑动。
    • Use Gravity(使用重力):默认勾选。如果取消,物体将不受重力影响。这是新手第一个坑:物体悬空不动,请先检查此项是否勾选!
    • Is Kinematic(是否为运动学):如前所述,非特殊需求保持false。
    • Interpolate(插值):默认为None。当刚体运动很快时,可能会在渲染帧之间“穿越”薄物体。如果发现碰撞检测不可靠(穿透),可以尝试将其设置为Interpolate(根据物理计算插值平滑变换)或Extrapolate(根据上一帧速度预测变换)。这能显著改善高速运动物体的视觉平滑度和碰撞可靠性。
  • Collider组件关键参数

    • Is Trigger(是否为触发器):如果勾选,该碰撞器将不再产生物理碰撞效果(即物体可以穿过),但会触发OnTriggerEnter等事件。第二个坑:如果你的物体直接穿过地面,请检查地面和下落物体的Collider,确保Is Trigger都没有被误勾选!
    • Material(物理材质):这是实现弹性、摩擦力的关键,我们稍后详细讲。

4. 实现自由落体与基础碰撞响应

基础场景跑通后,我们来实现一个更可控、更典型的自由落体示例。比如,我们想让物体从指定高度落下,并在碰撞时输出日志,甚至根据碰撞力度改变颜色。

4.1 创建控制脚本

在Project面板右键,Create -> C# Script,命名为FallingObjectController。双击用编辑器打开,编写如下代码:

using UnityEngine; public class FallingObjectController : MonoBehaviour { private Rigidbody rb; private Renderer objectRenderer; // 用于改变颜色 public float dropHeight = 10f; // 初始下落高度 public Color collisionColor = Color.red; // 碰撞后颜色 private Color originalColor; // 原始颜色 void Start() { // 获取组件引用 rb = GetComponent<Rigidbody>(); objectRenderer = GetComponent<Renderer>(); if (objectRenderer != null) { originalColor = objectRenderer.material.color; } // 设置初始位置 ResetPosition(); } void ResetPosition() { // 将物体移动到指定高度,并重置速度和旋转 transform.position = new Vector3(0, dropHeight, 0); if (rb != null) { rb.velocity = Vector3.zero; // 清零速度 rb.angularVelocity = Vector3.zero; // 清零角速度 rb.Sleep(); // 让刚体“睡眠”,节省性能,直到下次受力唤醒 } // 恢复颜色 if (objectRenderer != null) { objectRenderer.material.color = originalColor; } } // 当碰撞发生时调用(非触发器碰撞) void OnCollisionEnter(Collision collision) { // 打印碰撞信息 Debug.Log(gameObject.name + " 碰撞到了: " + collision.gameObject.name); Debug.Log("相对速度: " + collision.relativeVelocity.magnitude); Debug.Log("接触点数量: " + collision.contactCount); // 根据碰撞力度改变颜色(力度越大,颜色越红) float impactStrength = Mathf.Clamp01(collision.relativeVelocity.magnitude / 10f); if (objectRenderer != null) { objectRenderer.material.color = Color.Lerp(originalColor, collisionColor, impactStrength); } // 示例:如果碰撞力度很大,3秒后重置位置 if (collision.relativeVelocity.magnitude > 5f) { Invoke(nameof(ResetPosition), 3f); } } // 提供一个在游戏运行时手动重置的方法(可用于测试) void Update() { if (Input.GetKeyDown(KeyCode.R)) { ResetPosition(); } } }

将脚本拖拽到FallingObject游戏对象上。运行游戏,物体会从10个单位高度落下,碰撞地面时Console会输出信息,物体颜色会根据碰撞力度变红,强烈碰撞后3秒物体会重置到空中。

4.2 关键API与事件解析

  • OnCollisionEnter(Collision collision):这是最常用的碰撞检测事件。当非触发器碰撞器发生接触的那一帧被调用。Collision参数包含了丰富的碰撞信息:
    • collision.gameObject:与之碰撞的另一个物体。
    • collision.relativeVelocity:碰撞时的相对速度向量,其大小(magnitude)常用来衡量碰撞力度。
    • collision.contacts:一个ContactPoint数组,包含了所有接触点的位置、法线等信息。对于简单形状通常只有一个接触点,复杂碰撞可能有多个。
  • OnCollisionStayOnCollisionExit:分别在碰撞持续期间每帧调用,和碰撞分离的那一帧调用。
  • Rigidbody.Sleep():这是一个重要的性能优化方法。当一个刚体静止一段时间后,物理引擎会使其“睡眠”,不再进行物理计算,直到有新的力作用其上。手动调用Sleep()可以立即让其进入睡眠状态。

实操心得:在Start或初始化时,对静止的刚体调用rb.Sleep()是个好习惯。对于频繁重置位置的物体(比如掉落的道具),在重置位置和速度后调用Sleep(),可以避免它在空中就因为微小的数值误差或上一帧的残余速度而“抖动”,直到它真正开始下落时才被重力唤醒。

5. 高级碰撞调控:物理材质(Physic Material)详解

默认的碰撞效果(有点像硬塑料块撞到硬地面)往往不符合需求。我们可能需要橡皮球般的弹性,或者冰面般的低摩擦力。这时就需要Physic Material(物理材质)。

5.1 创建与配置物理材质

  1. 在Project面板右键,Create -> Physic Material,命名为BouncyBall
  2. 选中这个材质,Inspector面板会出现以下核心属性:
    • Dynamic Friction(动摩擦系数) &Static Friction(静摩擦系数):模拟摩擦力。0表示无摩擦(如冰面),1表示极大摩擦(如橡胶)。通常两者设为相同值即可。
    • Bounciness(弹性系数):0表示无弹性(完全非弹性碰撞,如粘土),1表示完全弹性(理想情况,无能量损失)。注意:弹性效果取决于碰撞双方,取两者Bounciness的平均值或最大值(取决于Bounce Combine模式)。
    • Friction Combine&Bounce Combine(摩擦/弹性组合模式):定义当两个物体碰撞时,如何组合各自的摩擦/弹性系数。
      • Average(平均):常用模式。(A的系数 + B的系数)/ 2。
      • Minimum(取小):使用两者中较小的值。
      • Maximum(取大):使用两者中较大的值。
      • Multiply(相乘):两者系数相乘,通常会导致极小的值。

5.2 应用物理材质并测试

  1. 将创建的BouncyBall物理材质,拖拽到FallingObjectBox Collider组件的Material属性槽中。
  2. BouncyBallBounciness设置为0.8,Friction都设置为0.1。
  3. 我们也可以为地面创建一个物理材质HardGroundBounciness设为0.1,Friction设为0.8,并赋给地面的Box Collider
  4. 运行游戏。现在立方体落下后,会弹跳得更高,并且滑动距离更远,更像一个弹力球落在粗糙地面上。

5.3 组合模式的实际影响

这是一个极易被忽略但至关重要的设置。假设球(Bounciness=0.9)落在水泥地(Bounciness=0.1)上。

  • 如果组合模式是Average,则有效弹性为(0.9+0.1)/2 = 0.5,球会弹起,但不高。
  • 如果组合模式是Maximum,则有效弹性为max(0.9,0.1) = 0.9,球会弹得很高,仿佛地面也很有弹性。
  • 如果组合模式是Minimum,则有效弹性为min(0.9,0.1) = 0.1,球几乎弹不起来。

避坑指南:如果你的物体弹性表现和预期不符,在检查了各自材质弹性值后,务必检查Bounce Combine模式!对于大多数需要直观控制的情景,Average模式是最符合直觉的。Maximum模式常用于希望某个物体(如超级弹力球)无论撞到什么都能弹得很高的场景。

6. 性能优化与稳定性调优

物理计算是性能消耗大户,不当的设置会导致卡顿甚至物理引擎崩溃。以下是几个关键的调优点。

6.1 刚体睡眠与唤醒

如前所述,物理引擎会自动让静止的刚体睡眠。但有时会出现“抖动不休”无法睡眠的情况,导致不必要的性能消耗。常见原因及解决:

  • 原因1:连续微小的碰撞或力。检查场景中是否有持续作用的微小力(如风力区域)、或碰撞体交叠。
  • 原因2:Collider形状过于复杂。对于运动物体,避免使用高精度的Mesh Collider。优先使用BoxSphereCapsule等基本碰撞体,或它们的组合(Compound Colliders)。如果必须用网格,考虑使用简化的凸包(Mesh Collider勾选Convex),但注意凸包碰撞计算量仍大于基本体。
  • 原因3:质量(Mass)过小。质量极小的物体会对微小的力非常敏感,难以稳定。确保质量在一个合理的范围内(比如0.1到100之间),避免使用0或极小的值。

6.2 碰撞体优化策略

  1. 层级简化(Layer Collision Matrix):不是所有物体都需要互相碰撞。通过Edit -> Project Settings -> Physics,打开碰撞矩阵(Collision Matrix),可以取消勾选某些层级之间的碰撞检测。例如,将大量只作为背景装饰的物体放在“Decor”层,并取消该层与“Player”、“Enemy”等层的碰撞,能大幅减少物理计算量。
  2. 使用触发器(Trigger)替代物理碰撞:如果只需要知道两个物体是否重叠,而不需要真实的物理推力(比如拾取物品、进入区域),那么使用触发器(勾选Is Trigger)是更高效的选择,因为它省去了复杂的碰撞响应计算。
  3. 调整物理更新频率:在Edit -> Project Settings -> Time中,Fixed Timestep决定了物理更新的时间间隔(默认0.02秒,即50Hz)。降低此值(如0.04秒)可以提高性能但降低物理精度;提高此值(如0.016秒)可以增加精度但加重负担。通常保持默认即可,除非有特殊的高精度或性能需求。

6.3 解决“抖动”与“穿透”问题

  • 抖动(Jitter):通常发生在两个质量、速度相近的物体堆叠或碰撞时,物理引擎在每一帧都无法稳定求解它们的位置,导致高频微震。
    • 解决方案:增加Fixed Timestep(提高计算精度),或降低Time设置中的Maximum Allowed Timestep(防止单帧物理计算时间过长)。更根本的方法是优化碰撞形状、调整质量比、或者对堆叠物体使用关节(Joint)约束而非纯粹靠碰撞。
  • 穿透(Penetration):物体部分或全部穿过了另一个碰撞体。
    • 解决方案:这是高速移动物体最常见的问题。首先,尝试为高速刚体启用InterpolateExtrapolate。其次,确保Collider的尺寸略小于视觉网格(Mesh),为物理计算留出容错空间。最后,可以考虑使用连续碰撞检测(CCD)。
      • 连续碰撞检测(Continuous Collision Detection):在Rigidbody组件中,将Collision Detection从默认的Discrete(离散)改为Continuous(连续)或Continuous Dynamic(连续动态)。这会显著增加计算开销,但能有效防止高速子弹、小球等物体穿透薄墙。仅对少数高速运动的物体启用此功能。

7. 常见问题排查与实战技巧实录

即使按照步骤操作,奇怪的问题依然可能出现。这里记录了一些典型问题及其排查思路。

7.1 问题清单与速查表

问题现象可能原因排查步骤与解决方案
物体静止不动,不下落1.Rigidbody组件未添加。
2.Use Gravity未勾选。
3. 物体被脚本锁定了位置(如rb.isKinematic = true或脚本中每帧设置position)。
1. 检查Inspector面板是否有Rigidbody
2. 检查RigidbodyUse Gravity是否勾选。
3. 检查所有附加脚本,是否在Update中直接修改transform.position。应使用rb.AddForce或修改rb.velocity来移动物理物体。
物体直接穿过地面或其他物体1. 地面或物体缺少Collider
2. 地面或物体的Collider被勾选为Is Trigger
3. 物体速度过快(穿透)。
4. 碰撞双方层级在碰撞矩阵中被禁用。
1. 确认双方都有Collider组件。
2. 检查双方ColliderIs Trigger属性。
3. 为高速物体启用InterpolateContinuous Collision Detection
4. 检查Physics设置中的Layer Collision Matrix。
碰撞后物体行为诡异(如疯狂旋转、飞走)1. 质量(Mass)设置不合理(如过大或过小)。
2. 碰撞体形状复杂且未设置为凸包(Convex)。
3. 物理材质(Physic Material)的摩擦/弹性系数极端或组合模式异常。
4. 多个碰撞体意外重叠。
1. 调整Mass到合理范围(0.1-100)。
2. 对于Mesh Collider,尝试勾选Convex,或改用基本碰撞体组合。
3. 检查并调整物理材质属性,将Bounce CombineFriction Combine设为Average
4. 在Scene视图中开启Gizmos显示碰撞体,检查是否有意外交叠。
物理效果卡顿或不稳定1. 场景中动态刚体数量过多。
2. 使用了高精度Mesh Collider且未勾选Convex
3.Fixed Timestep设置过低,或单帧物理计算超时。
1. 对静止物体使用静态碰撞体(无Rigidbody)。对大量小物体考虑使用简化碰撞体或触发器。
2. 对所有运动的Mesh Collider启用Convex,或替换为基本体。
3. 适当调高Fixed Timestep(如0.033s),或降低Maximum Allowed Timestep
OnCollisionEnter事件不触发1. 碰撞双方至少一方是触发器(Is Trigger)。触发器事件是OnTriggerEnter
2. 脚本未挂载在发生碰撞的物体上,或脚本被禁用。
3. 碰撞发生在刚体睡眠时(某些旧版本可能不触发)。
1. 确认是否需要物理碰撞,不需要则改用OnTriggerEnter,需要则取消Is Trigger
2. 检查脚本挂载和启用状态。
3. 尝试在脚本中禁用刚体睡眠(rb.sleepMode = RigidbodySleepMode.NeverSleep)进行测试,但不推荐长期使用。

7.2 独家避坑技巧

  1. “视觉网格”与“碰撞体”分离:对于复杂的模型,不要直接使用其网格渲染器(MeshRenderer)的网格作为Mesh Collider。最好创建一个简单的子物体(比如一个Cube或几个基本形状的组合),挂上基本的Collider,用这个子物体代表物理轮廓。这能极大提升性能和稳定性。
  2. 善用“刚体约束(Constraints)”:在Rigidbody组件底部,可以冻结(Freeze)某个位置或旋转轴。例如,一个2D横版游戏的角色,你可能需要冻结Z轴位置和X、Y轴旋转,只允许在XY平面移动和绕Z轴旋转。这能防止物体意外翻倒或掉出平面。
  3. 调试利器:物理调试视图:在Scene视图左上角的Gizmos下拉菜单中,可以开启Colliders的显示。你还可以在Game视图叠加物理调试信息(需要编写简单代码或使用第三方工具),实时观察碰撞体、接触点、力向量等,这对于排查复杂物理问题至关重要。
  4. 关于Time.fixedDeltaTime:所有在FixedUpdate中进行的物理相关操作(如AddForce)都是与这个固定时间步长相关的。如果你在Update中读取速度(rb.velocity),由于Update帧率可变,而物理更新固定,可能会读到过时或插值后的速度。对于需要精确物理状态判断的逻辑,尽量放在FixedUpdate中。

8. 从基础到应用:扩展场景示例

掌握了核心原理和避坑技巧后,我们可以尝试更复杂的场景,将刚体和碰撞检测用得更活。

8.1 场景:多物体堆叠与稳定性

创建一个场景,地面之上有多个不同形状(Cube, Sphere)和质量的物体。目标是让它们落下后能稳定堆叠。

  • 挑战:物体容易滑落、抖动或倒塌。
  • 解决方案
    1. 调整物理材质:适当增加摩擦系数(Friction),减少弹性(Bounciness),比如都设为0.5左右。
    2. 优化质量比:底部的物体质量设置得大一些(如10),上面的物体质量小一些(如1),增加稳定性。
    3. 使用Interpolate:为所有动态刚体启用Interpolate,平滑运动,减少抖动。
    4. 微调Fixed Timestep:如果堆叠非常不稳定,可以尝试稍微减小Fixed Timestep(如0.016),提高物理计算精度,但需注意性能。

8.2 场景:发射小球并计算碰撞次数

编写脚本,按空格键从摄像机前方发射一个小球(Sphere),并统计它直到静止共发生了多少次碰撞。

using UnityEngine; public class BallLauncher : MonoBehaviour { public GameObject ballPrefab; public float launchForce = 20f; private int totalCollisionCount = 0; void Update() { if (Input.GetKeyDown(KeyCode.Space)) { LaunchBall(); } } void LaunchBall() { totalCollisionCount = 0; GameObject ball = Instantiate(ballPrefab, transform.position, Quaternion.identity); Rigidbody rb = ball.GetComponent<Rigidbody>(); rb.AddForce(transform.forward * launchForce, ForceMode.Impulse); // 为小球添加一个临时脚本来计数 BallCollisionCounter counter = ball.AddComponent<BallCollisionCounter>(); counter.onCollisionLogged = (count) => { totalCollisionCount = count; Debug.Log("小球总计碰撞次数: " + totalCollisionCount); }; } } // 临时附加的计数脚本 public class BallCollisionCounter : MonoBehaviour { private int collisionCount = 0; public System.Action<int> onCollisionLogged; void OnCollisionEnter(Collision collision) { collisionCount++; Debug.Log($"碰撞 #{collisionCount} with {collision.gameObject.name}"); // 可以在这里判断速度,如果速度很小,认为即将静止,报告总数 if (GetComponent<Rigidbody>().velocity.magnitude < 0.1f) { onCollisionLogged?.Invoke(collisionCount); } } }

这个例子综合运用了刚体受力(AddForce)、碰撞事件监听和简单的物体生成与销毁管理。

8.3 场景:实现一个简单的“弹簧床”效果

创建一个有弹性的平台,物体落在上面会被高高弹起。

  1. 创建一个新的Cube作为弹簧床,为其添加Rigidbody,但勾选Is Kinematic(因为我们可能想用脚本控制其上下运动,而不是完全物理驱动)。
  2. 为这个Cube添加一个脚本,在OnCollisionEnter中,根据碰撞力度,给碰撞到的物体(collision.rigidbody)施加一个向上的力(AddForce)。
  3. 或者,更物理的方法是:为弹簧床的碰撞体赋予一个高弹性(如0.9)的物理材质,并将其RigidbodyMass设置得非常大(如1000),这样当质量小的物体撞上时,几乎所有的动能都会转化为弹性势能再返还,实现高弹跳。同时,可以结合脚本在碰撞时播放一个压缩再恢复的动画,增强视觉效果。

通过以上从基础到进阶的拆解,你会发现,Unity的刚体系统看似简单,但门道很深。每一个参数背后都对应着物理模拟的一个方面。理解它们,不仅能帮你实现“物体下落”这种基础功能,更能让你在开发复杂的物理游戏(如解谜、赛车、沙盒)时游刃有余。记住,遇到奇怪的物理现象,不要慌,按照“组件检查(Rigidbody/Collider)-> 属性核对(重力、触发器、材质)-> 性能与稳定性调优(睡眠、穿透、抖动)”的流程一步步排查,大部分问题都能迎刃而解。