Handle mouse input for the viewport currently under the mouse cursor

Godot Version

4.2.2

Question

Hi all,

I’d appreciate some help handling input with multiple viewports.

I have a split screen view created using two subviewports.

Each viewport renders a different scene and these scenes work independently as expected: the ‘player’ in each scene responds to the mouse “click” event by calling my pathfinding script to navigate the tilemap.

However, when I place these scenes in their respective subviewports, mouse input for the scene with the larger tile map is still being called when clicking on the other viewport. In other words, when a mouse click occurs on a tile that is walkable, the movement occurs, even if that tile wasn’t currently on screen.

I’ve tried using get_viewport().get_mouse_position() rather than get_global_mouse_position(). However, that doesn’t seem to report the mouse position only for that viewport.

I think i’m looking for a way to either:

  • restrict mouse input to the viewport currently under the mouse

or

  • toggle each viewport’s ability to receive mouse events on and off

I came across this forum post but despite looking at the viewport documentation, I’ve not been able to work out how to implement this.

My input code (a script on the ‘player’ in the world scene) currently looks like this:

func _physics_process(delta):
	
	if Input.is_action_just_pressed("click"):
					
		var mouse_pos = get_viewport().get_mouse_position()
		var tile_pos = terrain.local_to_map(mouse_pos)
		
		# get current position in tile space
		var current_pos = terrain.local_to_map(position)

		# get target postion by converting mouse to tile position
		var target_pos = terrain.local_to_map(terrain.get_local_mouse_position())
		
		path = pathfinding.find_path(current_pos, target_pos)

       if len(path) > 0:
		
		var direction = global_position.direction_to(path[0])
		
		var terrain_difficulty = pathfinding.get_terrain_difficulty(terrain.local_to_map(position))
		velocity = direction * SPEED * (1 / terrain_difficulty)
		
		look_at(path[0])
		
		if position.distance_to(path[0]) < SPEED * delta:
			path.remove_at(0)
		
	else:
		velocity = Vector2.ZERO
	
	move_and_slide()

My node hierarchy looks like this:


World scene within one of the viewports

I think i’ve almost got there by using has_point() on the viewport_rect() to check is the mouse click event position is within it. However, i’m struggling to refactor my physics_process code to separate out the positioning code from the movement code, which needs delta.

func _input(event):
	
	if event is InputEventMouse and event.is_action_pressed("click"):
		
		var container = get_viewport()
		
		if get_viewport_rect().has_point(event.position):
			print("within the world viewport") # <-- seems to only fire when clicks are within the viewport, as intended
						
			var mouse_pos = event.position
			print("mouse pos: ", mouse_pos)

I would be grateful for some help. Thank you.

I worked on this a bit more and managed to get it working with has_point(). Sometimes writing out the problem helps you work through it.

I moved my position/target and pathfinding call to _input(event) and kept my movement code in _physics_process(delta).

I made this tweak to both ‘player’ scripts and the conditional input code is only called when the mouse cursor is within the relevant viewport.

I’ll close this issue, but i’m sure my code isn’t very elegant. If there’s any improvements to be made here, i’d welcome them. Thank you.


func _input(event):
	
	if event is InputEventMouse and event.is_action_pressed("click"):
		
		if get_viewport_rect().has_point(event.position):
			print("within the world viewport")
						
			var mouse_pos = event.position
			print("mouse pos: ", mouse_pos)
			
			var tile_pos = terrain.local_to_map(mouse_pos)
			print("mouse pos:", mouse_pos)
			print("tile pos:", tile_pos)
		
			# get current position in tile space
			var current_pos = terrain.local_to_map(position)
			# get target postion by converting mouse to tile position
			var target_pos = terrain.local_to_map(terrain.get_local_mouse_position())
			
			path = pathfinding.find_path(current_pos, target_pos)
func _physics_process(delta):
		
	if len(path) > 0:
		
		var direction = global_position.direction_to(path[0])
		
		var terrain_difficulty = pathfinding.get_terrain_difficulty(terrain.local_to_map(position))
		velocity = direction * SPEED * (1 / terrain_difficulty)
		
		look_at(path[0])
		
		if position.distance_to(path[0]) < SPEED * delta:
			path.remove_at(0)
		
	else:
		velocity = Vector2.ZERO
	
	
	move_and_slide()

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.