godot-docs icon indicating copy to clipboard operation
godot-docs copied to clipboard

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

Open Shadowblitz16 opened this issue 2 years ago • 21 comments

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

Shadowblitz16 avatar Jun 14 '23 12:06 Shadowblitz16

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

AThousandShips avatar Jun 14 '23 14:06 AThousandShips

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

Shadowblitz16 avatar Jun 16 '23 20:06 Shadowblitz16

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

AThousandShips avatar Jun 16 '23 20:06 AThousandShips

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

Shadowblitz16 avatar Jun 16 '23 21:06 Shadowblitz16

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

AThousandShips avatar Jun 16 '23 21:06 AThousandShips

@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

Shadowblitz16 avatar Oct 27 '23 17:10 Shadowblitz16

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?

AThousandShips avatar Oct 27 '23 18:10 AThousandShips

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

Shadowblitz16 avatar Oct 27 '23 18:10 Shadowblitz16

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:

  • Left joystick moves mouse
  • PS Cross/Xbox A/Nintendo B is left mouse button
  • PS Circle/Xbox B/Nintendo A is right muse button
  • PS Triangle/Xbox Y/Nintendo X, or escape, toggles between joystick mode and normal mode

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:

  • Replace Input.MOUSE_MODE_CAPTURED with Input.MOUSE_MODE_CONFINED (or remove all assignments to Input.mouse_mode altogether, or even the active variable)
  • Add Input.warp_mouse(mouse_pos) above Input.parse_input_event(event) in _process
  • Add this to the end of _input:
if event is InputEventMouseMotion:
	if event.device >= 0:
		mouse_pos = event.position
		queue_redraw()

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"

AThousandShips avatar Oct 27 '23 18:10 AThousandShips

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

Shadowblitz16 avatar Oct 27 '23 18:10 Shadowblitz16

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

AThousandShips avatar Oct 27 '23 18:10 AThousandShips

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.

Shadowblitz16 avatar Oct 27 '23 18:10 Shadowblitz16

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

AThousandShips avatar Oct 27 '23 18:10 AThousandShips

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

Shadowblitz16 avatar Oct 27 '23 19:10 Shadowblitz16

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

AThousandShips avatar Oct 27 '23 19:10 AThousandShips

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

Shadowblitz16 avatar Oct 27 '23 19:10 Shadowblitz16

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

AThousandShips avatar Oct 27 '23 19:10 AThousandShips

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.)

stephanbogner avatar Aug 17 '24 17:08 stephanbogner

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.

Shadowblitz16 avatar Aug 18 '24 04:08 Shadowblitz16

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 avatar Aug 18 '24 06:08 stephanbogner

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)

stephanbogner avatar Aug 18 '24 15:08 stephanbogner