Context: I'm implementing Gamepad input and trying to simulate a mouse cursor. I have Area2Ds in my scene for objects I want to be able to interact with the simulated cursor (ex. hover/click). I'm trying to get an Area2D in the scene to follow the mouse cursor so I can get AreaEntered/Exited events when it overlaps those objects. My simulated mouse pointer is a Control with a TextureRect, on a CanvasLayer.

I've tried a bunch of methods on the pointer Control, like (C#):

myArea2D.GlobalPosition = myPointerControl.GetScreenTransform().Origin
myArea2D.GlobalPosition = myPointerControl.GetGlobalTransformWithCanvas().Origin

But none of these work. My Area2D follows the motion of the cursor (so the scale is right) but is offset by a large amount that seems to change depending on the position of the Camera2D.

In general I want a CanvasToWorld function I can use like this to translate from Canvas coordinates to World coordinates:
myNode2D.GlobalPosition = CanvasToWorld(myPointerControl.GlobalPosition);

Does anyone know how to do this?

I discovered a workaround. I can use Viewport.WarpMouse() to make the mouse follow the simulated mouse pointer, and then use Node2D.GetGlobalMousePosition() to get the coordinates of the mouse pointer in World space.

I'm not happy with this solution because it uses the actual mouse cursor as an intermediary. I'd much rather have a mouse-independent solution. It should be possible, it's just a matter of knowing the right set of transforms to apply/unapply. Unfortunately I can't make sense of Godot's documentation on their 2D coordinate systems. O.O

  • xyz replied to this.

    draggor What happens if you just assign texture rect's global position to area's global position?

    When I do that, I see the same behavior I described above: "My Area2D follows the motion of the cursor (so the scale is right) but is offset by a large amount that seems to change depending on the position of the Camera2D."

    • xyz replied to this.

      draggor Can you post a minimal reproduction project?

      Alright, so in the process of creating the min repro project, I think I figured things out.

      When in a 2D scene that doesn't have a Camera2D in it, setting area.GlobalPosition = pointer.GlobalPosition works as desired.

      The moment a Camera2D is added to the scene, the area appears offset by opposite the offset of the camera.

      From this article I found out the transform I was looking for was the inverse of the viewport's CanvasTransform. So the final solution was this:

      area.GlobalPosition = GetViewport().CanvasTransform.AffineInverse() * pointer.GlobalPosition