Switching Input modes and handling player input for different game mechanics

Godot Version

v4.4.stable.official [4c311cbee]

Question

Hi all, I have been reading (and re-reading) Input because I am trying to implement a mini-game in my game similar to the terminal/strategem input in Helldivers 2.

Essentially, I want to process input once a player interacts with an object in game, and then handle all controller input for this mini-game. I am going to show icons on screen and have the user input controller actions. However, I don’t want the player to move, shoot, etc. While doing this. I want to temporarily have the player input focus over on the mini-game.

Okay, that is the summary. If you are interested in my thought process, here is my reasoning so far:

  • At first I thought, okay, perhaps I detect when a player has approached the terminal, show a control node, give it focus, and handle the inputs there. But I am using _input and on both the terminal and player nodes. Both were handled.
  • Then I thought, well, maybe I have a signal call to the player to change some kind of enum or bool, and I stop handling input in the player script altogether. But this would mean I need to handle state anytime I pause the game, create antother mini-game, etc. The structure of Godot input seems to act like a net, where things are handled and stop processing down the chain, so it felt like swimming against the design current. At least for as I understand it, I could be wrong.

This led me to believe I should just ask. I have read the docs and searched for examples but am left unsure about what direction to take. FWIW, this is the closest thing to what I am looking for: _gui_input vs _unhandled_input for key presses - #6

Thank you!

progress

If it helps, here is a .gif of what I want to accomplish along with the Control node code. How can I stop processing shooting on the player via the other node in the scene? Is setting a state on the player the ideal solution?

Terminal script

extends StaticBody3D
class_name HackingTerminal 

@onready var detection_component: DetectionComponent = $DetectionComponent
@onready var hacking_gui: Control = $Control

func _ready():
	detection_component.player_entered_detection_area.connect(_enable_hacking_mode)
	detection_component.player_exited_detection_area.connect(_disable_hacking_mode)

func _unhandled_input(event):
	if event.is_action("shoot"):
		print("Yep")
	
func _enable_hacking_mode():
	print("Hacking mode enabled")
	hacking_gui.set_focus_mode(Control.FOCUS_ALL)
	hacking_gui.grab_focus()
		
func _disable_hacking_mode():
	print("Hacking mode disabled")

partial Player movement script

func _physics_process(delta):
	handle_player_movement(delta)
	if Input.is_action_pressed("shoot"):
		shooting_component.handle_weapon_input()

Couple of solutions:

  1. Turn off physics processing for your player while in the mini-game.
  2. Turn off input processing for the nodes that are taking in input.
  3. Use only _unhandled_input() for your player, and use _input() for your minigame.
1 Like

Going to give this a go in a handful of hours, thank you for the suggestions.

1 Like

Hey @dragonforge-dev thank you again for the response. These were all reasonable options and I had to see what made sense for my game. Really all of them do, but I settled on solution #3.

I believe this logic is good enough (please correct me if I am wrong):

func _physics_process(delta):
	handle_player_movement(delta)

func _unhandled_input(event):
	if event is InputEventJoypadMotion:
		movement_direction = Input.get_vector("left_gamepad_left", 
										"left_gamepad_right", 
										"left_gamepad_forward", 
										"left_gamepad_backward")
		rotation_direction = Input.get_vector("right_gamepad_left",
										"right_gamepad_right",
										"right_gamepad_forward",
										"right_gamepad_backward")
	if event.is_action_pressed("shoot", true):
		shooting_triggered = true
	if event.is_action_released("shoot"):
		shooting_triggered = false
	
func shoot():
	shooting_component.handle_weapon_input()
	
func handle_player_movement(delta):
	if shooting_triggered:
		shoot()
	
	var player_direction = (transform.basis * delta * Vector3(movement_direction.x, 0, movement_direction.y)).normalized() 
	var player_rotation = (transform.basis * delta * Vector3(rotation_direction.x, 0, rotation_direction.y)).normalized() 
	if player_direction:
		velocity.x = player_direction.x * player_speed
		velocity.z = player_direction.z * player_speed
	else:
		velocity.x = 0.0
		velocity.z = 0.0
	if player_rotation != Vector3.ZERO:
		player_mesh.basis = Basis.looking_at(player_rotation)
		player_mesh.basis.orthonormalized()
	move_and_slide()

If anyone else comes across this, this thread was also useful: unhandled_input is not handling some input

I definitely overthought this, though. It was good to go back over the docs.

Apologies for the late reply, my wife and I adopted a puppy shortly after my original post and I am just getting back into my side project. Thanks again!

1 Like

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