unity – Model missed by both EventSystem.current.RaycastAll and IPointerClickHandler when scaling time

Update 3: I’ve found a workaround, but why the problem only manifests when scaling time still mystifies me.

Original post:

I have a situation where a model is ignored in favour of one behind it. The rear model is returned by EventSystem.current.RaycastAll, and the front model’s IPointerClickHandler callback isn’t triggered if the rear model is present. The rear model does not implement IPointerClickHandler (or pointer up/down handlers, etc). Both these things work on the front model if the rear model isn’t there.

Debugging shows that the Raycast is only returning one result. By comparing behaviour when the rear model is and isn’t present, I can verify that the distance value in the RaycastResult is greater when hitting the rear model than when hitting the front model.

I can “fix” the problem by using the Inspector to disable and re-enable the front model.

Here’s the problem and the “fix” in action:

Tooltips fixed by disabling and re-enabling an object

Higher quality version here.

EventSystem.current.RaycastAll is used to power the tooltips, and IPointerClickHandler is (unsurprisingly) used to get the cube model to react to clicks.

Update: After more investigation, I’ve made limited progress:

  • I’ve fixed the problem when running the game at normal speed by giving both objects a kinematic Rigidbody.
  • However, the problem still occurs when I speed up time. I set Time.timeScale, and increase Time.fixedDeltaTime by the same scale factor as recommended here.
  • I can eliminate the problem by not changing Time.fixedDeltaTime, however I’m reluctant to do so since this has a big impact on framerates at higher speeds (12x being the highest available).
  • If time is paused (i.e. Time.timeScale == 0) then the problem never fixes itself – time needs to be moving forward for the raycast to start hitting the cube. Presumably this is because physics calculations are suspended when time isn’t progressing.

enter image description here

Again, higher quality version here

Unity version: 2020.3.11f1

Update 2: As requested, here’s the raycast and time scaling code:

public class TooltipDisplay : MonoBehaviour {
    public void FixedUpdate() { // I've tried it in Update too, no change
        PointerEventData pointerData = new PointerEventData(EventSystem.current);
        pointerData.pointerId = -1;
        pointerData.position = Mouse.current.position.ReadValue();
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(pointerData, results);
        Tooltip highest = null;
        foreach (var result in results) {
            // There's only one result here, and it's often the wrong one :(
            Tooltip t = GetTooltip(result.gameObject);
            if (t == null) continue;
            if (highest == null || t.priority > highest.priority) {
                highest = t;
            }
        }
        // If we found a tooltip, display it.
        // ...
        // ...
    }
}


public class SpeedLimiter {
    
    private int multiplier;
    private static readonly float DEFAULT_FIXED_DELTA_TIME = Time.fixedDeltaTime;
    public const float SECONDS_PER_COMMAND = 2.0f;
    public float timeAccumulated { get; private set; }
    
    // Called from a Unity Button press (bottom-right of video) or from an Input system keypress handler.
    public void SetMultiplier(int speed) {
        multiplier = speed;
        Time.timeScale = multiplier;
        Time.fixedDeltaTime = DEFAULT_FIXED_DELTA_TIME * multiplier;
    }

    public bool Advance() { // Called in Update
        bool looped = false;
        timeAccumulated += Time.deltaTime;
        while (timeAccumulated >= SECONDS_PER_COMMAND) {
           timeAccumulated -= SECONDS_PER_COMMAND;
           looped = true;
        }
        return looped;
    }
}


public class UHexahedron : MonoBehaviour {
    Vector3 fromPosition;
    Vector2 toPosition;
    float translationStart;
    float translationDuration;
    bool translating;

    public void FixedUpdate() {
        if (translating) {
            float currentTime = SpeedLimited.timeAccumulated;
            if (currentTime < translationStart) return;
            if (currentTime >= translationStart + translationDuration) {
                Translate(1);
            }
            else {
                float lerp = (currentTime - translationStart) / translationDuration;
                Translate(lerp);
            }
        }
    }
    
    private void Translate(float lerp) {
        if (lerp >= 1) {
            transform.position = toPosition; // Tried rigidbody.position, no change
            translating = false;
            return;
        }
        transform.position = Vector3.lerp(fromPosition, toPosition, lerp); // Again, tried rigidbody.position, no change.
    }
}

Update 3:

