godotengine / godot-docs

Godot Engine official documentation
https://docs.godotengine.org
Other
3.78k stars 3.07k forks source link

Add example docs on how to simulate mouse pressed and releases through artifical events #7515

Open Shadowblitz16 opened 1 year ago

Shadowblitz16 commented 1 year ago

Your Godot version: v4.1.beta1.mono.official [828ec2c5d]

Issue description: I am trying to understand how to simulate mouse button presses and releases without moving the mouse with the gamepad and keyboard.

The idea is that I would move the mouse, send a mouse button events and move it back but I can't get the control nodes to recognize the events and I don't know how to pass the correct position

URL to the documentation page (if already existing): na

AThousandShips commented 1 year ago

Feel free to use the examples in godotengine/godot-proposals#6874, I'm not able to write anything up right now but that should be a good start, I'd suggest a comprehensive tutorial rather than specifically this, and different aspects of simulating events from different sources

Shadowblitz16 commented 1 year ago

Feel free to use the examples in godotengine/godot-proposals#6874, I'm not able to write anything up right now but that should be a good start, I'd suggest a comprehensive tutorial rather than specifically this, and different aspects of simulating events from different sources

That works only with custom drawing I am trying to use the node2d or control's position or global_position property If I do then it's offseted by some weird value

AThousandShips commented 1 year ago

That works only with custom drawing

I'm talking about my suggestions and code there https://github.com/godotengine/godot-proposals/issues/6874#issuecomment-1554680721, nothing about drawing at all

Shadowblitz16 commented 1 year ago

That works only with custom drawing

I'm talking about my suggestions and code there godotengine/godot-proposals#6874 (comment), nothing about drawing at all

you assign to mouse_pos instead if position and draw a red circle instead of using a TextureRect

AThousandShips commented 1 year ago

Yes, but you can just use the simulated mouse input, you just have to adapt the code a little

But if that's not interesting to you then no need to use it, I just offered the option, my bad

Shadowblitz16 commented 10 months ago

@AThousandShips controls use different mouse detection. It's not as simple as just drawing a circle. I need the engine to pickup that I clicked somewhere that I didn't actually physically click

AThousandShips commented 10 months ago

You've confirmed that pushing input events won't work? I didn't suggest just drawing a circle, I provided code for doing this part, did you test it?

Shadowblitz16 commented 10 months ago

You've confirmed that pushing input events won't work? I didn't suggest just drawing a circle, I provided code for doing this part, did you test it?

You posted a example that draw a circle but that has nothing to do with my problem. Yes I have tested it. My problem is that godot seems to have 20 diffrent spaces for what or where 0,0 is infact I don't think controls and node2d's even use the same space.

What I am trying to do is convert my node2d position to an event position back to a control position.

func click()->void:
    var evt = InputEventMouseButton.new()
    evt.button_index = MOUSE_BUTTON_LEFT
    evt.position = get_viewport().get_mouse_position() # this does not work it's not in the same space
    evt.pressed = true
    Input.parse_input_event(evt)
    evt.pressed = false
    Input.parse_input_event(evt) 

honestly control nodes should have the same world space and draw order as node2d's but that's not what this issue is about. it's about getting documentation on how to converting a node2d position to a correct event position for a control node to reconize

AThousandShips commented 10 months ago

I literally posted the instructions to do that, as I've said multiple times it's not "drawing a circle", please read my comment :)

See: "This is an extremely basic example, do not use this for anything production but do analyse it for the basic approach to accomplish this sort of thing, note that it will glitch if you move the mouse while in active mode (just moving the stick will fix it), it starts with the mouse captured and you need to switch modes to turn that off (if the mouse exits the hover management is turned off, this is used to prevent this)

CursorExample.zip

Controls:

Hope this helps

An alternative, and non-portable, way to do this is to use Input.warp_mouse, this is accomplished by changing the following:

In this case you can just not draw the cursor if you want to just use the native cursor, this avoids some of the quirks, but adds their own quirks as well

Here is that second version, note that it is not portable and only works correctly on desktop platforms (not web) CursorExample2.zip"

Shadowblitz16 commented 10 months ago

I don't want to confine my mouse because I don't know if the game will be played on desktop or console. Also if example 1 is buggy and example 2 is not portable then that's not good I want to simulate a mouse click at a position the mouse may or may not be at without modifying the mouse.

This is starting to feel like a engine limitation and that https://github.com/godotengine/godot-proposals/issues/6874 should have been implemented

AThousandShips commented 10 months ago

Then say that instead of claiming I just showed a circle :)

Shadowblitz16 commented 10 months ago

Was example 1 not a circle? Maybe I got a project mixed up, sorry

I do see that you use a control instead of a node2d I use a animated sprite2d so world space might be diffrent.

AThousandShips commented 10 months ago

I gave you two different examples and you reduced them to one instead of presenting your issues with the second one (the second one literally shows controls being interacted with)

If you say "just draw a circle" and I give two examples then you're misrepresenting what I've said

Shadowblitz16 commented 10 months ago

