完整课程入口:996 全套课程体系 | Lua 学习路径 | 幂尔框架 mirs.cn
两个经典 UI 事故:点按钮把按钮下面一层的"关闭区域"也触发了(点击穿透);全屏遮罩挡不住后面的界面(吞事件失败)。两者都源于对 Cocos 触摸事件分发顺序与吞噬机制的理解不足。本文给出一套排查顺序与标准写法。
Cocos2d-x 3.x 的事件监听按注册时的优先级分发:addEventListenerWithSceneGraphPriority 按节点在场景树中的绘制顺序(越靠上层节点越先收到);固定优先级模式(数值越小越先)。996 客户端的 GUI 封装统一走场景树优先级,所以规律是:上层界面先拿到触摸,处理完可选择吞掉。
local listener = cc.EventListenerTouchOneByOne:create()
listener:setSwallowTouches(true) -- 命中后吞掉,不再下传
listener:registerScriptHandler(function(touch, event)
return true -- onTouchBegan 返回 true 才会吞
end, cc.Handler.EVENT_TOUCH_BEGAN)
cc.Director:getInstance():getEventDispatcher()
:addEventListenerWithSceneGraphPriority(listener, maskNode)
两个条件缺一不可:setSwallowTouches(true) 加上 onTouchBegan 返回 true。半透明遮罩挡不住点击,九成是Began 返回了 false 或压根没注册监听。
点击穿透排查按固定顺序:打印事件分发日志(重写 Dispatcher 或挂调试监听),确认触摸事件到达了哪些节点;从最上层节点往下检查每个 onTouchBegan 的返回值;检查按钮的 hitTest 区域是否与预期一致(子控件超出父节点边界时,命中区域计算会出现偏差)。反向问题——"整个界面点不动"——通常是某层透明节点开了 swallow 且 Began 恒返回 true,覆盖了全屏。
节点 setVisible(false) 后监听仍在分发链上(部分版本行为),隐藏遮罩时同步 getEventDispatcher():removeEventListener(listener) 或复用控件时确保监听状态与可见性一致。规范做法:界面基类统一管理监听的注册与移除(onEnter 注册、onExit 移除),子界面只写回调——事件与节点生命周期绑定后,穿透与泄漏两类问题都会显著减少。