如何优雅的做事件穿透

  • 先说使用场景
    引擎版本3.5.2。一个带有可拖动地图的游戏,ui和地图分属不同的摄像头(地图摄像机priority更低),有时候会在ui层弹出一些小提示,希望在点击某些按钮或者拖动地图的时候隐藏这些小提示

  • 需求就是:

    想在触摸事件(包含鼠标点击和手指触摸)发生了之后进行一些处理,而不影响原本游戏的任何事件传递逻辑



官方有两种处理方式
一种是用useCapture参数让Canvas注册触摸事件到捕获阶段
另一种是event.preventSwallow = true

这两种方式都会导致priority更低的地图摄像机无法接收到触摸事件

我现在临时的处理办法是在priority更高的ui摄像机下创建一个最高层级的节点node,然后

node.eventProcessor._handleEventTouch = ()=>{/* do something*/return false}
node.on(Node.EventType.TOUCH_START,()=>{})

这样就能让这个节点忽略任何触摸事件,还能知道触摸事件发生了
但是eventProcessor被标记从3.4.0废弃了

那官方有没有推荐一种更优雅的实现方式呢

1赞

有没有可能?:让这些小提示tips独立封装成一个个tip对象,对象的类内部各自定义一个EventTarget对象对外可访问,然后在tip类内部监听自定义事件,而在需要的外部调用引用tip的EventTarget对象进行相应的emit。

先感谢大佬出谋划策

我目前是用上面的临时方案感知到触摸后game.emit(‘click_top_mask’),然后各个tip自己监听click_top_mask消息

那就还是需要有一个格式的地方,优雅的game.emit(‘click_top_mask’)

如果在代码的各个角落手动给tip发消息,那对其他模块的入侵太大,游戏改来改去的时候容易出bug
image

这些小提示对象是在同一个层级或节点上统一管理吗?

不是在同一层级的,是散落在游戏各个模块的
也不太好做到一起,因为和各自所属的模块联系很紧密

那就试试用一个带有EventTarget类对象的管理类统一管理这些tip吧,各tip按需要引入这个管理类的EventTarget对象,把事件注册在这个EventTarget上,各tip按需emit,必要时把自身引用作为参数传递出去

这些其实有做,我上面用game.on和game.emit就是统一管理的意思
问题的关键是,我想知道屏幕被触摸了,还不想影响原来游戏的点击逻辑
比如现在游戏弹出一个tip,我想在玩家手指接触屏幕的时候这个tip就消失,但是不影响玩家这次点击所触发的按钮逻辑

看不明白啊,这需求和事件穿透有什么关系?不是应该把tips统一管理,然后在原本的事件处理中加上隐藏或者销毁这些tips的代码么。这些tips用不着注册和监听事件吧

按照玩游戏的经验体会,感觉还是让玩家多点一次屏幕吧。第一次是为了隐藏tip,第二次才是点按钮。那就还是最好把tip集中在一个层级节点统一管理,让这个节点的触摸带有"触摸拦截"。要是实在要一次性达到两步,那就只能把隐藏tip的操作放在点按钮后的事件处理中了

原本的事件处理地方太多了,都加上隐藏tip的代码会难以维护,以后代码加加减减的时候容易出bug

你在ui在的Canvas挂的脚本上注册input.on的触摸事件,然后在其回调函数里event.preventSwallow = true;触摸事件是会穿透到下面地图的Canvas的。文档写了这么做会降低效率,真不是啥好选择,虽然能满足你的要求

地图和ui是同一个canvas,只不过不是一个摄像机,我测试是无法穿透到地图摄像机,会被拦截