Unity物理系统全解析:从核心原理到性能优化实战指南

📅 2026/7/7 5:36:35 👁️ 阅读次数 📝 编程学习
Unity物理系统全解析:从核心原理到性能优化实战指南

1. 项目概述:为什么物理系统是Unity的“游戏规则制定者”

干了这么多年Unity开发,我越来越觉得,物理系统这东西,它不像渲染那么直观,也不像UI那么显眼,但它绝对是决定你游戏“手感”和“世界感”的灵魂。你可以把游戏世界想象成一个舞台,渲染负责灯光和布景,让一切看起来真实;而物理系统,就是那个在幕后默默工作的导演和物理法则制定者,它决定了演员(游戏对象)如何移动、如何互动、如何倒下、如何弹起。没有它,你的角色会穿墙而过,子弹会无视障碍,箱子推不动,汽车开起来像在冰上漂移——整个世界就失去了可信的基石。

Unity的物理系统,本质上是一套高度封装的模拟引擎,它把现实世界中复杂的力学计算(牛顿力学为主)打包成我们开发者可以轻松调用的组件和API。无论是想让一个球体受重力下落,还是模拟一场复杂的汽车碰撞,亦或是实现布娃娃系统的滑稽死亡,都离不开它。对于新手来说,可能觉得加个Rigidbody(刚体)和Collider(碰撞体)就叫用了物理,但这里面门道深了去了。参数怎么调?性能瓶颈在哪?3D和2D物理有什么区别?DOTS物理又是什么新玩意儿?这些才是真正决定你项目成败的细节。

这篇文章,我就结合自己踩过的无数个坑,从最基础的组件拆解开始,到高级的性能优化和实战避坑,带你彻底吃透Unity的物理系统。无论你是刚入门的新手,还是想深化理解的熟手,相信都能找到对你有用的干货。我们不止讲“怎么用”,更要深挖“为什么这么用”,以及“怎么用更好”。

2. 物理系统的核心架构与引擎选型

刚接触Unity物理,很多人会被各种名词搞晕:PhysX、Box2D、Unity Physics、Havok… 它们之间到底是什么关系?我的项目到底该用哪个?这部分我们就来彻底理清这个脉络。

2.1 内置物理引擎:PhysX与Box2D

Unity最常用、最成熟的两套物理方案,就是其内置的3D和2D物理系统。它们开箱即用,与编辑器集成度最高,文档和社区资源也最丰富。

2.1.1 内置3D物理:基于NVIDIA PhysX

这是Unity 3D项目的默认和主力物理引擎。PhysX是NVIDIA旗下久经沙场的物理中间件,以稳定和性能著称。当你为一个GameObject添加Rigidbody组件时,你就是在启用PhysX引擎来模拟这个物体的运动。

它的工作流程非常直观:Collider(碰撞体)定义形状边界,Rigidbody赋予物体质量、阻力等物理属性并负责运动计算。每一帧,物理引擎会:

  1. 收集所有带RigidbodyCollider的物体。
  2. 进行连续碰撞检测(CCD)离散碰撞检测,判断哪些物体发生了接触或穿透。
  3. 根据物体的质量、速度、碰撞法线等信息,计算碰撞后的力和运动。
  4. 通过物理更新(FixedUpdate)循环,积分计算每个物体的新位置和旋转。
  5. 将结果同步回Transform组件。

注意:这里有个关键点,物理计算是在FixedUpdate中进行的,而渲染是在Update中。这意味着物体的物理状态更新频率(Time.fixedDeltaTime)可以和帧率(Time.deltaTime)不同。将物理相关的代码(如AddForce)放在FixedUpdate中,能保证与物理引擎步调一致,避免计算不稳定。

2.1.2 内置2D物理:基于Box2D

对于2D项目,Unity集成了轻量级且高效的Box2D引擎。其核心组件是Rigidbody2D和各类Collider2D(如BoxCollider2D,CircleCollider2D)。

