Check if mouse is over Screen (UI)

Is there a way to check if the mouse is currently within a screen component / UI element?

Use case: I have an orbit cam and i would like to discard input if im currently over an ui element.
Currently if im dragging a Slider it will also move my camera.

Hi @LucaHeft,

You can do that using the mouseenter and mouseleave element events. Use a boolean property that you turn on/off accordingly and use it to check if the mouse is on top of an element or not.

Check the UI buttons tutorial on how to use these events:

https://developer.playcanvas.com/en/tutorials/ui-elements-buttons/

This would indeed work, but i would have to manually add a script to all UI elements.
This would be a hassle.

On the callback of the input event on the UI element, it is possible to stop to the propagation via https://developer.playcanvas.com/en/api/pc.ElementTouchEvent.html#stopPropagation

Hi @LucaHeft,

Another option would be to fire an event when a piece of UI gets opened like:

app.fire('windowOpen');

And subscribe to it in your in you Orbit Cam script to change a boolean like:

var self = this;
app.on('windowOpen', function() {
  self.active = false;
});

Then you could rely on that boolean to limit your orbit cam.

You could then fire an event like:

app.fire('windowClose');

whenever the button that closes the window is clicked and subscribe to the event in your Orbit Cam Script like:

app.on('windowClose', function() {
  self.active = true;
});

I hope you find that helpful!

2 Likes

I would still have to add this to every UI element. Which i would like to avoid.

This works for 2D UIs which fill the whole screen, but i also have 3D UIs.

Im searching for something like this:
https://docs.unity3d.com/2018.1/Documentation/ScriptReference/EventSystems.EventSystem.IsPointerOverGameObject.html

Another solution would be to raycast from Screen coords when the mouse is clicked ( pc.Picker maybe). Is this an option?

Is this already being done to handle grabbling the slider though?

Well the slider has grabbing logic that is indeed correct. I just mentioned the slider as an example. It could also be just an image, with no scripting logic.

Basically. When Mouse down is pressed my orbit cam should check if the mouse is over an UI element. If so do nothing. If not rotate camera.

Ok so i just did a quick test with pc.Picker and it seems to work!

For future reference:

var PickerFramebufferTest = pc.createScript('pickerFramebufferTest');

// initialize code called once per entity
PickerFramebufferTest.prototype.initialize = function() {
    // Create a frame buffer picker with a resolution of 1024x1024
    this.picker = new pc.Picker(this.app, 1024, 1024);
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onSelect, this);
};

PickerFramebufferTest.prototype.onSelect = function (event) {
    var canvas = this.app.graphicsDevice.canvas;
    var canvasWidth = parseInt(canvas.clientWidth, 10);
    var canvasHeight = parseInt(canvas.clientHeight, 10);

    var camera = this.entity.camera;
    var scene = this.app.scene;
    var picker = this.picker;

    picker.prepare(camera, scene);

    // Map the mouse coordinates into picker coordinates and
    // query the selection
    var selected = picker.getSelection(Math.floor(event.x * (picker.width / canvasWidth)), Math.floor(event.y * (picker.height / canvasHeight)));

    if (selected.length > 0) {        
        if(selected[0].node.parent.element){
            console.log("We hit UI!");
        }else{
            console.log("We hit something else!");
        }
    }
};

Hope this helps. You may want to bubble up the hierarchy depending on your use case.

1 Like

Be aware that using pc.Picker involves a GPU stall when used. Since it is only on mouse down, you should be okay

yes, picker is definitely a pretty expensive solution, as the scene (or part of it, depending on how you call it) needs to be re-rendered specifically for this.

And on top of that a WebGL context readPixels() has to be executed which can be quite slow, since it stalls the GPU as @yaustar mentioned.

1 Like