繼續物理小遊戲,我們先回顧一下CreatorPrimer倉庫中提供的五個組件腳本:
使用這5個組件腳本,可以構建非常有趣的物理小遊戲,下面我們對這5個自定義組件做一個簡單介紹:
- PhysicsManager: 物理引擎管理器,使用它無需編程即可開啓\關閉物理引擎,並提供剛體的着色調試開關。
2. PhysicsVelocity: 物理速度控制組件,提供了一個force函數方便使用cc.Button在編輯器中調用,爲剛體施加外力。
3. PhysicsColliderNotification: 物理碰撞通知組件,使用它可以讓非物理組件或腳本能收到物理碰撞事件。
4. ScoreNotificationHandle:得分通知處理組件,該組件監聽PhysicsColliderNotification發出的事件通知,更新Label文本。
5. ScoreVerifyNotificationHandle:帶驗證功能的得分通知處理組件。
我們今天的重點是PhysicsColliderNotification組件的實現。
1. 開啓剛體碰撞監聽
PhysicsColliderNotification組件功能是監聽剛體的碰撞事件,因此它需要依賴剛體組件(RigidBody),同時在運行時自動開啓剛體的enabledContactListener屬性。
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碰撞處理函數的實現細節:
/**
* 物理碰撞通知組件,要以讓非物理組件或腳本能收到物理碰撞事件
*/
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屬性,通過引擎提供的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
如果覺得公衆號上的文章對您或您身邊的朋友有幫助,請分享給他,願我們一起成長!