虽然原理与3D类似,但有几点根本区别:

  • 坐标系:2D物理只在XY平面内运算,Z轴被忽略。Rigidbody2D只有Z轴的旋转。
  • 组件独立RigidbodyRigidbody2D是两个完全独立的组件,API也不同,不能混用。
  • 性能考量:Box2D通常比3D PhysX在2D模拟上更高效,因为计算维度减少。
  • 功能差异:一些3D物理的特性(如网格碰撞体MeshCollider)在2D中没有完全对应的组件,2D有特有的组件如CompositeCollider2D(复合碰撞体)来优化复杂形状。

选择建议:如果你的项目是纯2D视角(如横版过关、卡牌对战),毫不犹豫选择2D物理系统。即使是3D场景但游戏玩法是2D的(如2.5D游戏),也通常使用2D物理,因为更简单高效。只有需要真正的3D空间交互(如第一人称射击、赛车、物理解谜),才使用3D物理。

2.2 面向数据的技术栈:DOTS物理方案

随着项目规模扩大,传统面向对象(OOB)的物理模拟可能遇到性能瓶颈,尤其是需要处理成千上万个动态物理物体时。Unity推出的DOTS(面向数据的技术栈)架构,就是为了解决这一问题,其物理系统也迎来了革新。

2.2.1 Unity Physics Package

这是DOTS架构下的默认物理解决方案。它不再使用GameObjectMonoBehaviour那套体系,而是基于Entity(实体)、ComponentData(组件数据)和System(系统)。

  • 核心变化:物理状态(位置、旋转、速度)现在是存储在ComponentData中的纯数据,PhysicsSystem(一个ISystem)负责批量处理所有这些数据。这种数据导向的设计使得CPU缓存命中率更高,SIMD指令集得以充分利用,性能有数量级的提升。
  • 使用场景:非常适合大规模军团战斗、大量可破坏物体、粒子物理(如大量树叶、碎片)等场景。如果你的项目有“海量实体”的物理需求,这是必须考虑的方向。
  • 上手门槛:较高。需要理解ECS(实体组件系统)和Jobs(作业系统)的概念,编程模式与传统方式差异较大。

2.2.2 Havok Physics for Unity

Havok是游戏业界另一款顶级的物理引擎。Unity通过Havok Physics for Unity包,将Havok引擎集成到DOTS框架中,作为Unity Physics包的一个替代或扩展。

  • 优势:Havok在复杂约束、车辆物理、布料模拟等方面有非常深厚的积累和口碑。如果你需要实现极其真实和复杂的机械结构、高级的车辆动力学,Havok可能是更好的选择。
  • 注意:这是一个需要特定许可方案的商业包,并非免费使用。在项目初期就需要评估成本和收益。

引擎选型决策树: 为了更直观地帮你选择,可以参考下面的决策流程:

项目类型物理需求特点推荐引擎关键考量
传统3D游戏角色、道具、中等数量互动物体内置3D (PhysX)成熟、易用、生态好,满足绝大多数需求
传统2D游戏横版、俯视角、纯2D交互内置2D (Box2D)轻量、高效、API专为2D设计
超大规模模拟数千至上万动态物理实体(如RTS小兵、大量碎片)DOTS Unity Physics性能为王,需接受ECS学习曲线
专业模拟/3A大作需要顶尖的车辆、布料、复杂约束模拟DOTS Havok Physics功能强大,但涉及商业授权与更高复杂度
移动端/性能敏感物体数量不多,但对帧率和功耗要求严苛内置2D/3D简化DOTS优先使用简单碰撞体,减少动态物体,谨慎评估DOTS收益

3. 核心组件深度解析与参数调校实战

了解了宏观架构,我们深入到微观层面,看看构成物理交互的那些核心组件。很多人只是拖拽组件上去,但对里面每个参数的意义一知半解,调起来全靠玄学。这部分我们就把每个参数掰开揉碎了讲。

3.1 刚体:物理世界的“身份证”