It looks like part of the problem was that the PhysicsRaycaster on the main Camera had its maxRayIntersections set to 1. By increasing this I’ve managed to get the tooltips and click handling working, but it’s still unclear why this problem was only occurring when scaling fixedDeltaTime.

I started out by implementing a manual physics raycast alongside the EventSystem one in order to check that the raycasts were pointing at the correct things. I modified the TooltipDisplay code to look like this:

public class TooltipDisplay : MonoBehaviour {
    public void FixedUpdate() { // I've tried it in Update too, no change
        PointerEventData pointerData = new PointerEventData(EventSystem.current);
        pointerData.pointerId = -1;
        pointerData.position = Mouse.current.position.ReadValue();
        
        // EventSystem raycast
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(pointerData, results);
        Tooltip highest = null;
        foreach (var result in results) {
            // There's only one result here, and it's often the wrong one :(
            Tooltip t = GetTooltip(result.gameObject);
            if (t == null) continue;
            if (highest == null || t.priority > highest.priority) {
                highest = t;
            }
        }
        
        // Manual Physics raycast
        Ray ray = Camera.main.ScreenPointToRay(pointerData.position);
        RaycastHit() physicsResults = Physics.RaycastAll(ray, 100);
        Tooltip manual = null;
        foreach (var rh in physicsResults) {
            Tooltip t = GetTooltip(rh.transform.gameObject);
            if (t == null) {
                continue;
            }
            if (manual == null || t.priority > manual.priority) {
                manual = t;
            }
        }
        
        if (highest != null && manual != null) {
            highest = new Tooltip(
                highest.title + " / " + manual.title,
                highest.body,
                highest.priority
        }
        
        // If we found a tooltip, display it.
        // ...
        // ...
    }
}

This produced the following behaviour on-screen, where the right-hand half of the tooltip title was always correct, and the left-hand one lagged behind:

EventSystem raycast lagging behind Physics raycast

This led me to wonder whether the EventSystem raycast might be using the wrong camera. This wasn’t the problem, but searching around that brought me to this article which mentioned the limit on the PhysicsRaycaster and led me to the fix. I’ve increased maxRayIntersections and everything is now fine.

What remains unclear is why this problem only shows up when changing fixedDeltaTime.

So, although I now have a workaround, if anyone can work out why the original problem only showed up when scaling time, I’ll gladly award you the bounty.

unity – Model missed by both EventSystem.current.RaycastAll and IPointerClickHandler unless disabled and re-enabled in the Editor

I have a situation where a model is ignored in favour of one behind it. The rear model is returned by EventSystem.current.RaycastAll, and the front model’s IPointerClickHandler callback isn’t triggered if the rear model is present. The rear model does not implement IPointerClickHandler (or pointer up/down handlers, etc). Both these things work on the front model if the rear model isn’t there.

Debugging shows that the Raycast is only returning one result. By comparing behaviour when the rear model is and isn’t present, I can verify that the distance value in the RaycastResult is greater when hitting the rear model than when hitting the front model.

I can “fix” the problem by using the Inspector to disable and re-enable the front model.

Here’s the problem and the “fix” in action:

enter image description here

Higher quality version here.

EventSystem.current.RaycastAll is used to power the tooltips, and IPointerClickHandler is (unsurprisingly) used to get the cube model to react to clicks.

Unity version: 2020.3.11f1

Unity: Model missed by both EventSystem.current.RaycastAll and IPointerClickHandler unless disabled and re-enabled in the Editor

I have a situation where a model is ignored in favour of one behind it. The rear model is returned by EventSystem.current.RaycastAll, and the front model’s IPointerClickHandler callback isn’t triggered if the rear model is present. The rear model does not implement IPointerClickHandler (or pointer up/down handlers, etc). Both these things work on the front model if the rear model isn’t there.

Debugging shows that the Raycast is only returning one result. By comparing behaviour when the rear model is and isn’t present, I can verify that the distance value in the RaycastResult is greater when hitting the rear model than when hitting the front model.

I can “fix” the problem by using the Inspector to disable and re-enable the front model.

You can find a video of the problem and “fix” in action here: https://imgur.com/a/Oku79iB

EventSystem.current.RaycastAll is used to power the tooltips, and IPointerClickHandler is (unsurprisingly) used to get the cube model to react to clicks.