A question about global_position and many nodes that share one script

Using Godot 4.2.1

So I have a map scene with several Location Nodes:

extends Area2D
class_name LocationNode

func _input(event: InputEvent) -> void:
	if event.is_action_pressed("left_mouse"):
		Events.map_location_selected.emit(self.global_position)

and I have a location selector - a node with a sprite that highlights the player’s selected location:

extends Node2D
class_name MapLocationSelector


func _ready():
	Events.map_location_selected.connect(_on_map_location_selected)
	
func _on_map_location_selected(location: Vector2):
	var tween := create_tween().set_trans(Tween.TRANS_QUINT)
	var end := location
	tween.tween_property(self, "global_position", end, 0.3)

They’re connected via global Events bus. My problem is that the location selector only ever goes to the first location node on the list and just stays there. This happens no matter which location node I select. There’s probably a really simple fix to all this but I just can’t figure it out.

Thanks for taking a look, hope you can help.

This Area2D, will emit its global_position whenever the mouse is clicked. If there are multiple copies of this Area2D script you are not going to get the behavior you want, since all the nodes will get that left click input and all will emit their global position in an order. so whichever is the last in line will get to set their position on the event bus.

Anyways, I think you want to try something like ray selectable when firing from the mouse position and having that collision propagate a signal to the Area2d and event bus for movement. So don’t use the _input function for this on the Area2D

1 Like

What @pennyloafers said.

Easiest way is to check if mouse cursor is within bounds of this area2D, so just add a line to your _input like so:

func _input(event: InputEvent) -> void:
	if event.is_action_pressed("left_mouse"):
		if get_global_rect().has_point(get_viewport().get_mouse_position()):
			Events.map_location_selected.emit(self.global_position)
1 Like

I meant something like this, area 2d has a collision shape and can be pickable by the mouse.

This requires the areas colissionshap to be pickable and override the _input_event function.

https://docs.godotengine.org/en/stable/classes/class_collisionobject2d.html#class-collisionobject2d-private-method-input-event

1 Like

This line has two problems:

  1. get_global_rect is available only for Control nodes, but not for Area2D nodes.
  2. Control.get_global_rect is in the coordinate system of the canvas layer, but get_viewport().get_mouse_position() is in the coordinate system of the viewport, so if the canvas layer is moved, scaled or rotated, then this combination will deliver incorrect results.

Regarding the original question, I would use physics picking and the _input_event function, as pennyloafers suggested.
_input is called on every node in the viewport, and not just the nodes, where the mouse is.

1 Like

Good points.

I generally give people answers that help them think in a direction. If it’s Area2D instead of Control, same principle: areas have collision shapes, and shapes have “get_rect()” method. From there it should be easy to google how to find if a mouse pointer is within a rect (for example, to_local() func that translates coords from global to local).

1 Like

Haven’t been able to test or implement the solutions you all provided due to RL problems but I wanted to thank everyone who contributed to the thread

2 Likes