VRTK解决多Canvas射线穿透问题(WorldSpace)

很久之前写过一篇关于 VRTK 多 Canvas 画布重叠手柄射线穿透问题的文章,当时才疏学浅,没有找到解决方案。最近虽然不干 VR 了,但偶然研究了下,找到了比较完美的解决方案。
起因
起因请看这篇文章: VRTK重叠Canvas上UIPointer射线穿透的问题(Unity VR)
解决方法
以下 1、2 两点必须同时修改,非二选一。
步骤1:修改 VRTK_UIGraphicRaycaster
在VRTK_UIGraphicRaycaster
脚本第 59 行,找到如下一行:
//梓喵出没
eventData.pointerCurrentRaycast = nearestRaycast.Value;
将本行注释掉:
//梓喵出没
//eventData.pointerCurrentRaycast = nearestRaycast.Value;
注释的目的是让射线投射从本地零点开始,这一行就是造成“近在咫尺的 UI 选不到,射线穿透到背后很远的 UI 上”的原因。
关于Raycast
方法如何被调用的问题,有兴趣的可以翻 UGUI 关于 GraphicRaycaster 与 EventSystem 的源码。
步骤2:修改 VRTK_VRInputModule 自定义排序
修改 1 中内容后,使用EventSystem.RaycastAll
即可以获取到射线路径上所有的 UI 元素,如果不修改 1 中内容,则会导致获取不全。
由于 EventSystem.RaycastAll 返回结果的排序与我们预期有差别,因此需要手动排序。在VRTK_VRInputModule
脚本的第 56 行,即 eventSystem.RaycastAll
的后面,追加如下内容:
raycasts.Sort((res1, res2) =>{
if (Mathf.Abs(res1.distance - res2.distance) < 0.001)
{
return res2.depth.CompareTo(res1.depth);
}
return res1.distance.CompareTo(res2.distance);
});
if (raycasts.Count > 0)
{
pointer.pointerEventData.pointerCurrentRaycast = raycasts[0];
}
距离差小于 0.001 后按 depth 排序的目的是为了避免距离计算中的精度问题。当 A B两个元素重叠时,有可能上一帧计算的距离值 A 比 B 小,在这一帧距离值就变成了 B 比 A 小(float),如果顺序乱跳,后面强行设置pointerCurrentRaycast
时就可能出现悬浮点在 A B 间不停切换的现象。
引用资料
该修复方法来自于 GitHub finartist。
- [代码]【GitHub】finartist issues/1846