Rigidbody(或Rigidbody2D)是物理模拟的基石。没有它,物体就是个静态的装饰品。我们来看关键参数:

  • Mass(质量):这是最容易用错的一个。新手常犯的错误是给所有物体都设置成1。在物理引擎中,质量是相对的。一个角色的质量是1,一个箱子的质量也设1,那么推动它们需要的力是差不多的,这显然不合理。最佳实践是参考现实世界的相对比例。比如,角色70kg(质量70),小石头0.5kg(质量0.5),汽车1000kg(质量1000)。这样碰撞和力的反馈才真实。
  • Drag / Angular Drag(阻力/角阻力):想象物体在空气或水中的运动。Drag影响位置移动的减速,Angular Drag影响旋转的减速。值越大,物体停下来越快。如果你想做一个“手感”很重的角色或飞船,可以适当调高阻力。对于飘在空中的气球或太空物体,阻力应该接近0。
  • Use Gravity(使用重力):勾选后受全局重力影响。注意:全局重力在Edit -> Project Settings -> Physics中设置。有时为了实现特殊效果(如反重力区域),可以通过代码临时关闭某个刚体的此选项。
  • Is Kinematic(是否为运动学刚体):这是超级重要的一个选项。勾选后,该刚体将不受物理引擎的力(重力、碰撞力等)影响,但你可以通过直接修改其Transform的位置来移动它。同时,它仍然可以与其他非运动学刚体发生碰撞并影响对方。典型用途:玩家控制的角色(用脚本移动)、移动的平台、电梯。这样你可以获得精确的移动控制,同时还能推开场景中的箱子。
  • Interpolate(插值):物理更新在FixedUpdate,渲染在Update,如果FixedUpdate频率低于帧率,物体运动可能会不流畅(抖动)。Interpolate选项会让物体根据上一帧和当前帧的物理状态进行平滑插值,渲染出中间位置,从而获得丝滑的视觉表现。对于高速移动的物体(如子弹、赛车),强烈建议开启Extrapolate(外推)则是预测下一帧位置,风险较高,容易导致“抖动”,一般不推荐。

实操心得:对于主角控制器,我通常的配置是:Mass设为合适值(如1),Drag设为5-10让其移动有“重量感”且能快速停下,Angular Drag调高(如999)防止角色奇怪地翻滚,Is Kinematic不勾选(如果使用CharacterController则不加Rigidbody),Interpolate选择Interpolate。对于被主角推动的箱子,Mass设为10-20,Drag适中,Is Kinematic不勾选。

3.2 碰撞体:物理交互的“形状定义者”

碰撞体定义了物体的物理轮廓。它只是一个形状,没有质量。关键点在于形状的复杂度和层级管理。

3.2.1 碰撞体类型与选择

  • 基础碰撞体Box,Sphere,Capsule。性能最好,应作为首选。Capsule尤其适合角色,因为它的圆滑顶部能很好地处理斜坡和台阶。
  • Mesh Collider(网格碰撞体):使用模型的实际网格作为碰撞形状。这是性能杀手,尤其是复杂网格。仅适用于静态的、形状极其不规则且基础碰撞体无法近似的环境物体(如一块奇形怪状的岩石),并且务必勾选Convex(凸包)选项(如果物体是凹的,PhysX会为其生成一个凸包近似体)。对于动态物体,绝对不要用Mesh Collider
  • Terrain Collider(地形碰撞体):专为地形系统优化,性能很好。
  • Wheel Collider(车轮碰撞体):用于车辆模拟,内置了悬挂、摩擦等复杂计算。
  • Compound Colliders(复合碰撞体):通过在一个物体上添加多个基础碰撞体来组合成复杂形状。这是平衡精度和性能的常用手段。例如,一个柜子可以用一个BoxCollider做主体,两个小BoxCollider做把手。

3.2.2 碰撞矩阵与层级

