cocos creator 事件
cocos creator 事件
在做一個消除類遊戲時,需要對點選的方塊做出響應。程式碼很簡單,可背後的原理還多著呢。
1. 普通節點註冊click事件
在cc中如果需要相應click事件,需要為該節點新增一個Button元件。或使用類似效果的事件比如
-
cc.Node.EventType.MOUSE_DOWN
-
cc.Node.EventType.TOUCH_END
//author herbert qq:464884492 //註冊按鈕click事件 btn.node.on("click", event=>{cc.log("button click")}); //註冊MOUSE_DOWN btn.node.on(cc.Node.EventType.MOUSE_DOWN,event=>{cc.log("button MOUSE_DOWN")}); //註冊TOUCH_END btn.node.on(cc.Node.EventType.TOUCH_END,event=>{cc.log("button TOUCH_END")})
2. 應該減少事件註冊量
是否沒有問題了?在寫juqery時,有事件委託(delegate)的概念。啥意思呢,就是在節點的父級註冊事件,來響應子節點的事件源。為啥可以實現,主要歸功於js事件的兩大機制
-
事件冒泡,事件響應從子節點往上冒泡到頂層節點
-
事件捕獲,事件響應衝頂層節點依次傳遞到最末級節點
所以問題來了,消除類遊戲都是通過預製資源生成不同樣式的方塊。若在每一方塊上都註冊事件,勢必導致記憶體上漲(雖然現在記憶體很大了)。看看cc文件,事件機制完全是一樣的(最終都是JS),然而我想在我的Canvas上註冊一個click事件,問題出現了。
3.問題來了
問題就是我在Canvas上註冊了click事件,點選button時,Canvas 上居然沒有收到我的click事件。由此我檢視cc原始碼,寫了一堆測試程式碼,最總得出以下結
-
click事件確實Button元件特殊存
-
click事件不會向上或向下傳遞
-
node.emit觸發事件不會向上或向下傳遞
-
node.dispatchEvent支援事件向上或向下傳遞
-
使用node.dispatchEvent引數必須是 cc.Event.EventCustom物件
4.click事件特殊在哪裡
cc.Button 元件中的click事件,其實是cc的自定義事件,原始碼為證
//author:herbert wx:464884492 ... this.node.on(cc.Node.EventType.TOUCH_END, this._onTouchEnded, this); ... _onTouchEnded (event) { if (!this.interactable || !this.enabledInHierarchy) return; if (this._pressed) { cc.Component.EventHandler.emitEvents(this.clickEvents, event); this.node.emit('click', this);//觸發事件 } this._pressed = false; this._updateState(); event.stopPropagation(); //停止冒泡 }, ...
所以,之所Button能響應click事件,是因為元件註冊了TOUCH_END事件,並在響應該事件函式中發射一個click事件。
5. javascript 自定義事件
參考mdn文件,js自定事件方式如下
// author:herbert wx:464884492 <script text="javascript"> let cusEvent = new Event("custom", { bubbles: true //允許冒泡 }); document.body.addEventListener("custom", e => { console.log("自定義事件"); console.log(" Body event by custom"); }); let btn = document.querySelector("#btn"); btn.addEventListener("custom", e => { console.log("自定義事件"); console.log("Button event by custom"); }) btn.dispatchEvent(cusEvent); </script>
5.瞭解下cc.node.emit
cc.node.emit 最終呼叫的是CallbacksInvoker.prototype.invoke 方法,從原始碼來看,是從對應的快取物件中找到註冊的回撥方法,依次呼叫回撥函式。
//author herbert wx:464884492 CallbacksInvoker.prototype.invoke = function (key, p1, p2, p3, p4, p5) { var list = this._callbackTable[key]; if (list) { var rootInvoker = !list.isInvoking; list.isInvoking = true; var callbacks = list.callbacks; var targets = list.targets; for (var i = 0, len = callbacks.length; i < len; ++i) { var callbmhtack = callbacks[i]; if (callback) { var target = targets[i]; if (target) { callback.call(target, p1, p2, p3, p4, p5); } else { callback(p1, p2, p3, p4, p5); } } } if (rootInvoker) { list.isInvoking = false; if (list.containCanceled) { list.purgeCanceled(); } }}};
所以click自然不會往上或往下傳遞。
6.dispatchEvent,事件冒泡
dispatchEvent 利用自定義事件的 bubbles 屬性,實現冒泡。至於為啥使用 btn.node.dispatchEvent(new cc.Event.EventMouse(cc.Node.EventType.MOUSE_DOWN, true))
沒有觸發事件是因為cc在底層,將事件型別統一改成了 cc.Event.MOUSE
,原始碼為證
author:herbert wx:464884492 ... var EventMouse = function (eventType, bubbles) { cc.Event.call(this, cc.Event.MOUSE, bubbles); ... };
場景
執行效果
7.總結
做開發,不管是開發遊戲還是其他應用程式。思路基本是一樣的。在簡單的事,多想想,再發散一下,你會收穫更多。
需要進cocos遊戲開發群的朋友,請新增我微信回覆cocos
歡迎感興趣的朋友關注我的訂閱號“小院不小”,或點選下方二維碼關注。我將多年開發中遇到的難點,以及一些有意思的功能,體會都會一一發布到我的訂閱號中。如需本文demo請在訂閱號中回覆ccevent