
1. 为什么需要动态添加刚体和碰撞体在Cocos Creator游戏开发中我们经常会遇到需要运行时动态创建物理元素的场景。比如一个射击游戏里子弹的生成、一个平台跳跃游戏里随机出现的障碍物或者一个消除游戏里新生成的方块。这些情况都要求我们能够在代码中实时创建带有物理特性的对象。静态的物理元素可以直接在编辑器里设置但动态生成的元素必须通过代码来赋予物理属性。这里就涉及到两个核心组件刚体RigidBody和碰撞体Collider。刚体决定了物体如何参与物理模拟如重力、速度等碰撞体则定义了物体的物理形状和碰撞边界。2. 准备工作物理系统初始化2.1 启用物理引擎在开始之前我们需要确保物理系统已经正确初始化。在Cocos Creator中物理引擎默认是关闭的需要在游戏启动时手动开启// 在游戏启动脚本中 import { PhysicsSystem } from cc; onLoad() { // 初始化物理系统 PhysicsSystem.instance.enable true; // 设置重力根据你的游戏需求调整 PhysicsSystem.instance.gravity new Vec3(0, -10, 0); // 设置物理步长影响物理模拟精度 PhysicsSystem.instance.fixedTimeStep 1/60; }2.2 创建基础节点动态添加物理组件通常需要一个节点作为载体。我们可以先创建一个空节点然后再为其添加物理组件const newNode new Node(PhysicsObject); this.node.addChild(newNode); // 设置节点位置如果需要 newNode.setPosition(0, 0, 0);3. 添加刚体组件3.1 创建刚体组件刚体是物理模拟的核心组件它决定了物体如何参与物理运动。在Cocos Creator中我们可以这样添加刚体import { RigidBody } from cc; // 添加刚体组件 const rigidBody newNode.addComponent(RigidBody); // 设置刚体类型动态、静态或运动学 rigidBody.type RigidBodyType.Dynamic; // 设置质量 rigidBody.mass 1; // 设置线性阻尼影响移动阻力 rigidBody.linearDamping 0.1; // 设置角阻尼影响旋转阻力 rigidBody.angularDamping 0.1;3.2 刚体类型选择Cocos Creator提供了三种刚体类型根据需求选择合适的类型很重要类型描述适用场景Dynamic完全受物理引擎控制玩家角色、可互动物体Static不受物理影响不会移动地面、墙壁等静态环境Kinematic通过代码控制移动但能影响其他刚体平台、移动障碍物4. 添加碰撞体组件4.1 碰撞体类型选择Cocos Creator支持多种碰撞体形状常用的有BoxCollider- 长方体碰撞体SphereCollider- 球体碰撞体CapsuleCollider- 胶囊体碰撞体PolygonCollider- 多边形碰撞体2DCircleCollider- 圆形碰撞体2D4.2 添加盒形碰撞体示例import { BoxCollider } from cc; // 添加盒形碰撞体 const collider newNode.addComponent(BoxCollider); // 设置碰撞体大小 collider.size new Vec3(1, 1, 1); // 长宽高 // 设置碰撞体中心点偏移可选 collider.center new Vec3(0, 0.5, 0); // 设置是否为触发器不产生物理效果只检测碰撞 collider.isTrigger false;4.3 添加球形碰撞体示例import { SphereCollider } from cc; const collider newNode.addComponent(SphereCollider); // 设置球体半径 collider.radius 0.5; // 设置球心位置偏移 collider.center new Vec3(0, 0, 0);5. 物理材质配置5.1 创建和配置物理材质物理材质决定了物体碰撞时的表现如摩擦力、弹性等import { PhysicsMaterial } from cc; // 创建物理材质 const material new PhysicsMaterial(); material.friction 0.5; // 摩擦力系数 material.restitution 0.3; // 弹性系数 // 将材质应用到碰撞体 collider.material material;5.2 材质参数说明参数范围说明friction0-1摩擦力0表示无摩擦1表示最大摩擦restitution0-1弹性0表示无弹性1表示完全弹性碰撞6. 碰撞检测与处理6.1 注册碰撞回调要让物体能够响应碰撞事件需要注册相应的回调函数import { Collider, ITriggerEvent } from cc; // 获取碰撞体组件 const collider newNode.getComponent(Collider); // 注册碰撞开始回调 collider.on(onCollisionEnter, this.onCollisionEnter, this); // 注册碰撞持续回调 collider.on(onCollisionStay, this.onCollisionStay, this); // 注册碰撞结束回调 collider.on(onCollisionExit, this.onCollisionExit, this); // 注册触发器开始回调 collider.on(onTriggerEnter, this.onTriggerEnter, this); // 注册触发器持续回调 collider.on(onTriggerStay, this.onTriggerStay, this); // 注册触发器结束回调 collider.on(onTriggerExit, this.onTriggerExit, this);6.2 实现回调函数// 碰撞开始 onCollisionEnter(event: ICollisionEvent) { console.log(碰撞开始:, event.otherCollider.node.name); } // 碰撞持续 onCollisionStay(event: ICollisionEvent) { // 每帧碰撞时调用 } // 碰撞结束 onCollisionExit(event: ICollisionEvent) { console.log(碰撞结束:, event.otherCollider.node.name); } // 触发器开始 onTriggerEnter(event: ITriggerEvent) { console.log(触发开始:, event.otherCollider.node.name); } // 触发器持续 onTriggerStay(event: ITriggerEvent) { // 每帧触发时调用 } // 触发器结束 onTriggerExit(event: ITriggerEvent) { console.log(触发结束:, event.otherCollider.node.name); }7. 动态修改物理属性7.1 运行时修改刚体属性// 获取刚体组件 const rigidBody node.getComponent(RigidBody); // 施加力 rigidBody.applyForce(new Vec3(0, 10, 0)); // 施加冲量 rigidBody.applyImpulse(new Vec3(5, 0, 0)); // 设置速度 rigidBody.linearVelocity new Vec3(2, 0, 0); // 设置角速度 rigidBody.angularVelocity new Vec3(0, 1, 0); // 唤醒刚体如果处于休眠状态 rigidBody.wakeUp();7.2 运行时修改碰撞体属性// 获取碰撞体组件 const collider node.getComponent(BoxCollider); // 修改碰撞体大小 collider.size new Vec3(2, 2, 2); // 启用/禁用碰撞体 collider.enabled false; // 修改是否为触发器 collider.isTrigger true;8. 性能优化建议8.1 对象池管理频繁创建和销毁物理对象会产生性能开销建议使用对象池import { NodePool, instantiate } from cc; // 创建对象池 const physicsObjectPool new NodePool(); // 初始化对象池 for (let i 0; i 10; i) { const obj instantiate(this.physicsPrefab); physicsObjectPool.put(obj); } // 从对象池获取对象 const getPhysicsObject () { let obj null; if (physicsObjectPool.size() 0) { obj physicsObjectPool.get(); } else { obj instantiate(this.physicsPrefab); } return obj; }; // 回收对象到对象池 const recyclePhysicsObject (obj: Node) { physicsObjectPool.put(obj); };8.2 物理更新频率调整对于不需要高精度物理模拟的游戏可以降低物理更新频率// 在游戏启动脚本中 PhysicsSystem.instance.fixedTimeStep 1/30; // 默认是1/608.3 休眠机制利用对于暂时不动的物理对象可以启用休眠rigidBody.sleep();当需要时再唤醒rigidBody.wakeUp();9. 常见问题与解决方案9.1 碰撞体不生效的可能原因物理系统未启用确保PhysicsSystem.instance.enable true节点未激活检查node.active是否为true碰撞体未启用检查collider.enabled是否为true层级碰撞矩阵未设置在项目设置中检查物理碰撞矩阵刚体类型错误静态刚体不会对碰撞做出反应9.2 碰撞检测不准确碰撞体尺寸不匹配确保碰撞体大小与视觉元素匹配物理材质太滑调整摩擦系数刚体质量差异过大质量差异过大会导致碰撞表现异常物理步长太大尝试减小PhysicsSystem.instance.fixedTimeStep9.3 性能问题排查物理对象过多使用对象池管理复杂碰撞体简化碰撞体形状频繁的物理查询缓存物理查询结果不必要的物理更新对静止物体启用休眠10. 实际应用案例10.1 射击游戏子弹实现// 创建子弹 createBullet(position: Vec3, direction: Vec3) { const bullet new Node(Bullet); this.node.addChild(bullet); bullet.setPosition(position); // 添加刚体 const rigidBody bullet.addComponent(RigidBody); rigidBody.type RigidBodyType.Dynamic; rigidBody.mass 0.1; rigidBody.linearDamping 0; // 添加球形碰撞体 const collider bullet.addComponent(SphereCollider); collider.radius 0.1; collider.isTrigger true; // 子弹通常使用触发器 // 设置初始速度 rigidBody.linearVelocity direction.multiplyScalar(20); // 5秒后自动销毁 setTimeout(() { bullet.destroy(); }, 5000); }10.2 平台游戏可破坏砖块// 创建可破坏砖块 createBreakableBlock(position: Vec3) { const block new Node(Block); this.node.addChild(block); block.setPosition(position); // 添加刚体 const rigidBody block.addComponent(RigidBody); rigidBody.type RigidBodyType.Dynamic; rigidBody.mass 2; // 添加盒形碰撞体 const collider block.addComponent(BoxCollider); collider.size new Vec3(1, 1, 1); // 注册碰撞回调 collider.on(onCollisionEnter, (event) { // 当受到足够大的冲击时销毁 if (event.impulse 5) { block.destroy(); this.createDebris(position); } }, this); }11. 高级技巧与注意事项11.1 复合碰撞体一个节点可以添加多个碰撞体组件来实现复杂形状// 创建角色身体头部 const character new Node(Character); this.node.addChild(character); // 身体胶囊体 const bodyCollider character.addComponent(CapsuleCollider); bodyCollider.height 1.8; bodyCollider.radius 0.3; bodyCollider.center new Vec3(0, 0.9, 0); // 头部球体 const headCollider character.addComponent(SphereCollider); headCollider.radius 0.25; headCollider.center new Vec3(0, 1.7, 0);11.2 物理查询除了碰撞检测还可以主动进行物理查询// 射线检测 const raycastResult PhysicsSystem.instance.raycast( new Vec3(0, 0, 0), // 起点 new Vec3(0, 1, 0), // 方向 100, // 距离 0b1, // 检测层级 true // 是否检测触发器 ); if (raycastResult) { console.log(击中:, raycastResult.collider.node.name); } // 球形检测 const sphereCastResults PhysicsSystem.instance.sphereCast( new Vec3(0, 0, 0), // 中心点 5, // 半径 0b1, // 检测层级 true // 是否检测触发器 ); sphereCastResults.forEach(result { console.log(范围内物体:, result.collider.node.name); });11.3 同步物理与渲染物理模拟和渲染更新是分开的如果需要精确同步update(deltaTime: number) { // 获取刚体的物理位置 const physicsPos this.rigidBody.node.worldPosition; // 同步到渲染节点 this.graphicsNode.setPosition(physicsPos); // 或者强制物理系统立即更新 PhysicsSystem.instance.syncSceneToPhysics(); }12. 调试与可视化12.1 显示碰撞体边框在开发过程中可以开启碰撞体可视化// 在游戏启动脚本中 PhysicsSystem.instance.debugDrawFlags EPhysicsDrawFlags.Aabb | EPhysicsDrawFlags.Pair | EPhysicsDrawFlags.CenterOfMass | EPhysicsDrawFlags.Joint | EPhysicsDrawFlags.Shape;12.2 物理调试信息可以获取各种物理调试信息// 获取物理系统状态 console.log(物理系统状态:, PhysicsSystem.instance.enable); // 获取物理世界信息 console.log(刚体数量:, PhysicsSystem.instance.physicsWorld.bodyCount); console.log(碰撞体数量:, PhysicsSystem.instance.physicsWorld.colliderCount);13. 跨平台注意事项不同平台的物理表现可能略有差异移动端性能减少物理对象数量简化碰撞体浮点精度某些平台浮点计算精度较低避免过小的物理参数休眠策略不同平台对刚体休眠的处理可能不同碰撞检测复杂形状在不同平台上的碰撞检测结果可能不一致14. 版本兼容性不同Cocos Creator版本的物理系统有差异2.4.x及之前使用Bullet物理引擎3.0.x及以上默认使用Cannon.js可选PhysXAPI变化注意组件API在不同版本间的变化建议在项目初期确定使用的物理引擎和版本避免后期迁移成本。