这是管理“谁和谁碰撞”的核心配置。在Edit -> Project Settings -> Physics(或Physics 2D)中,你可以看到Layer Collision Matrix

  • 为什么需要:你肯定不希望子弹和子弹之间相互碰撞(浪费性能),也不希望UI元素触发物理事件。通过图层,你可以精细控制碰撞关系。
  • 标准做法
    1. 定义图层:Player,Enemy,Bullet,Environment,IgnoreRaycast,TriggerOnly等。
    2. 在碰撞矩阵中,取消不必要的交叉勾选。例如,Bullet层只与PlayerEnemyEnvironment层碰撞,BulletBullet之间不碰撞。
    3. 将游戏对象分配到对应的图层。
  • 技巧:可以创建一个TriggerOnly层,专门用于放置只作为触发器(不进行物理碰撞)的物体,并在矩阵中取消该层与所有其他层的物理碰撞勾选,只保留触发器交互。这能避免不必要的物理计算。

3.3 物理材质:定义表面的“性格”

物理材质Physics Material(2D中叫Physics Material 2D)附着在碰撞体上,定义了物体表面的摩擦力和弹性(反弹力)。

  • Dynamic Friction(动摩擦):物体运动时的摩擦力。值越大,越难推动或滑动。
  • Static Friction(静摩擦):物体从静止到运动所需克服的摩擦力。通常略大于或等于动摩擦。
  • Bounciness(弹性):0表示完全无弹性(碰撞后不反弹),1表示完全弹性(能量无损失,永远弹跳)。现实世界中通常在0到1之间。
  • Friction Combine / Bounce Combine(摩擦/弹性组合模式):当两个物体接触时,它们的摩擦力和弹性如何计算?有几种模式:
    • Average:取平均值。(最常用)
    • Minimum:取最小值。
    • Maximum:取最大值。
    • Multiply:相乘。

实战案例:制作一个冰面。创建一个物理材质,将Dynamic FrictionStatic Friction都设为非常低的值(如0.05),Bounciness设为0。将其赋给地面碰撞体。再制作一个弹力球材质,Bounciness设为0.8,赋给球体。你就能看到球在冰面上滑动很长距离,撞到墙后高高弹起。

4. 物理交互的脚本控制与事件处理

物理组件搭好了,世界动起来了,接下来就要用脚本与之交互,这是让游戏“活”起来的关键。

4.1 力的施加:让物体动起来

最直接的方式就是给刚体施加力。Unity提供了几种方式,区别很大:

  • Rigidbody.AddForce(Vector3 force, ForceMode mode):最常用的方法。
    • ForceMode.Force:添加一个持续的力(与质量有关)。适合模拟火箭推进器、风力。
    • ForceMode.Impulse:添加一个瞬间的冲量(与质量有关)。适合模拟子弹射击、跳跃的瞬间蹬地力。
    • ForceMode.Acceleration:添加一个持续的加速度(忽略质量)。适合做不因物体重量而改变的效果。
    • ForceMode.VelocityChange:添加一个瞬间的速度变化(忽略质量)。适合直接设定一个速度突变。
  • Rigidbody.AddTorque(Vector3 torque):添加扭矩,让物体旋转。
  • Rigidbody.AddForceAtPosition(Vector3 force, Vector3 position):在特定位置施加力,可以产生旋转效果(比如推一个箱子的边缘)。

重要原则:所有施加力的代码,都应该放在FixedUpdate中,而不是Update。因为物理计算在固定时间步长中进行,在Update中调用可能导致一帧内多次或零次施加力,造成不稳定。

// 示例:让物体向前持续移动(像汽车引擎) void FixedUpdate() { if (Input.GetKey(KeyCode.W)) { rb.AddForce(transform.forward * enginePower, ForceMode.Force); } } // 示例:让物体跳跃 void Update() { if (Input.GetKeyDown(KeyCode.Space) && isGrounded) { rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse); } }

4.2 物理查询:射线与形状投射

