版本: v3.4.0
简介
cocosCreator 内置了 2D 物理系统 和 3D 物理系统,开发者可以通过项目 -> 项目设置 -> 功能裁切来配置物理系统相关:
本文仅对2D 物理系统 做下说明和遇到的问题汇总。该物理系统在cocosCreator的功能裁切中,主要有两个:
- 内置的2D物理系统 :它仅提供了碰撞检测的功能,对于物理计算较为简单的情况,推荐使用。
- 基于Box2D的2D物理系统 : 支持RigidBody2D(刚体)和Joint2D(物理关节)的使用,支持模拟物理世界相关的重力,摩擦,弹性等设置。 运行开销比内置的要大。
简单的理解二者的使用,如果你只需要简单的碰撞检测,类似于飞机射击的那种,用内置的就可以。
如果你需要物体支持重力,摩擦,还要支持碰撞相关,比如合成水果相关,那就Box2D。
使用物理引擎,他们之间均需要Collider2D(碰撞组件)的支持。
2D碰撞组件
目前引擎支持如下几种碰撞组件:
- 盒碰撞组件(BoxCollider2D)
- 圆形碰撞组件(CircleCollider2D)
- 多边形碰撞组件(PolygonCollider2D)
在属性检测器中添加组件,搜索Collider2D
即可查找对应的组件相关。他们的共同属性:
属性名 | 说明 |
---|---|
Editing | 对碰撞组件的区域进行编辑,它不会在程序运行的时候显示出来 |
Tag | 标签,可用于发生碰撞回调时,通过tag 区分不同的碰撞组件,默认为0 |
Group | 碰撞矩阵分组,在项目 -> 物理中可以设置,用于检测不同分组之间的可能性 |
Sensor | 勾选后会有碰撞回调,但没有物理碰撞效果。 比如物体落在地板上,停止移动,就不要勾选。 |
Density | 碰撞组件的密度,用于刚体的质量计算 |
Friction | 摩擦力系数 |
Restitution | 弹性系数 |
Offset | 设置碰撞组件偏移 |
如果只需要有碰撞检测和碰撞回调事件的发生,物理引擎选择内置即可,且必须勾选Sensor
选项。
RigidBody2D 刚体组件
RigidBody2D的使用主要用于Box2D
物理引擎中;如果使用的是内置
物理引擎,切记,该组件是不会生效的。
它的使用可通过属性检查器 -> 添加组件 -> RigidBody2D即可。它的属性:
属性 | 说明 |
---|---|
Group | 刚体的分组。通过 碰撞矩阵 可设置不同分组间碰撞的可能性 |
EnabledContactListener | 开启监听碰撞回调 |
Bullet | 这个刚体是否是一个快速移动的刚体,并且需要禁止穿过其他快速移动的刚体 |
Type | 刚体类型,有Static, Dynamic,Kinematic, Animated等 |
AlllowSleep | 是否允许刚体休眠 |
GravityScale | 重力缩放比例 仅对 Dynamic 类型的刚体生效 |
LinearDamping | 移动速度衰减系数 |
AngularDamping | 旋转速度衰减系数 |
LinearVelocity | 移动速度 仅对 Dynamic 和 Kinematic 类型的刚体生效 |
AngularVelocity | 旋转速度 仅对 Dynamic 和 Kinematic 类型的刚体生效 |
FixedRotation | 是否固定旋转 |
AwakeOnLoad | 加载完成后立刻唤醒刚体 |
与内置引擎相比较,使用Box2D需要碰撞回调的支持,一定要增加该组件,且勾选属性EnableContactListener
。
但要注意的是: Collider2D组件下的Senor
属性就不要再勾选了,否则无法模拟物理世界活动。
碰撞回调
碰撞回调的注册主要有两种:
PhysicsSystem2D.instance.on
注册全局回调clllider.on
注册单个碰撞体的回调
他们的回调事件类型是相同的,主要有:
export const Contact2DType: {None: string;// 两个碰撞体开始接触时调用一次BEGIN_CONTACT: string;// 只在两个碰撞体结束接触时被调用一次END_CONTACT: string;// 每次将要处理碰撞体接触逻辑时被调用PRE_SOLVE: string;// 每次处理完碰撞体接触逻辑时被调用POST_SOLVE: string;
};
回调的实例:
@ccclass('TestContactCallBack')
export class TestContactCallBack extends Component {onLoad () {// 注册单个碰撞体的回调函数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);}// 注册全局碰撞回调函数const System2D = PhysicsSystem2D.instanceif (System2D) {System2D.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);System2D.on(Contact2DType.END_CONTACT, this.onEndContact, this);System2D.on(Contact2DType.PRE_SOLVE, this.onPreSolve, this);System2D.on(Contact2DType.POST_SOLVE, this.onPostSolve, this);}}/*监听回调事件的参数:self: 指的是回调脚本上的节点碰撞体other: 指的是发生碰撞的另一个碰撞体contact: 包含碰撞的主要信息*/onBeginContact (self: Collider2D, other: Collider2D, contact: IPhysics2DContact) {console.log('onBeginContact');let curTag = other.tag; // 获取当前标签}onEndContact (self: Collider2D, other: Collider2D, contact: IPhysics2DContact) {console.log('onEndContact');}onPreSolve (self: Collider2D, other: Collider2D, contact: IPhysics2DContact) {console.log('onPreSolve');}onPostSolve (self: Collider2D, other: Collider2D, contact: IPhysics2DContact | null) {console.log('onPostSolve');}
}
如果使用的是内置的物理引擎,它的事件回调仅支持BEGIN_CONTACT
和END_CONTACT
两种。
如果每个碰撞体都有各自的碰撞检测活动,就不要使用全局监听。比如说,飞机发射的子弹击中敌人,敌人死亡发生爆炸,如果是全局回调,就会导致所有的敌人都会死亡。
在cocosCreator 3.4.x中 物理系统是默认开启的,通过PhysicsSystem2D
设置即可
PhysicsSystem2D.instance.enable = true;
通过debugDrawFlags
可调试物理信息相关,主要的标记位有:
export enum EPhysics2DDrawFlags {None = 0,Shape = 1,Joint = 2,Aabb = 4,Pair = 8,CenterOfMass = 16,Particle = 32,Controller = 64,All = 63
}// 打开物理调试
PhysicsSystem2D.instance.debugDrawFlags = EPhysics2DDrawFlags.Aabb |EPhysics2DDrawFlags.Pair |EPhysics2DDrawFlags.CenterOfMass |EPhysics2DDrawFlags.Joint |EPhysics2DDrawFlags.Shape;// 关闭物理调试
PhysicsSystem2D.instance.debugDrawFlags = EPhysics2DDrawFlags.None;
其他
- 动态构建物体,碰撞器的大小受图形影响,不要使用UITransform下的
getBoundingBox
方法获取大小 。以创建合成水果的不同水果item为例,使用的是CircleCollider2D
圆形碰撞组件
private createFruitItem(index: number) {// 图片相关,从图集当中对应的图片const name = "fruit_" + index.toString();const fruitImg = this.node.getComponent(Sprite);const spriteFrame = fruitImg.spriteAtlas.getSpriteFrame(name);if (spriteFrame != null) {fruitImg.spriteFrame = spriteFrame;}// 碰撞组件相关let collider = this.node.getComponent(CircleCollider2D);collider.on(Contact2DType.BEGIN_CONTACT, this.beginContac, this);const transfrom = this.node.getComponent(UITransform);// 大小相关collider.radius = transfrom.width/2;collider.tag = index;collider.apply();
}
- 在碰撞回调中,如果需要对节点销毁,就稍微延迟下销毁,否则很容易导致错误:
private beginContac(self: Collider2D, other: Collider2D) {setTimeout(()=> {if(this.node && this.node.isValid) {this.node.destroy();}}, 50);
}
实例项目参考: CreatorGameProjects 下的Plane和ComposeFruits即可。
End