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

很久之前写过一篇关于 VRTK 多 Canvas 画布重叠手柄射线穿透问题的文章,当时才疏学浅,没有找到解决方案。最近虽然不干 VR 了,但偶然研究了下,找到了比较完美的解决方案。

2023 年补充

你的需求真的非 VRTK 不可么?

都 2023 年了,别再抱着 VRTK 不放了,你现在既有现成的 XRI 套件(Unity 官方维护),也有第三方的 MRTK、Oculus Interaction SDK 等套件使用。VRTK 开发时还是 VR 初级阶段,开发者很多边界条件都没考虑。

我就要用 VRTK

如果非得用 VRTK,建议复写自己的 InputModule 及 Raycaster,从源头上把 UI 排序整对了。针对一些运行时创建遮罩层的 UGUI 组件(eg:Dropdown),建议看看 UGUI C# 源码,看看人家到底干了啥,再参照 Unity 官方的 Raycaster 与 InputModule,想想自己复写时怎么处理排序。

你到底行不行

本文内容只是为了解决多画布穿透问题,不涉及其他问题(例如某些组件工作不正常)。

当然,你也可以找我定制一套完全修复版的 VRTK UI 交互脚本,让你的各种 UI 组件都可以正常工作,但那不是本文的内容。

上文已经提到你可以自己去复写 InputModule 及 Raycaster,这没什么难的。我给你提示了方向,如果你去研究下,会发现自己实现一套 UI 交互只需要改动一点点代码而已,根本不复杂。

懒得写或者不会写,也可以找我定制一套。

起因

起因请看这篇文章: 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。

  1. [代码]【GitHub】finartist issues/1846
梓喵出没博客(azimiao.com)版权所有,转载请注明链接:https://www.azimiao.com/7509.html
欢迎加入梓喵出没博客交流群:313732000

发表评论

*

*