除了被动等待碰撞,我们经常需要主动探测物理世界。

  • 射线检测Physics.Raycast。这是最常用、最高效的查询。用于检测鼠标点击、武器瞄准、判断是否着地等。
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, 100f)) { Debug.Log("点击到了: " + hit.collider.name); // 可以通过hit.point获取点击的世界坐标,hit.normal获取法线 }
  • 球形/盒形/胶囊体检测Physics.SphereCast,Physics.BoxCast,Physics.CapsuleCast。这些是“有体积”的射线,用于检测一个区域。比如判断角色前方一个胶囊体空间内是否有障碍物。
  • 重叠检测Physics.OverlapSphere,Physics.OverlapBox。检测一个区域内所有碰撞体。用于爆炸范围检测、拾取物品等。
  • 2D对应API:以上所有方法在2D中都有对应,如Physics2D.Raycast,Physics2D.OverlapCircleAll等,注意使用RaycastHit2DCollider2D

性能提示:物理查询是有成本的,尤其是Overlap系列和复杂形状的Cast。尽量避免每帧对大量物体进行查询,或者使用图层掩码LayerMask参数来限制检测范围。

4.3 碰撞与触发器事件

这是物理交互的信息反馈机制。你需要编写脚本并挂载在带有碰撞体的物体上。

  • 碰撞事件:需要至少一个物体有非运动学刚体,且双方碰撞体都没有勾选Is Trigger
    • OnCollisionEnter(Collision collisionInfo):碰撞开始时调用一次。
    • OnCollisionStay(Collision collisionInfo):碰撞持续期间每帧调用。
    • OnCollisionExit(Collision collisionInfo):碰撞结束时调用一次。
    • Collision参数包含了丰富的碰撞信息:contacts(接触点数组)、impulse(冲量)、relativeVelocity(相对速度)等。例如,可以通过relativeVelocity.magnitude来判断碰撞的剧烈程度,决定是否播放破碎音效。
  • 触发器事件:至少一个碰撞体勾选了Is Trigger。触发器不会产生物理碰撞力,只用于检测重叠。
    • OnTriggerEnter(Collider other):进入触发器范围。
    • OnTriggerStay(Collider other):停留在触发器内。
    • OnTriggerExit(Collider other):离开触发器范围。
    • 典型应用:检测玩家进入宝箱范围、进入敌人视野、进入存档点、拾取物品。

一个常见误区:很多人分不清OnCollisionEnterOnTriggerEnter。记住一个简单的原则:如果你需要物体被“挡住”(有物理反馈),用碰撞;如果只需要知道“穿过”或“进入某个区域”,用触发器。

5. 高级主题与性能优化实战

当你的游戏物体多起来,物理系统很容易成为性能瓶颈。这部分我们来聊聊如何让物理系统既高效又稳定。

5.1 物理更新与时间步长

FixedUpdate的调用频率由Time.fixedDeltaTime决定,默认是0.02秒(50Hz)。这个值直接影响物理模拟的精度和性能。

  • 提高频率(减小fixedDeltaTime:如设为0.01秒(100Hz),物理模拟会更平滑,尤其对高速运动的物体(避免“隧道效应”——物体因速度太快而穿过薄墙)。但代价是CPU计算量翻倍。
  • 降低频率(增大fixedDeltaTime:如设为0.04秒(25Hz),能提升性能,但物理模拟会变“卡顿”,可能引发不稳定。
  • 自适应策略:Unity允许设置Time.maximumAllowedTimestep(默认0.333秒)。如果一帧的Update耗时过长,导致距离上一次FixedUpdate的时间超过了这个最大值,物理引擎会“补算”多次FixedUpdate直到追上,但这可能导致“螺旋式死亡”(一帧内计算过多物理,导致更卡,下一帧计算更多…)。对于性能波动大的游戏,可以适当调低此值,以牺牲物理精度换取帧率稳定。

个人经验:对于大多数60FPS的游戏,保持0.02秒的默认值是合理的。如果有很多高速物体(如弹幕游戏),可以尝试提高到0.01秒。移动端为了省电,可以尝试0.033秒(30Hz)。务必在真机上测试手感。

5.2 刚体睡眠与唤醒

物理引擎一个重要的优化机制是刚体睡眠。当一个刚体的速度低于某个阈值并持续一段时间后,它会进入“睡眠”状态。睡眠的刚体不再参与每帧的物理计算,直到有外力(碰撞、施加力等)将其“唤醒”。

  • Rigidbody.sleepThreshold:可以手动设置睡眠的速度阈值。对于几乎不动的静态物体(如堆在地上的杂物),可以适当调高这个值,让它们更快入睡。
  • 手动控制Rigidbody.Sleep()强制入睡,Rigidbody.WakeUp()强制唤醒。例如,当一个怪物死亡变成一堆静态尸体时,你可以调用Sleep();当这堆尸体被爆炸波及,再调用WakeUp()
  • 检查状态Rigidbody.IsSleeping()

善用睡眠机制,可以大幅减少活跃刚体的数量,是物理优化的首要手段。

5.3 碰撞体优化策略

碰撞体的性能消耗:MeshCollider(Concave) >>MeshCollider(Convex) > 复杂Compound Collider>TerrainCollider> 基础碰撞体。

优化清单

  1. 静态物体标记为Static:在Inspector右上角,将永远不会移动的环境物体(地形、建筑)标记为Static。Unity会在烘焙阶段为这些物体优化碰撞数据,运行时效率极高。
  2. 简化碰撞形状:永远用最简单的碰撞体去近似复杂模型。一个人物用CapsuleCollider,一辆车用几个BoxColliderWheelCollider组合。
  3. 分层管理:如前所述,使用图层碰撞矩阵,禁用不必要的碰撞检测对。
  4. 减少动态刚体数量:动态刚体(非运动学)是性能消耗大户。思考哪些物体真的需要物理模拟?一个飘动的旗帜也许可以用Shader动画代替。一堆碎块可以在碰撞后一段时间转为静态或销毁。
  5. 使用触发器代替复杂碰撞:如果只需要检测进入区域,用简单的BoxCollider作为触发器,而不是复杂的网格碰撞。
  6. 2D项目使用CompositeCollider2D:如果你的2D环境由许多小碰撞体组成(如瓦片地图),使用CompositeCollider2D将它们合并成一个或少数几个大的碰撞体,能极大提升性能。

5.4 布娃娃系统与关节

布娃娃系统常用于角色的死亡动画,实现逼真的倒地效果。Unity内置了Ragdoll创建向导(GameObject -> 3D Object -> Ragdoll),可以快速生成。

其本质是为角色的每个骨骼(盆骨、脊柱、四肢等)添加RigidbodyCapsule/Box Collider,并用Character Joint(角色关节)将它们连接起来。关节限制了骨骼间的相对运动范围(如肘部只能在一定角度内弯曲)。

关节是另一个强大的工具,用于模拟现实世界的连接关系:

  • Hinge Joint(铰链关节):像门合页,绕一个轴旋转。
  • Fixed Joint(固定关节):将两个物体牢固地固定在一起,但可以打破。
  • Spring Joint(弹簧关节):像弹簧一样连接两个物体。
  • Configurable Joint(可配置关节):功能最强大,可以模拟几乎所有类型的关节,如万向节、滑块等,但设置也最复杂。

使用关节时,要特别注意性能,因为每个关节都需要额外的约束求解计算。

6. 常见问题排查与实战避坑指南

这里汇总了我这些年遇到的最典型、最让人头疼的物理问题及其解决方案。

6.1 物体抖动或“跳舞”

现象:堆叠的物体或相互接触的物体持续高频抖动。原因

  1. 质量比过于极端:一个质量1的物体压在一个质量0.01的物体上,容易不稳定。尽量让相互作用的物体质量在同一数量级(比如相差10倍以内)。
  2. 碰撞体穿透:在初始状态或运动后,两个碰撞体发生了轻微穿透,物理引擎会疯狂地施加力试图将其推开,导致抖动。
  3. 物理材质弹性过高:两个高弹性物体碰撞后,可能会在很小的空间内来回弹跳,看起来像抖动。

解决

  • 检查并调整相关物体的Mass
  • 确保场景中物体初始位置没有穿插。对于运动学物体移动的平台,确保其移动平滑,避免瞬移导致穿透。
  • 降低物理材质的Bounciness,或调整Bounce Combine模式为Minimum

6.2 高速物体穿透(隧道效应)

现象:子弹、高速移动的玩家穿过了薄墙。原因:物理引擎是按固定时间步长检测的。如果物体速度太快,在一帧内移动的距离超过了其自身尺寸,就可能从墙的一侧“跳”到另一侧,中间过程没有被检测到。解决

  1. 连续碰撞检测:为高速物体的Rigidbody设置Collision DetectionContinuous(连续)或Continuous Dynamic(连续动态)。这会启用更耗性能但能追踪轨迹的检测算法。
  2. 增加碰撞体厚度:把墙做厚一点。
  3. 降低速度或提高物理更新频率:权衡性能和效果。
  4. 使用射线投射作为补充:对于子弹,除了用刚体,还可以每帧用Raycast从上一帧位置到当前帧位置检测中间路径。

6.3OnTriggerOnCollision事件不触发

排查步骤

  1. 检查双方都有碰撞体:这是最基本的。
  2. 检查至少一方有刚体:触发器事件要求至少一方有刚体;碰撞事件要求双方都有刚体(且非运动学刚体才能产生力的交互)。
  3. 检查图层碰撞矩阵:确保两个物体所在的图层在Project Settings -> Physics的矩阵中是勾选状态。
  4. 检查脚本是否挂载正确:脚本必须挂在带有碰撞体的GameObject上。
  5. 检查方法名拼写和参数:必须是OnTriggerEnter(Collider other),大小写和参数类型都不能错。
  6. 检查物体是否处于激活状态GameObjectCollider组件都要是激活的。
  7. 对于2D物理:确保使用的是OnTriggerEnter2D(Collider2D other)OnCollisionEnter2D(Collision2D collisionInfo)

6.4 物理表现不一致(不同帧率下)

现象:在60FPS的PC上运行正常,在30FPS的手机上物体飞出去了。原因:代码中存在“帧依赖”问题。例如,在Update中使用Time.deltaTime来施加力,但力的大小与帧率挂钩。帧率低时,deltaTime变大,单次施加的力也变大。黄金法则所有直接修改物理属性(位置、旋转、速度、力)的代码,都必须放在FixedUpdate中,并使用Time.fixedDeltaTime(如果需要进行与时间相关的计算)。

// 错误做法(在Update中): void Update() { rb.AddForce(Vector3.forward * speed * Time.deltaTime); // 帧率低时,力会变大! } // 正确做法(在FixedUpdate中): void FixedUpdate() { rb.AddForce(Vector3.forward * speed * Time.fixedDeltaTime); } // 或者,如果AddForce的参数本身就是一个固定的力值(如跳跃的Impulse),则不需要乘Time。

6.5 性能突然下降

诊断工具:使用Unity Profiler (Window -> Analysis -> Profiler),切换到PhysicsPhysics2D模块。

  • 查看Active Rigidbodies数量是否异常增多。
  • 查看CollisionsTriggers计数是否过高。
  • 检查是否有大量MeshCollider在动态物体上。行动:根据Profiler结果,应用前面提到的优化策略:标记Static、简化碰撞体、启用刚体睡眠、优化图层矩阵、减少动态物体数量。

物理系统是Unity里一个深不见底的子系统,它看似自动,实则处处需要精心设计和调校。从引擎选型到组件参数,从脚本交互到性能优化,每一个环节都影响着最终游戏的品质和体验。记住,没有“最好”的设置,只有“最适合”你当前项目的配置。多测试,多分析,用Profiler说话,用玩家的手感反馈来验证。希望这篇长文能帮你建立起Unity物理系统的完整知识地图,少走些我当年走过的弯路。