继续物理小游戏,我们先回顾一下CreatorPrimer仓库中提供的五个组件脚本:
通用物理组件
使用这5个组件脚本,可以构建非常有趣的物理小游戏,下面我们对这5个自定义组件做一个简单介绍:
- PhysicsManager: 物理引擎管理器,使用它无需编程即可开启关闭物理引擎,并提供刚体的着色调试开关。
物理引擎管理器
- PhysicsVelocity: 物理速度控制组件,提供了一个force函数方便使用cc.Button在编辑器中调用,为刚体施加外力。
- PhysicsColliderNotification: 物理碰撞通知组件,使用它可以让非物理组件或脚本能收到物理碰撞事件。
- ScoreNotificationHandle:得分通知处理组件,该组件监听PhysicsColliderNotification发出的事件通知,更新Label文本。
- ScoreVerifyNotificationHandle:带验证功能的得分通知处理组件。
我们今天的重点是PhysicsColliderNotification组件的实现。
1. 开启刚体碰撞监听
开启碰撞接触监听
PhysicsColliderNotification组件功能是监听刚体的碰撞事件,因此它需要依赖刚体组件(RigidBody),同时在运行时自动开启刚体的enabledContactListener属性。
代码语言:javascript复制cc.Class({ //依赖刚体组件
editor: CC_EDITOR && {
requireComponent: cc.RigidBody,
},
extends: cc.Component,
...
start () {
//获取刚体组件
let rigidBody = this.getComponent(cc.RigidBody);
//开启碰撞接触监听
rigidBody.enabledContactListener = true;
},
...
});
最初,有网友在使用Shawn提供的脚本发现时有不灵,发现是因为未开启刚体碰撞监听开关的原故,因此重构时增加了RigidBody的依赖,同时在组件start生命周期函数中开启刚体的enabledContactListener属性,增强使用体验,减少意外发生。
2. 碰撞事件监听
在篮框节点上开启了刚体的碰撞监听,就可以此节点的任意组件代码上编写碰撞监听处理函数了,我们看一下PhysicsColliderNotification碰撞处理函数的实现细节:
代码语言:javascript复制/**
* 物理碰撞通知组件,要以让非物理组件或脚本能收到物理碰撞事件
*/cc.Class({
...
extends: cc.Component,
properties: {
notificationName:'',
_p0: null,
_p1: null,
},
/**
* 只在两个碰撞体开始接触时被调用一次
*/
onBeginContact(contact, selfCollider, otherCollider) {
cc.log(otherCollider.node.name);
this._p0 = otherCollider.node.position;
},
/**
* 只在两个碰撞体结束接触时被调用一次
*/
onEndContact: function (contact, selfCollider, otherCollider) {
this._p1 = otherCollider.node.position;
if (this.notificationName) {
cc.director.emit(this.notificationName, contact, this._p0, this._p1);
}
},
});
不知道大家是否还记得,在篮框的碰撞组件中需要设置Sensor属性,它可以使用节点不产生物理碰撞效果,让其它动态刚体可以穿透它,但能监听物理碰撞事件,请看下图:
开启Sensor属性
开启了Sensor属性,通过引擎提供的onBeginContact、onEndContact两个事件监听函数获取篮球刚体的坐标点,识别篮球是从上向下投进的,还是从下向上进框的,从而实现正确记分。
组件中的_p0、_p1变量就是刚体碰撞时的开始点和结束点,在onEndContact事件中通过cc.director.emit将自定义事件、碰撞开始结束坐标点广播出去。
3. 自定义事件
为什么不直接在刚体节点上直接处理得分呢?要使用cc.director.emit中转一下呢?因为刚体碰撞事件,只能在刚体节点上才以监听到,得分的表现使用的是一个Label组件,如果将代码写在一些,那这个PhysicsColliderNotification组件做的事情就不只一件,而且太过去具体,而且设计多个对象,导致通用性会大大降低。
使用cc.director.emit('xxx')将广播一个事件出去,在任意脚本中使用cc.director.on('xxx')接收事件,不论是更新得分,还是处理游戏的流程、特效等等,会更加的灵活可变,可以让策划或设计师发挥出更多的创作空间。
cc.Director是继承成自cc.EventTarget,cc.director是一个全局变量,因此使用cc.director.emit、cc.director.on实现事件的订阅、发布非常简单,是实现组件间的通信的一种非常方便的方案。
很多人都使用过cc.Node.emit、cc.Node.on来发送和监听事件,唯一不方便的就是你需要先获取发送事件的节点对象。相信还有人怀念Cocos2d-x中的CCNotificationCenter,完全可以使用cc.EventTarget实例化一个全局的EventTarget对象来模拟,实现相同的效果。
通过事件可以方便解耦对象之间的依赖,用一个通俗点的说法就是:“你不要打电话给我,我会打电话给你!”。打电话首先需要电话号码就是事件名称,说这句话的人就是EventTarget对象,听话的人就是递交电话号码(事件名)、接听电话的程序同学。
4. 小结
本篇介绍了PhysicsColliderNotification组件的实现,全是代码和逻辑,不能照顾到非程序员同学,还请包涵。
监听物理碰撞一定要开启刚体的enabledContactListener属性,在onBeginContact、onEndContact事件中获取刚体的位置以识别刚体的运行方向。同时使用cc.director.emit将事件、坐标点广播出去,在关心的地方做对应的逻辑处理。
目前源码已经合并到CreatorPrimer仓库主干,欢迎把玩,提出你的建议! 源码地址:https://github.com/ShawnZhang2015/CreatorPrimer