这两天在使用cocos2d-js做一个拔萝卜游戏,研究了一番cocos2d的事件分发机制,总结分享一下。
事件
Cocos2d-JS v3.x中事件分发机制进行了重写,事件可以与任意对象绑定,而不是只有Layer才能获取。对象创建自己的事件监听器,然后加入到全局的事件管理器统一管理。
事件监听器有以下几种:
事件分发
在了解事件分发机制之前,我们首先要明确什么是事件分发。
对于事件分发,cocos2d官方定义为:当事件发生(例如,用户触摸屏幕,或者敲键盘),EventDispatcher 会发布(Event objects)事件对象到合适的EventListeners,并调用你的回调。各个Event object包含事件的信息(比如,触摸点所在的坐标)。
我的理解是对于一个事件(比如触摸事件、键盘相应事件等)可以与任意对象绑定,那么当这个事件被用户触发时,此时应该执行哪一个对象的回调函数(比如此时我们在好几个sprite上同时绑定了touch事件,用户此时点击屏幕,用户此时想点击的到底是哪一个sprite),我们就需要作出判断。EventDispatcher做的就是这个事情。
以touch事件(触摸事件)为例,判断用户点击的是哪一个sprite的方法很简单,其实就是在onTouchBegan方法中获取点击点的坐标pos,然后通过cc.rectContainsPoint(target.getBoundingBox(),pos)判断点击的点是否在SushiSprite上。不过此时又会出现另外一个问题,就是如果两个sprite有相互重叠的部分,而此时用户点击的恰恰是重叠部分,那么怎么判断到底点击的是哪一个sprite呢?这里是通过priority(分为两种:SceneGraPHPriority和FixedPriority)来解决的,优先级高的sprite优先执行它所对应的事件监听器的回调函数。这样就形成了一个按照优先级高低排列的sprite队列等待依次执行用户触发的事件,而这中间则通过swallowTouches(吞没事件)属性来控制是否继续向优先级低的sprite传递事件。这就形成了一个完整的事件分发机制。
tip:
SceneGraPHPriority(显示优先级):根据屏幕显示的“遮盖”实际情况,进行有序的函数回调。zOrder越大,优先级越大。
FixedPriority(固定优先级):依据手动设定的 Priority 值来决定事件相应的优先级,值越小优先级越高
总结一下:
通过点击位置进行点击范围判断,来确定执行哪一个sprite的事件监听器
如果该位置存在重叠的sprite绑定了相同的事件,则依据优先级(SceneGraPHPriority显示优先级或FixedPriority固定优先级)来顺序执行函数回调
通过设置swallowTouches属性为true,并在onTouchBegan中返回true或者false来决定是否阻止事件的顺序传递。如果onTouchBegan返回true,且swallowTouches为true,则事件被吞没,事件的顺序传递则被阻止。
实例
新建一个sprite,并为其添加一个touch事件:
var SushiSprite = cc.Sprite.extend({ onEnter:function () { cc.log("onEnter"); this._super(); },onExit:function () { cc.log("onExit"); } }); addTouchEventListenser:function(){ this.touchListener = cc.EventListener.create({ event: cc.EventListener.TOUCH_ONE_BY_ONE,// When "swallow touches" is true,then returning 'true' from the onTouchBegan method will "swallow" the touch event,preventing other listeners from using it. swallowTouches: true,//onTouchBegan event callback function onTouchBegan: function (touch,event) { var pos = touch.getLocation(); var target = event.getCurrentTarget(); if ( cc.rectContainsPoint(target.getBoundingBox(),pos)) { cc.log("touched") return true; } return false; } cc.eventManager.addListener(this.touchListener,this); });
上面的代码:
首先通过使用cc.EventListener.create创建了一个Touch事件监听器touchListener
然后,通过cc.eventManager.addListener注册监听器到事件管理器。cc.EventListener.create扩展出一个用户监听器。
event属性,定义这个监听器监听的类型。
swallowTouches属性设置是否吃掉事件,事件被吃掉后不会递给下一层监听器。
onTouchBegan方法处理触摸点击按下事件,我们在这里可以获取到触摸点的坐标pos。event.getCurrentTarget()获取当前事件的接受者,并判断当前的是否点击到了SushiSprite。
在touch事件中,我们还可以添加onTouchMoved/onTouchEnded方法监听touch移动和结束的回调。如果onTouchBegan返回false后onTouchMoved/onTouchEnded不会执行。
tip: 在onTouchBegan方法中获取点击点的坐标pos,pos)判断点击的点是否在SushiSprite上。
博客文章地址:http://joebon.cc/cocos2d-event-dispatcher