sorry :( is there a way to do example1 without locking or moving the cursor?

AThousandShips commented 10 months ago

The locking is to prevent the mouse from interfeering, as it will relocate the mouse position, and break the sync, can be done without it I think (hard to remember how it worked as I wrote the code some time ago) so feel free to test by removing that part, but might need to compensate for normal mouse input

Shadowblitz16 commented 10 months ago

would temporary moving it and moving it back work? would the movement even be visible in the same frame?

EDIT: Also I did test whether a mouse motion event is required. It looks like controls don't detect things unless a mouse motion event happens

AThousandShips commented 10 months ago

I don't know, you play around with it and see!

Yes the motion is requried, and works with the complete example, so I'd suggest some workaround

The reason for the locking of the mouse was to allow co-existing of mouse and controller, and it's possible that further tweaks can allow that even without it, my project was just a starting point to work off of, and unfortunately I don't have the time to play more with it at the moment as I don't need this kind of input in any project I'm working with

stephanbogner commented 1 month ago

Adding this information to the docs would be great. Some ideas of what to include:

  1. Mentioning things like "Virtual mouse" or "Emulated cursor" as it seems like a common use case
  2. Mentioning that mouse-like cursors via gamepads should be avoided (if possible) as it's less accessible (not sure where I heard it) and seems less liked (even though games like Destiny use it)
  3. Name-dropping some common games/terms for SEO (Ace Attorney, Monkey Island, point and click adventure, etc.)
Shadowblitz16 commented 1 month ago

Adding this information to the docs would be great. Some ideas of what to include:

1. Mentioning things like "Virtual mouse" or "Emulated cursor" as it seems like a common use case

2. Mentioning that mouse-like cursors via gamepads should be avoided as it's less accessible (not sure where I heard it) and [seems less liked](https://www.reddit.com/r/gamedesign/comments/1b5z69v/whats_everyones_thoughts_on_the_mouse_cursor/) (even though games like [Destiny](https://archive.org/details/GDC2016Candland/page/n7/mode/2up) use it)

3. Name-dropping some common games/terms for SEO (Ace Attorney, Monkey Island, point and click adventure, etc.)

Mouse like cursors should not be recommended against just because this engine doesn't support it very well. There are plenty of scenarios that this sort of thing would be a good idea.

stephanbogner commented 1 month ago

Adding this information to the docs would be great. Some ideas of what to include:

1. Mentioning things like "Virtual mouse" or "Emulated cursor" as it seems like a common use case

2. Mentioning that mouse-like cursors via gamepads should be avoided as it's less accessible (not sure where I heard it) and [seems less liked](https://www.reddit.com/r/gamedesign/comments/1b5z69v/whats_everyones_thoughts_on_the_mouse_cursor/) (even though games like [Destiny](https://archive.org/details/GDC2016Candland/page/n7/mode/2up) use it)

3. Name-dropping some common games/terms for SEO (Ace Attorney, Monkey Island, point and click adventure, etc.)

Mouse like cursors should not be recommended against just because this engine doesn't support it very well. There are plenty of scenarios that this sort of thing would be a good idea.

Well. I gave two reasons that having nothing to do with engine support. Of course there are scenarios where they are useful/unavoidable (mentioned in point 3) but generally controlling a cursor with a gamepad – especially for menus – is not that pleasant.

stephanbogner commented 1 month ago

In my cursor.gd-script the following serves me well (if others find it helpful): (I'll update my comment as I learn more)

var follow_mouse := false # Whether we use mouse coordinates or gamepad input
var last_tracked_mouse_position := Vector2.ZERO

func _input(event:InputEvent) -> void:
    # Fake mouse clicks and release when on gamepad
    if event.is_action_pressed("select"):
        if not event is InputEventMouseButton:
            trigger_left_mouse_click_at(global_position)
    elif event.is_action_released("select"):
        if not event is InputEventMouseButton:
            trigger_left_mouse_release_at(global_position)

    # Check whether we should use mouse input
    if not follow_mouse and event is InputEventMouseMotion:
        var distance := last_tracked_mouse_position.distance_to(get_global_mouse_position())
        if distance > 5: # This avoids switching with very sensitive mice
            follow_mouse = true
            last_tracked_mouse_position = get_global_mouse_position()

func _process(delta:float) -> void:
    var direction_and_strength := Vector2(Input.get_axis("move_left", "move_right"), Input.get_axis("move_up", "move_down"))

    # Switch to gamepad mode
    if direction_and_strength.length() > 0.5:
        follow_mouse = false

    # Trigger fake mouse move event
    if not follow_mouse:
        trigger_mouse_move_event_at(global_position)

func trigger_left_mouse_click_at(global_coordinates:Vector2) -> void:
    var click_event := InputEventMouseButton.new()
    click_event.device = -1
    click_event.position = global_coordinates
    click_event.button_index = MOUSE_BUTTON_LEFT
    click_event.pressed = true
    Input.parse_input_event(click_event)

func trigger_left_mouse_release_at(global_coordinates:Vector2) -> void:
    var release_event := InputEventMouseButton.new()
    release_event.device = -1
    release_event.position = global_coordinates
    release_event.button_index = MOUSE_BUTTON_LEFT
    release_event.pressed = false
    Input.parse_input_event(release_event)

func trigger_mouse_move_event_at(global_coordinates:Vector2) -> void:
    var move_event := InputEventMouseMotion.new()
    move_event.device = -1
    move_event.position = global_coordinates
    Input.parse_input_event(move_event)