【Bug反馈】getUIDelta原生环境返回值不准,已上传可复现Demo

【复现Demo】https://gitee.com/szrpf/Dragger/repository/archive/master.zip

【Creator版本】3.8.2

【Bug描述】getUIDelta接口在原生环境下,返回的值不准。

【复现流程】启动原生模拟器,按住小猪头像快速画圈,小猪头像触点会移位。

按住小狗头像同样操作则正常。

初步判断是getUIDelta在原生环境下有问题。

已提交可复现Demo,麻烦引擎组看一下

@dumganhar 麻烦看看?

我稍后看下。

建个 issue 跟进:

https://github.com/cocos/cocos-engine/issues/17866

这个问题是原生平台的事件派发与 web、小游戏平台有区别,原生平台会缓存事件到下一帧开头进行派发。而如果缓存的事件中有相同的 touch id,那么导致缓存的 Touch 列表中都是同一个 Touch 对象。导致用户上层获取到多次的 ui delta 是不对的,进而导致这个偏移计算有问题。

如果你想让原生行为与 web 一致,可以先改下 input.ts 中的

/**
     * @en Dispatch input event immediately.
     * The input events are collocted to be dispatched in each main loop by default.
     * If you need to recieve the input event immediately, please set this to true.
     * NOTE: if set this to true, the input events are dispatched between each tick, the input event can't be optimized by engine.
     *
     * @zh 立即派发输入事件。
     * 输入事件默认会被收集到每一帧主循环里派发,如果你需要立即接收到输入事件,请把该属性设为 true。
     * 注意:如果设置为 true,则输入事件可能会在帧间触发,这样的输入事件是没办法被引擎优化的。
     */
    private _dispatchImmediately = !NATIVE; // 这里改为 true

好的,大佬辛苦了,希望能更新到下个版本中

会在 3.8.5 的第二个社区版中修复,修复 PR:

好的大佬,赞

这个 bug 好有年代感啊,应该是 3.5 左右的版本引入的。
Bug 的原因是,原生平台上派发事件是做了缓存处理的,缓存到下一帧开头通过 Input._frameDispatchEvents 进行派发。

touch 事件有个机制:touch ID 到 Touch 对象的映射关系,可以看 TouchManager.ts 里面的代码。
如果一帧以内触发了同一个 touch ID 多次的 move 事件(这种在快速移动鼠标的情况下容易出现),
第二次的 move 事件在派发的时候会去 TouchManager 中查找 touchId,如果找到直接返回找到的 Touch 对象,进行填充。问题点是第二次找到的 Touch 对象其实跟第一次的 Touch 对象是同一个实例,第二次 Touch 的数据填充的时候,也恶意地把第一次的 Touch 内容覆盖了。因此导致相同的 UIDelta 被调用多次。这个 bug 还是挺严重的问题。平时碰不到,估计监听 move 的时候,收到重复的 touch 对象,没有产生副作用,而 getUIDelta 这个函数则会暴露出此 bug。

解决方案

在 c++ 传递给 JS 的源头做缓存机制,缓存/复制原始的 touch 对象,在 Input. _frameDispatchEvents 做一次系统事件派发的模拟,这样也不影响 TouchID 复用的机制。

太到位了,期待3.8.5版本