存在重叠的WordSpace Canvas时,UIPointer射线会穿透Canvas,导致触发错误。
问题探究
解决方法直接跳到最后“解决方法”部分。
测试场景如下图,有两个重叠的Canvas,分别叫做Canvas3与Canvas4,它们下面各有一个按钮。
Canvas上挂一脚本如下:
void Start(){
if(this.GetComponentInChildren<Button>()){
this.GetComponentInChildren<Button>().onClick.AddListener(this.onBtnClick);
}
}
void onBtnClick(){
Debug.Log("it's" + this.name);
}
运行后射线指向Canvas3,射线仿佛穿透了Canvas3,打印出的信息是Canvas4。
VR与UGUI交互肯定通过EventSystem进行,通过VRTK_UIPointer脚本向底层找,找到了检测碰撞的代码。
代码把射线出发点、方向传递给EventSystem
,通过EventSystem.RaycastAll
获取射线指到的UI:
//VRTK_VRInputModule.cs
protected virtual List<RaycastResult> CheckRaycasts(VRTK_UIPointer pointer)
{
RaycastResult raycastResult = new RaycastResult();
raycastResult.worldPosition = pointer.GetOriginPosition();
raycastResult.worldNormal = pointer.GetOriginForward();
pointer.pointerEventData.pointerCurrentRaycast = raycastResult;
List<RaycastResult> raycasts = new List<RaycastResult>();
eventSystem.RaycastAll(pointer.pointerEventData, raycasts);
return raycasts;
}
既然如此,打印下碰撞结果:
Debug.Log("碰撞到UI了!,共碰到了" + raycasts.Count);
for (int i = 0; i < raycasts.Count; i++)
{
Debug.Log(raycasts[i].gameObject.name,raycasts[i].gameObject);
}
发现返回结果只有Canvas4下的两个组件(截图省略了VRTK自动创建的drag组件)。
关于EventSystem.RaycastAll
,官方文档就一句话:使用所有设置过的BaseRaycaster
进行碰撞检测。
翻了翻Unity的开源代码,没有找到EventSystem.RaycastAll
的具体实现。后来又试着修改了参数属性值,调整了Layer等,也没有作用。
总结:EventSystem.RaycastAll
并没有直接返回All碰撞,(也许)按照内部的排序逻辑,把它认为正确的物体吐了出来。
解决方法
-
概率性解决
将这些重叠Canvas上挂载的Graphic Raycaster
组件的Blocking Objects
修改为All
这是概率性解决穿透的方法,有的面板管用,有的面板不好用。
改为All也无效时,运行后将后面的面板关闭再打开,基本可以解决问题,原因未知,也许和内部排序逻辑有关。 -
斩草除根
在设计阶段避免重叠Canvas同时出现的情况。如果必须出现,那就自求多福吧。 -
坐以待毙
等待着Unity出来给个解释。