World Canvas Raycasting not blocked by other game objects?

I am using several world canvas instances in my scene
and it seems like the raycast order is not working as it should.

An easy setup to show the problem can look like the following:

Place a UI canvas in the scene,

change “Render Mode” to “World Space”,

set position and scale accordingly so that it has the size of other game objects like a cube.

Add a simple button to the canvas and

place a cube infront and behind the canvas.

Now change the “Graphic Raycaster” component settings to:

  • Ignore Reversed Graphics: false

  • Blocking Objects: “All”

  • Blocking Mask: “Everything”

Here an image of the setup:
Setup

And here another image showing the problem I’m facing:
Problem

As you can see, even tho the ray should be blocked by the cube,

the event is still forwarded to the canvas/button element behind.

When you try the same from “behind” the canvas, it does not show this behaviour.

In such a case, the cube does (as desired) block the ray.

So is there any way to fix this behaviour so that objects infront always block the ray?

It is required for my application to work like this because otherwise you easily interact with a canvas that is somewhere behind and you actually should not.

Thanks for any helpful ideas.

A fix that worked for me:

This is only useful if you have access to the code that generates the ray itself.

I mainly use it for a VR pointer.

Instead of using the FindFirstRaycast() method, I wrote and used the following code:

bool minSet = false;
float minDistance = 0;
RaycastResult result = new RaycastResult();
foreach (RaycastResult r in m_RaycastResultCache) {
    if (r.gameObject == null) { continue; }

    // rounding is applied because otherwise the min object switches many times
    // (e.g. if a button with a text is hovered over) - which can lead to weird behaviour.
    // rounding at 2 decimals after comma is enough precision for now
    float dist = Mathf.Round(r.distance * 100) / 100f;
    if (!minSet || dist < minDistance) {
        minSet = true;
        minDistance = dist;
        result = r;
    }
}

The hit precision is pretty inaccurate after the first two decimals after comma.

That’s why I simply round the distance accordingly.

This should mainly be a problem if you have direct feedback (e.g. like a vibration of the controller).

Maybe tell me if this was useful for you

or if you have ideas for improvements.

Update 1:

I also tested this in a “fresh” scene - without any additional scripts attached.

This only seems to happen in one direction (from negative to positive Z-axis)

and if a collider is behind the Canvas (in my case there is the ground Plane).

Removing/Disabling the ground plane makes the bug disappear but is no solution.

Any ideas?

Update 2:

Okay, another idea why this could happen:

The raycasting used by the camera obviously seems to use the event system.

There is the method [EventSystem.RaycastAll][1] which I think the camera uses.

One of the [BaseRaycasters][2] it uses is the [PhysicsRaycaster][3] and looking up the [Physics.RaycastAll][4] method for it, it states that:

Casts a ray through the Scene and returns all hits. Note that order is not guaranteed.

So thats probably the reason why it only works in one direction.

Used to retrieve the result, is probably the method [FindFirstRaycast][5].

There is no information on how this method is implemented.

So if you have access to the source code, please let me know.

I assume it just goes through the list of raycast results and checks on after another until it finds the first valid raycast result.

In this case (because the order is not guaranteed) it doesn’t have to be the first that was hit, or the first thats the closest to the origin.

So I probably have to replace this check my own, which I hope will work and don’t affect performance too much.

Maybe one of you can help me out with something I don’t know that already does this?

Update 3:

Okay, my guess was correct that the “FindFirstRaycast” just takes the first result of the list that is not null.

I wonder why this method then even exists then…

Here is the part of the [source code][6]:

protected static RaycastResult FindFirstRaycast(List<RaycastResult> candidates) {
      for (var i = 0; i < candidates.Count; ++i)
      {
            if (candidates*.gameObject == null)*

continue;

return candidates*;*
}
return new RaycastResult();
}
So the thing I consider to do now is replacing the use of this method in my application
by something like the following:
“Find the GameObject with the smallest distance (hit distance) thats not null and return it.”
I know that this check takes a bit more time but I guess it wont be as much as long as there are not always 10000+ entries in the list.
I gonna post an update soon on how it worked out and if it solved my problem.
_[1]: Redirect to... title of new-page
_
[2]: Redirect to... title of new-page
_[3]: Redirect to... title of new-page
_
[4]: https://docs.unity3d.com/ScriptReference/Physics.RaycastAll.html*_
_[5]: Redirect to... title of new-page
_
[6]: https://github.com/tenpn/unity3d-ui/blob/master/UnityEngine.UI/EventSystem/InputModules/BaseInputModule.cs*_

Thanks for this @S1R0 , I was puzzled with the same setup you have there and a VR laser pointer we wrote haha