creator 3.x 2D 物理引擎 基础使用

发布时间 2023-08-08 18:07:54作者: lesten

首先, 本文基于 V3.5 , 官方文档在这里: https://docs.cocos.com/creator/3.5/manual/zh/physics/

一.
明确 2D 物理引擎, 和 3D 物理引擎 在接口上有点区别, 实际区别我也不清楚在哪里, 官方文档也没说.
比如:

启用物理引擎
PhysicsSystem2D.instance.enable = true;

3D 的叫 
PhysicsSystem.instance.enable = true;

比如:
刚体 3D 的叫 Rigidbody, 2D 的叫  Rigidbody2D
碰撞体 3D 的叫 BoxCollider, 2D 的叫 BoxCollider2D

一开始比较懵逼.

开启 调试, 会画出 物理世界中的刚体 或 碰撞体.

PhysicsSystem2D.instance.debugDrawFlags = EPhysics2DDrawFlags.All;

/*
    export enum EPhysics2DDrawFlags {
      None = 0,
      Shape = 1,
      Joint = 2,
      Aabb = 4,
      Pair = 8,
      CenterOfMass = 16,
      Particle = 32,
      Controller = 64,
      All = 63
    }
*/

二. 刚体想象成一个你不能看到(绘制)也不能摸到(碰撞)的且不能变形的物体。 Rigidbody2D 即可以添加 2D 刚体组件。
由于 Builtin 2D 物理系统只带有碰撞检测的功能,所以刚体对于 Builtin 2D 物理系统是不生效的,本篇设置只对 Box 2D 物理系统产生作用。
基本属性:

属性 说明
Group 刚体的分组。通过 碰撞矩阵 可设置不同分组间碰撞的可能性
EnabledContactListener 开启监听碰撞回调
Bullet 这个刚体是否是一个快速移动的刚体,并且需要禁止穿过其他快速移动的刚体, 请参考 Rigidbody2D API 获取更多信息
Type 刚体类型,详情请参考下方 刚体类型
AlllowSleep 是否允许刚体休眠, 物理配置 中可调整休眠的临界值
GravityScale 重力缩放比例, 仅对 Dynamic 类型的刚体生效
LinearDamping 移动速度衰减系数
AngularDamping 旋转速度衰减系数
LinearVelocity 移动速度, 仅对 Dynamic 和 Kinematic 类型的刚体生效
AngularVelocity 旋转速度, 仅对 Dynamic 和 Kinematic 类型的刚体生效
FixedRotation 是否固定旋转
AwakeOnLoad 加载完成后立刻唤醒刚体
AngularVelocity 旋转速度, 仅对 Dynamic 和 Kinematic 类型的刚体生效
FixedRotation 是否固定旋转
AwakeOnLoad 加载完成后立刻唤醒刚体
  // 获取移动速度
  const velocity = rigidbody.linearVelocity;
  // 获取旋转速度
  const velocity = rigidbody.angularVelocity;
  // 获取旋转速度衰减系数
  const damping = rigidbody.angularDamping;

刚体类型 说明

刚体类型 说明
Static 静态刚体,零质量,零速度,即不会受到重力或速度影响,但是可以设置他的位置来进行移动。该类型通常用于制作场景
Dynamic 动态刚体,有质量,可以设置速度,会受到重力影响。 唯一可以通过 applyForce 和 applyTorque 等方法改版受力的刚体类型
Kinematic 运动刚体,零质量,可以设置速度,不会受到重力的影响,但是可以设置速度来进行移动
Animated 动画刚体,在上面已经提到过,从 Kinematic 衍生的类型,主要用于刚体与动画编辑结合使用. 仅对 Dynamic 和 Kinematic 类型的刚体生效

三. 碰撞体

Editing 是否对碰撞组件进行编辑。勾选后可以在场景内编辑碰撞组件的位置、样式和大小。详情请参考下方 编辑碰撞组件
Tag 标签。当发生碰撞后,可根据 Tag 区分不同碰撞组件
Group 碰撞组件分组。通过 碰撞矩阵 可设置不同分组间碰撞的可能性
Sensor 指明碰撞组件是否为传感器类型,传感器类型的碰撞组件会产生碰撞回调,但是不会发生物理碰撞效果
Density 碰撞组件的密度,用于刚体的质量计算
Friction 碰撞组件摩擦力系数,碰撞组件接触时的运动会受到摩擦力影响
Restitution 碰撞组件的弹性系数,指明碰撞组件碰撞时是否会受到弹力影响
Offset 碰撞组件相对于节点中心的偏移

四. 碰撞回调 demo 代码

@ccclass('TestContactCallBack')
export class TestContactCallBack extends Component {
    start () {

        // 注册单个碰撞体的回调函数
        let collider = this.getComponent(Collider2D);
        if (collider) {
            collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
            collider.on(Contact2DType.END_CONTACT, this.onEndContact, this);
            collider.on(Contact2DType.PRE_SOLVE, this.onPreSolve, this);
            collider.on(Contact2DType.POST_SOLVE, this.onPostSolve, this);
        }

        // 注册全局碰撞回调函数
        if (PhysicsSystem2D.instance) {
            PhysicsSystem2D.instance.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
            PhysicsSystem2D.instance.on(Contact2DType.END_CONTACT, this.onEndContact, this);
            PhysicsSystem2D.instance.on(Contact2DType.PRE_SOLVE, this.onPreSolve, this);
            PhysicsSystem2D.instance.on(Contact2DType.POST_SOLVE, this.onPostSolve, this);
        }
    }
    onBeginContact (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        // 只在两个碰撞体开始接触时被调用一次
        console.log('onBeginContact');
    }
    onEndContact (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        // 只在两个碰撞体结束接触时被调用一次
        console.log('onEndContact');
    }
    onPreSolve (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        // 每次将要处理碰撞体接触逻辑时被调用
        console.log('onPreSolve');
    }
    onPostSolve (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        // 每次处理完碰撞体接触逻辑时被调用
        console.log('onPostSolve');
    }
}

五 设置好 刚体 碰撞体以后, 需要在项目中设置 相关内容
项目 => 项目设置 => 物理 => 碰撞矩阵

  1. 设置碰撞分组

    设置好之后 状态显示 不一定会实时更新, 需要重新打开.

  2. 配置 使用的物理引擎, 是基于 Box2D 还是 builtin 2D 物理系统

六 还有个需要注意的问题,
在添加全局的碰撞监听后直接去销毁对象会产生Uncaught Error

// 等待物理回调销毁
director.once(Director.EVENT_AFTER_PHYSICS, ()=>{
	selfCollider.node.destroy();
})

// 延迟一帧后销毁
setTimeout(()=>{
    selfCollider.node.destroy();            
}, 50)

解决方案自这里: https://forum.cocos.org/t/topic/106186