How to convert mouse coordinates to 3d position?

inspired_coderinspired_coder Posts: 6Member
edited October 2017 in 3D

I want to move 3d cube with mouse cursor, so i need convert mouse position for it. Can anyone help me?

Thanks in advance.


  • TwistedTwiglegTwistedTwigleg Posts: 542Moderator

    You’ll probably need to use raycasting to convert the mouse position to 3D space using a ray. Here’s a link to the documentation on raycasting which has some sample code that should help.

  • inspired_coderinspired_coder Posts: 6Member

    Yes, thank you, but there is not clear. I am using that function to get 3d world coordinates:


    ...but these coordinates cannot corresponds mouse position. Where is mistake?

  • TwistedTwiglegTwistedTwigleg Posts: 542Moderator
    Answer ✓

    This is the code that projects a Ray from the mouse coordinates

    var ray_lenght = 1000
    var mouse_pos = get_viewport().get_mouse_pos()
    var camera = get_node("camera")
    var from = camera.project_ray_origin(mouse_pos)
    var to = from + camera.project_ray_normal(mouse_pos) * ray_length

    Then you use those coordinates to raycast using intersect_ray in the direct space state.
    Something like this:

    var space_state = get_world().get_direct_space_state()
    # use global coordinates, not local to node
    var result = space_state.intersect_ray( from, to )

    Then you can do whatever you need with the results of the raycast.

  • 1000h1000h Posts: 133Member
    edited December 2017

    I'm trying to do the same thing right now.
    At first I tried using


    If you use these values to set the velocity of the object you want to move, they are relative to the origin. This means that the velocity of the physics body changes depending on where you click on the screen, and not where you click according to the physics body.

    Intuitively, you'd want the velocity to change depending on where you click relative to the physics body (e.g. click below the actor he goes down), but this is not the case when projecting the click event position.

    Then I thought I might transform (using Transform.xform(vector)) the projected position by the actors origin. This does not work either. The projected position is now no longer relative to the origin on which the camera is centered, but now relative to a moving body, and so the velocity will increase even more dramatically.

    When I was playing around, instead of using KinematicBody.move() I tried KinematicBody.move_to(), which is a teleportation function rather than a velocity type function. This got the object to move immediately (teleporting) to the position if I multiplied the projected click position by a large enough floating value, which to me seemed a bit arbitrary, and the value I chose that kept the object within the screen bounds was about 300. The reason this happens, remember, is the projected click always comes back as some value between 0 and 1, so you need to multiply it by some scalar.

    Anyway this is as far as I've gotten now, so I too haven't figured it out yet. I have looked a bit into some math but I am not mathy, so I don't know how to apply any of it yet. Of course the idea is to get it to work without using if else statements.

    The hacky way is setting up a navigation mesh plane veritcally so that it fills the screen. Your object would then travel to a point on the navigation mesh plane, not according to a projected screen point. This way is much faster assuming you are as bad at math as I am, but I am still trying to figure out the mathy way because reasons

  • 1000h1000h Posts: 133Member
    edited December 2017

    So the way I am settling on for now (although I'll probably switch to my navigation mesh idea) isn't as mathy as I'd have hoped, but it does the trick (generally).

    Note: This way works only if the camera is set such that the y and z coordinate of its transform origin are 0.

    So I have a Control that is the detector, and it does this:

            var event_point = Vector2()
            event_point = event.pos
            var dir = Vector3()
            var projected_click = Vector3()
            projected_click = cam.project_position(event_point)
            dir = projected_click
            dir*= cam.get_zfar()
            dir.x = 0

    Then on the kinematic simply do this:

        var new_position = get_global_transform().origin.linear_interpolate(dir,delta)

    I noted something interesting while doing this.
    I am using Godot something
    Usually when I set up a camera, I want to set the z_far to max because why not. But instead of typing in 4096, I type in like, 10000000 just so it pops up to the max and it is faster it seems to type that way. However, I realized that if you type in 1000000 or something much larger, then try to retrieve the zfar through cam.get_zfar(), although it is displaying at 4096 in the editor, get_zfar() will pick up the float of whatever crazy number you type in to cap it off in the editor. So if you did what I did, you need go back and type in 4096 in the editor, or when you use get_zfar in the code I've provided, it will shoot your dude off in some crazy direction

  • 1000h1000h Posts: 133Member
    edited December 2017

    I am now realizing that when the camera projects a position using project_position, the position is based on the camera's transform origin and the znear value. If you project a position and you have a near of .1, it will project the position within some range of the camera's position and .1. If you are at a position of 32 with a near of .1, the projected position will be something like 31.9. Likewise, if you have a near of 10, and a position of 32, it will be somewhere in the 20s. By altering this near value, I've found that the code works above even with strange camera orientations, but then you run into clipping issues.

    I realize that a way to get around this issue of having the correct position but avoiding clipping is to set the z_near to some greater value, store the projected click position in this state, and then immediately switch the z_near to some near 0 value again that avoids clipping!

Sign In or Register to comment.