Cocos Creator物理系统全解析:从引擎选型到实战避坑指南
1. 项目概述为什么物理系统是游戏开发的基石如果你刚开始接触Cocos Creator可能会觉得物理系统是一个“高级”话题离自己还很远。但事实上无论你是想做一款简单的2D平台跳跃游戏还是一个复杂的3D模拟经营游戏物理系统都是你绕不开的核心模块。它不仅仅是让物体“掉下来”那么简单它定义了游戏世界中物体如何运动、如何碰撞、如何交互是构建真实感和趣味性的关键。想象一下一个没有物理的弹球游戏球会直接穿过挡板一个没有物理的赛车游戏车辆会像幽灵一样飘过墙壁。这显然不是我们想要的体验。Cocos Creator内置的物理系统正是为了将现实世界的物理规则或经过游戏化调整的规则引入到虚拟世界中让开发者可以专注于游戏逻辑和玩法设计而无需从零开始编写复杂的碰撞检测和运动模拟代码。在Cocos Creator 3.x版本中物理系统已经发展得非常成熟它不是一个单一的方案而是一个支持多种后端引擎的、可配置的模块化系统。这意味着你可以根据项目需求是追求性能的微信小游戏还是追求真实感的原生平台大作来选择最合适的物理后端。理解这套系统的工作原理和配置方法是成为一名合格游戏开发者的必经之路。接下来我将带你从零开始彻底搞懂Cocos Creator的物理系统包括如何选择、配置以及在实际开发中避开那些常见的“坑”。2. 物理引擎选型Bullet、Cannon.js、PhysX与Builtin深度解析当你新建一个Cocos Creator项目物理系统默认是开启的但具体使用哪个“引擎”来驱动则需要你根据项目情况做出关键选择。这个选择直接影响着游戏的性能、包体大小和物理模拟的真实度。Cocos Creator目前主要支持四种物理后端Builtin、Cannon.js、Bullet (ammo.js)和PhysX。它们各有优劣绝不是随便选一个就行。2.1 内置轻量级选项BuiltinBuiltin顾名思义是Cocos Creator内置的一个最基础的物理模块。它的核心功能只有一个基础的碰撞检测Trigger。它不包含任何物理模拟计算比如重力、速度、摩擦力等动力学模拟它统统没有。什么时候用Builtin如果你的游戏只需要知道两个物体“是否碰到了”而不需要它们产生真实的物理反应那么Builtin是你的最佳选择。例如角色进入一个区域触发剧情触发器。子弹击中敌人时检测伤害通常用射线或触发器而非刚体碰撞。任何只需要“碰撞事件”而不需要“物理反馈”的场景。使用Builtin的注意事项功能阉割所有3D刚体组件RigidBody、力组件ConstantForce、关节约束Joint都将完全无效。碰撞体变触发器所有3D碰撞体组件BoxCollider, SphereCollider等的IsTrigger属性无效它们全部被当作触发器来使用。无物理材质碰撞体上的Material属性用于设置摩擦力、弹性无效。性能与包体这是它的最大优势。由于没有复杂的物理计算库使用Builtin可以最大程度地减小游戏包体提升运行时性能特别适合对包体大小极其敏感的微信小游戏等平台。实操心得对于超轻量级的休闲游戏如点消、卡牌对战我强烈建议先评估是否真的需要完整物理。很多时候用Builtin处理简单的碰撞检测再配合一些Tween动画来模拟物理效果如物体弹跳既能满足需求又能获得最佳的包体和性能表现。2.2 纯JavaScript的均衡之选Cannon.jsCannon.js是一个用纯JavaScript编写的开源3D物理引擎。它的特点是全平台兼容性好因为不依赖任何原生库或WASM模块。在Cocos Creator中引入后它的模块大小约为141KB是一个在功能和体积间取得不错平衡的选择。Cannon.js的核心特点完整的刚体物理支持质量、速度、力、扭矩等完整的动力学模拟。基础的碰撞体支持盒子Box、球体Sphere等基本形状的碰撞检测。物理材质可以设置摩擦力、恢复系数弹性。射线检测用于射击、拾取等逻辑。工作流程在Cannon.js后端下你必须为节点添加刚体组件RigidBody物理系统才会对其进行动力学模拟。然后再根据需要添加碰撞体组件来定义其形状。适用场景需要完整物理模拟但项目以Web平台特别是小游戏为主希望避免WASM的兼容性或加载问题。项目物理交互相对简单不需要特别复杂的碰撞形状如凸包、三角网格或高级特性如车辆、布料模拟。作为从Builtin到更强大物理引擎的一个过渡学习选择因为其API相对直观。2.3 功能强大的WASM方案Bullet (ammo.js)Bullet是业界知名的开源物理引擎被广泛应用于许多3A游戏和电影制作中。Cocos Creator通过ammo.js使用Emscripten工具将Bullet编译为WebAssembly/asm.js版本将其引入。这是Cocos Creator默认的物理引擎也是功能最全面、模拟最精确的选择之一。Bullet的核心优势功能全面支持刚体、软体、绳索、车辆、角色控制器等高级特性。碰撞形状丰富除了基本形状还支持凸包Convex Hull、三角网格静态碰撞体Static Triangle Mesh等复杂形状。连续碰撞检测CCD防止高速运动的物体如子弹穿过薄墙。物理约束关节支持铰链、滑块、固定、通用等多种关节可用于制作机械、布娃娃等。性能与精度WASM版本性能远超纯JS方案模拟更加稳定和真实。需要注意的代价包体体积ammo.js模块较大约1.5MB会对小游戏等平台的包体产生显著压力。初始化时间WASM模块需要加载和初始化在Web平台会导致游戏启动时间略有增加。适用场景需要复杂、真实物理模拟的中重度游戏。项目目标平台包含PC、原生App可以承受一定的包体大小。需要使用复杂碰撞形状或高级物理特性。2.4 面向原生的高性能王者PhysXPhysX是NVIDIA公司开发的高性能物理模拟引擎在游戏主机和PC平台享有盛誉。Cocos Creator集成了PhysX 4.1旨在为原生平台iOS, Android, Windows, macOS提供顶级的物理性能和稳定性。PhysX的突出特点极致性能在原生平台上其性能通常优于Bullet特别是在复杂场景和多物体交互时。高稳定性经过大量商业项目验证模拟结果非常稳定不易出现物体抖动、穿透等异常现象。iOS平台优势由于iOS对WASM的支持 historically 存在一些性能开销使用PhysX原生库能获得比Bullet (WASM) 更好的性能。重要限制与考量包体巨大PhysX原生库的引入会使原生平台的包体增加约5MB或更多对于包体有严格限制的项目如某些小游戏渠道需谨慎。平台支持度目前部分有严格包体限制的小游戏平台、以及一些较新的或特定架构的平台如初期的HarmonyOS设备、x86模拟器等可能无法使用或支持不完善。Apple Silicon (M1) 架构的设备从v3.4版本开始已得到支持。效果差异PhysX与Bullet内部的算法实现不同可能导致相同的参数产生略微不同的物理效果。例如刚体的阻尼Damping计算模型就存在差异虽然引擎内部会尽量抹平但对于从Bullet迁移到PhysX的项目可能需要细微调整。适用场景以iOS、Android原生应用或PC端游为主要发布平台且对物理性能和模拟真实性有极高要求的项目。项目本身包体空间充裕不介意物理库带来的体积增加。2.5 引擎选型速查与决策指南为了帮助你快速决策我整理了以下对比表格特性BuiltinCannon.jsBullet (ammo.js)PhysX说明核心功能仅触发检测基础刚体物理完整刚体物理高级特性完整刚体物理高性能功能依次增强碰撞形状基础形状盒子、球体盒子、球体、胶囊、圆柱、凸包、三角网格等盒子、球体、胶囊、凸包、三角网格等Bullet/PhysX支持复杂形状静态地形不支持支持支持静态网格支持静态网格制作复杂关卡地形的关键连续碰撞检测不支持有限支持支持支持防止高速穿透物理材质无效支持支持支持定义摩擦力和弹性关节/约束无效不支持支持铰链、滑块等支持铰链、固定等制作机械结构发布平台全平台全平台纯JS全平台WASM主要为原生平台选择时首要考虑平台包体影响几乎无较小 (~141KB)较大 (~1.5MB WASM)很大 (~5MB 原生)小游戏项目需重点权衡性能表现最优一般Web/小游戏平台优原生平台最优性能与平台强相关学习成本最低较低中等中等均使用Cocos统一API决策流程建议问需求我的游戏需要真实的物理反馈吗还是只需要碰撞事件仅需事件 -Builtin。定平台我的主要发布平台是哪里微信/抖音小游戏等Web平台优先考虑Bullet功能全或Cannon.js包体小。若物理简单至极可用Builtin。iOS/Android原生APP强烈推荐PhysX以获得最佳性能。若考虑跨平台统一可选Bullet。PC或主机PhysX是首选。看包体项目对包体大小是否敏感极度敏感 5MBBuiltin或Cannon.js。有一定空间~10MBBullet。空间充足PhysX原生或Bullet全平台。测效果在项目中期如果对效果不满意可以在项目设置 - 功能裁剪 - 物理系统中随时切换引擎Builtin除外切换至它会导致物理组件失效。建议对关键物理场景进行AB测试。3. 物理系统核心组件详解与配置实战选好了引擎接下来就是如何在场景中实际使用物理。Cocos Creator的物理系统遵循组件化设计主要通过以下几种组件协作工作。理解它们之间的关系是正确使用物理的关键。3.1 刚体RigidBody物理世界的“演员”刚体组件是物理模拟的核心。任何需要受到重力影响、被力推动、或者参与动力学模拟的物体都必须挂载刚体组件。你可以把它理解为这个物体在物理世界中的“身份证”和“控制中枢”。关键属性解析类型TypeDYNAMIC动态刚体。受重力、力影响会与其他刚体发生碰撞并产生反应。例如玩家、箱子、篮球。STATIC静态刚体。质量无限大不受力影响永远不会移动。通常用于地面、墙壁等固定环境。性能最优。KINEMATIC运动学刚体。不受物理力影响但可以通过代码直接设置其位置、旋转来运动。当它运动时会推动撞到的动态刚体。例如移动的平台、电梯。质量Mass物体的质量影响其惯性。质量越大越难被推动或改变运动状态。线性阻尼/角度阻尼Linear/Angular Damping相当于空气阻力用于逐渐减慢物体的移动速度或旋转速度。值越大物体停止得越快。设置适当的阻尼可以让运动看起来更自然而不是像在真空中一样永远滑动。允许休眠Allow Sleep当刚体速度几乎为零且一段时间未受外力时物理引擎会将其置为“休眠”状态不再计算其物理以节省性能。大多数情况应该开启。线性因子/角度因子Linear/Angular Factor这是一个非常实用的属性用于锁定刚体在某个轴上的移动或旋转。例如将一个角色的刚体LinearFactor的y设为0它就不会因重力下落将AngularFactor的x和z设为0它就不会向前后或左右翻滚。实操心得动态与静态的选择。很多新手会把所有不动的物体都设为STATIC这没错。但要小心那些“看起来不动但未来可能需要动”的物体比如一扇可以被炸毁的门。如果一开始设为STATIC后来想改为DYNAMIC可能会因为初始状态穿插等问题导致奇怪的物理现象。一个保守的做法是对于可能动态的物体初始设为DYNAMIC但质量设为很大并用代码固定其位置需要时再减小质量或施加力。3.2 碰撞体Collider定义物体的“形状”刚体决定了物体“如何运动”而碰撞体则定义了物体“是什么形状”。物理引擎根据碰撞体的形状来进行碰撞检测。一个刚体可以附带多个碰撞体。3D碰撞体类型盒碰撞体BoxCollider长方体形状最常用性能最好。球碰撞体SphereCollider球体形状。胶囊碰撞体CapsuleCollider胶囊形状非常适合用于角色控制器因为它在斜坡上不易卡住且能模拟人的体型。圆柱碰撞体CylinderCollider圆柱形状。注意在Bullet等引擎中圆柱体的碰撞计算可能不如盒子或球体稳定。网格碰撞体MeshCollider使用模型的三角网格作为碰撞形状。这是性能杀手应尽量避免用于动态物体。通常仅用于极其复杂的静态地形如岩石、山脉且应尽量使用简化的碰撞网格而非渲染用的高模。凸包碰撞体ConvexCollider用一组顶点生成一个包裹物体的凸包形状。比网格碰撞体性能好适用于形状不规则的动态物体如一个榔头。但顶点数不宜过多。地形碰撞体TerrainCollider专用于地形组件效率很高。核心属性是否为触发器IsTrigger如果勾选则该碰撞体只触发碰撞事件不产生物理碰撞反应。物体可以相互穿过。用于触发区域、技能范围检测等。物理材质Material引用一个PhysicsMaterial资源用于定义该表面的摩擦力Friction和弹性Restitution。弹性为1表示完全弹性碰撞为0表示完全非弹性碰撞。附着节点Attached通常自动关联到刚体所在节点。可用于实现更复杂的父子刚体关系。3.3 物理材质PhysicsMaterial定义表面的“触感”物理材质是一个独立的资源文件.physic你可以在资源管理器中创建它。它定义了物体表面的物理特性摩擦力Friction物体在该表面滑动时的阻力。0为光滑如冰面1为非常粗糙。弹性Restitution碰撞后速度恢复的程度。0表示碰撞后完全不反弹如泥巴1表示完全反弹理想弹性体如超级弹力球。配置技巧通常你会为几种典型的表面创建材质如Ice低摩擦低弹性、Wood中摩擦中低弹性、Rubber高摩擦高弹性、Metal中摩擦高弹性。然后在碰撞体上按需引用。3.4 物理系统全局配置物理世界的全局行为在项目设置 - 物理中配置。这里有几个关键参数重力Gravity物理世界的重力加速度向量。默认是(0, -10, 0)即Y轴向下10个单位/秒²。你可以根据游戏风格调整比如月球重力可以设为(0, -1.6, 0)或者横版游戏设为(0, 0, 0)并自行处理下落逻辑。允许休眠Allow Sleep全局开关建议开启。固定时间步Fixed Time Step物理模拟的更新频率。默认0.016667秒即60Hz。不要随意修改。降低它如0.02可以提高性能但降低模拟精度提高它如0.01增加精度但消耗性能。通常保持默认即可。最大子步数Max Sub Steps当游戏帧率波动一帧的时间超过Fixed Time Step时物理引擎会在这一帧内进行多次子步计算来“追赶”真实时间。此值限制了最大子步数防止卡顿时物理计算消耗过多性能。默认10步通常足够。碰撞矩阵Collision Matrix这是性能优化和逻辑控制的神器。它定义了不同分组Layer的物体之间是否进行碰撞检测。碰撞矩阵实战假设你的游戏有这些层Default,Player,Enemy,Bullet,Ground,Item。你肯定不希望Bullet和Bullet之间相互碰撞子弹对射穿模那么就在矩阵中取消Bullet与Bullet的勾选。你希望Player和Item只触发事件而不发生物理碰撞玩家直接穿过宝物拾取那么可以勾选它们之间的检测用于触发事件但通过代码或设置刚体类型来避免物理反应。通过精细配置碰撞矩阵可以大幅减少不必要的碰撞检测对Pair数量这是提升物理性能最有效的手段之一。4. 从零构建一个物理场景小球撞击积木塔理论说得再多不如动手实践。让我们创建一个经典的物理demo一个球从斜坡滚下撞击一座由方块堆成的塔。步骤1创建基础场景与物理配置新建一个3D场景。打开项目设置-功能裁剪-物理系统确保你选择的物理后端已启用例如默认的Bullet。在项目设置-物理中确认重力为(0, -10, 0)。步骤2创建静态地面和斜坡在场景中创建一个Cube节点重命名为Ground缩放至(20, 1, 20)。这将是地面。为Ground节点添加BoxCollider组件。因为是静态地面不需要添加RigidBody组件或者添加后类型设为STATIC。碰撞体会自动与场景中的静态几何体关联。再创建一个Cube节点重命名为Ramp作为斜坡。将其旋转例如Rotation X -30并放置在地面一端。同样为Ramp添加BoxCollider组件。由于它也是静止的不添加刚体或设为STATIC。步骤3创建动态小球创建一个Sphere节点重命名为Ball放置在斜坡顶端。为Ball节点添加RigidBody组件。类型默认为DYNAMIC。你可以调整Mass为5让它重一些。为Ball节点添加SphereCollider组件。组件会自动匹配球体模型的半径。创建一个物理材质BouncyRubber设置Friction为0.8Restitution为0.9很有弹性。并将该材质拖拽到Ball的SphereCollider组件的Material属性上。步骤4创建积木塔创建一个Cube节点重命名为Block缩放为(1.8, 0.5, 0.5)像一个长木板。为Block添加RigidBodyDYNAMIC和BoxCollider组件。创建一个物理材质Wood设置Friction为0.6Restitution为0.2并赋予它。在层级管理器中右键Block-复制创建多个副本。使用移动工具快捷键W手动将这些Block堆叠起来形成一个简单的塔状结构。注意对齐避免初始状态就穿插太严重否则物理引擎启动时可能会产生巨大的力导致塔炸开。步骤5编写简单的控制脚本为了能互动我们写一个脚本让小球在按下空格时获得一个向前的力。在资源管理器右键创建TypeScript脚本命名为BallController。将脚本挂载到Ball节点上。编辑脚本内容import { _decorator, Component, input, Input, KeyCode, RigidBody, Vec3 } from cc; const { ccclass, property } _decorator; ccclass(BallController) export class BallController extends Component { // 可以调整施加的力的大小 property public impulsePower: number 10; start() { // 注册键盘输入事件 input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this); } onKeyDown(event: any) { // 按下空格键时给小球一个向前的冲量 if (event.keyCode KeyCode.SPACE) { const rigidBody this.getComponent(RigidBody); if (rigidBody) { // 施加一个世界坐标系Z轴正方向的冲量 rigidBody.applyImpulse(new Vec3(0, 0, this.impulsePower)); } } } onDestroy() { // 记得销毁时取消事件监听 input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this); } }步骤6运行与观察点击编辑器上方的运行按钮。你会看到小球因重力从斜坡滚下撞击积木塔。按下空格键小球会受到一个向前的力撞击效果会更明显。观察物理模拟是否稳定塔的倒塌是否自然。5. 物理开发中的常见“坑”与排查技巧即使按照教程一步步来在实际开发中你还是会遇到各种奇怪的物理问题。这里我总结了一些最常见的“坑”和解决方法。5.1 物体抖动或穿透这是最常见的问题之一。原因1缩放Scale非均匀或过大/过小。物理引擎对物体的尺度很敏感。尽量避免使用极端的缩放值如0.01或1000。确保碰撞体组件的Scale属性是(1,1,1)缩放应由节点本身的Scale控制。原因2质量Mass差异过大。一个质量1的物体去碰撞一个质量10000的物体很容易出现抖动。尽量让相互作用的物体质量在同一个数量级内。原因3帧率过低或物理步长设置不当。如果游戏帧率FPS远低于物理更新频率默认60Hz物体可能在一帧内移动了很远的距离导致穿透。可以尝试适当增加Fixed Time Step或启用刚体的连续碰撞检测CCD如果引擎支持。排查首先检查节点的缩放和碰撞体尺寸。其次在RigidBody组件上勾选Enable CCD如果可用。对于高速运动的子弹更应该使用射线检测PhysicsSystem.instance.raycast而非碰撞体。5.2 刚体“飘”在空中或下坠缓慢原因最常见的原因是线性因子Linear Factor被错误设置。如果你不小心将Y轴的因子设为0刚体就不会受重力影响在Y轴方向运动。排查检查RigidBody组件中的Linear Factor属性确保它是(1,1,1)除非你确实需要锁定某个轴。5.3 碰撞事件不触发检查清单双方都有碰撞体吗至少一方需要有碰撞体组件。碰撞矩阵允许吗在项目设置-物理-碰撞矩阵中确认这两个物体所在的层Layer之间的碰撞检测是勾选的。其中一方是触发器吗如果A是触发器IsTriggertrueB是普通碰撞体它们会触发onTriggerEnter事件但不会触发onCollisionEnter事件。你的脚本监听对了吗刚体类型对吗两个都是STATIC类型的刚体它们之间不会产生碰撞事件。至少一方是DYNAMIC或KINEMATIC。脚本挂对了吗监听碰撞事件的脚本必须挂载在带有碰撞体组件的节点上。函数名写对了吗Cocos Creator使用特定的回调函数名普通碰撞onCollisionEnter,onCollisionStay,onCollisionExit触发器碰撞onTriggerEnter,onTriggerStay,onTriggerExit5.4 性能突然下降当场景中动态刚体数量很多时性能问题会凸显。优化策略1善用休眠Sleep。确保刚体的Allow Sleep属性开启。静止的物体会进入休眠不再消耗计算资源。优化策略2简化碰撞形状。能用Box/Sphere就别用Mesh/Convex。对于复杂静态场景使用多个简单碰撞体组合远比一个复杂网格碰撞体高效。优化策略3精细配置碰撞矩阵。这是最重要的优化手段。确保无关的物体层之间不进行碰撞检测如UI层和Enemy层。优化策略4控制动态刚体数量。对于大量相同的小物体如子弹、碎片考虑使用对象池Object Pooling并在一段时间后回收或休眠而不是无限创建。工具使用Cocos Creator的分析器Profiler查看Physics部分的耗时定位性能瓶颈。5.5 不同物理后端的效果不一致正如前面提到的从Bullet切换到PhysX后同样的场景可能感觉“不一样了”。阻尼问题如果之前用Bullet调好的阻尼值在PhysX下感觉物体停得太快或太慢可能需要参考官方文档的公式进行转换。因子问题PhysX对Linear/Angular Factor的支持是固定的可能无法完全模拟Bullet中某些轴的缩放效果。如果依赖这个特性切换时需注意。最佳实践尽早确定物理后端并贯穿整个开发周期。避免在项目后期切换引擎因为调整所有物理参数将是一项浩大的工程。在项目初期就用目标引擎进行原型测试。物理系统是连接游戏逻辑与虚拟世界真实感的桥梁。掌握它意味着你能够创造出更具沉浸感和趣味性的交互体验。从理解四大引擎的选型开始到熟练运用刚体、碰撞体、材质三大组件再到通过碰撞矩阵和参数调优来控制性能与效果每一步都需要在项目中反复实践和体会。记住没有“最好”的配置只有“最适合”你当前项目需求的配置。多动手实验善用调试工具你就能让游戏世界中的每一个碰撞、每一次坠落都恰到好处。