cocos2dx-3.0+的按键事件处理还是不尽如人意

3.0的事件分发机制比2.0时代有了大幅提升,但为何按键事件还是没改动?

具体地说:我基于3.0封装了自己的对话框系统(作用域跨scene),并注册了安卓的back键事件,并且保证它的优先级一定高于任何一个普通scene上的listener。

我认为:对话框的listener在处理完key事件后,该事件就不应该分发到scene上。对应到UI上的具体体现,就是对话框显示时,收到backkey事件,对话框dismiss,然后下层的scene应该直接得不到这个backkey事件才对,这样才是符合逻辑的。

但事实是,3.0中没有一个keyevent的消耗机制(显而易见,2.0时代当然也是没有的),在对话框处理完backkey事件后,我无法中断key event的继续向下分发。

我粗略看过EventDispatcher::dispatchEventToListeners的代码,除了改动源码外别无他法。

个人认为,这不得不算是3.0的一个遗憾之处。

好吧,自己动手丰衣足食,花了点时间给按键事件的回调加了是否已消耗的返回值,可以控制EventDispatcher是否继续向下分发了:2:
貌似涉及改动cocos 14个文件,略不和谐:3:

:3: 这个问题我们也碰到了,多层layer注册了安卓的back键事件,最上层响应时,事件会传递,所有layer都会响应。处理起来很麻烦。

握爪

我以前改2.0的时候,还被群里的人嘲讽说这样改后逻辑不通,呵呵

:6:怎么改啊,我也遇到这个情况了!!!

由于cocos2dx版本更新太快,所以我以前基于低版本3.0的改动一直就只存活在那一个游戏中……由于这种“非主流”的需求官方肯定是不会理睬的,所以每次有新版cocos2dx出来的时候,我都懒得再去把代码合进去了

过几天我翻一下旧代码,整理一下后贴出来

大致改动如下,我只说关键修改点

CCEventListener.h

将_onEvent成员变量,修改成:
//@return: void --> bool
//true – event consumed
//false – event not consumed
std::function<bool(Event*)> _onEvent; /// Event callback function

CCEventDispatcher.cpp

void EventDispatcher::dispatchEvent(Event* event)函数接近末尾的地方,
listener->_onEvent(event);
return event->isStopped();
这两行改成:
return listener->_onEvent(event) || event->isStopped();

CCEventListenerKeyboard.h

onKeyReleased改成:
@return: void --> bool
true表示已消耗,事件将中断分发
false表示未消耗,事件将继续分发
/
std::function<bool(EventKeyboard::KeyCode, Event
)> onKeyReleased;

CCEventListenerKeyboard.cpp

bool EventListenerKeyboard::init()函数,改成如下实现:
bool EventListenerKeyboard::init()
{
//modified by laoyur 2014-10-16
//void --> bool
auto listener = (Event* event)->bool{
auto keyboardEvent = static_cast<EventKeyboard*>(event);
if (keyboardEvent->_isPressed)
{
if (onKeyPressed != nullptr)
onKeyPressed(keyboardEvent->_keyCode, event);
}
else
{
if (onKeyReleased != nullptr)
return onKeyReleased(keyboardEvent->_keyCode, event);
}

    return false;
};

if (EventListener::init(Type::KEYBOARD, LISTENER_ID, listener))
{
    return true;
}

return false;

}

以上是关键修改点,改完后build一下,把其他相关需要修改的地方照葫芦画瓢改一下即可,本质上是原来是void无返回,现在改成了bool返回类型,返回true的话表示事件已消耗,不会继续分发;返回false则继续分发。