Help with hold down a button for certain amount of time to perform action

Godot Version

v4.2.2.stable.official [15073afe3]

Question

Hello. I am trying to implement a feature in which the player can switch between two CharacterBody3D’s by pressing a button to switch one way and holding the button for a certain amount of time to switch back. The problem is, that the system works, but the timer doesn’t reset if the button is let go. In practice, the player can just spam press E (or “interact”) to switch back. I don’t want this to be the case. Any help is appreciated. I’ll answer any questions as needed. The feature that isn’t working is the line “await get_tree().create_timer(1).timeout”

extends CharacterBody3D

var speed
var execute_this_code: bool = false
const WALK_SPEED = 5.0
const SPRINT_SPEED = 8.0
const JUMP_VELOCITY = 4.5
const SENSITIVITY = 0.003

#bob variables
const BOB_FREQ = 2.0
const BOB_AMP = 0.08
var t_bob = 0.0

@onready var mech = $"."
@onready var player = $"../Player"

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

func _process(delta):
	if Input.is_action_just_pressed("interact"):
		exit()

func _unhandled_input(event):
	if execute_this_code:
		if event is InputEventMouseMotion:
			mech.rotate_y(-event.relative.x * SENSITIVITY)
			mech.rotation.x = clamp(mech.rotation.x, deg_to_rad(0), deg_to_rad(0))

func _physics_process(delta):
	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	if not is_on_floor():
			velocity.y -= gravity * delta

	if Input.is_action_pressed("sprint"):
		speed = SPRINT_SPEED
	else:
		speed = WALK_SPEED
	if execute_this_code:
		var input_dir = Input.get_vector("left", "right", "forward", "backward")
		var direction = (mech.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
		if is_on_floor():
			if direction:
				velocity.x = direction.x * speed
				velocity.z = direction.z * speed
			else:
				velocity.x = lerp(velocity.x, direction.x * speed, delta * 7.0)
				velocity.z = lerp(velocity.z, direction.z * speed, delta * 7.0)
		else:
			velocity.x = lerp(velocity.x, direction.x * speed, delta * 3.0)
			velocity.z = lerp(velocity.z, direction.z * speed, delta * 3.0)
	#Head bob
	t_bob += delta * velocity.length() * float(is_on_floor())
	$Camera3D.transform.origin = _headbob(t_bob)
	

	
	
	move_and_slide()

func _headbob(time) -> Vector3:
	var pos = Vector3.ZERO
	pos.y = sin(time * BOB_FREQ) * BOB_AMP
	pos.x = cos(time * BOB_FREQ / 2) * BOB_AMP
	return pos

func enter(body):
	execute_this_code = true
	player.execute_this_code = false
	$"../Player/Head/Camera3D/InteractRay".enabled = false
	$"../Player/Head/Camera3D".current = false
	$Camera3D.make_current()

func exit():
	if execute_this_code:
		if !Input.is_action_pressed("interact"):
			return
		await get_tree().create_timer(1).timeout
		if !Input.is_action_pressed("interact"):
			return
		execute_this_code = false
		player.execute_this_code = true
		$"../Player/Head/Camera3D/InteractRay".enabled = true
		$Camera3D.current = false
		$"../Player/Head/Camera3D".make_current()
		print("good")

func _on_seat_interacted(body):
	pass

I don’t understand why you have the same if !interact twice.

Sorry, I was following this tutorial:

The video is a little outdated (Godot 3 I believe) but it still works for the most part, just the one issue. I’m not exactly sure why he writes it twice.

I found a solution:

func exit():
	if execute_this_code:
		if !Input.is_action_pressed("interact"):
			$"../Timer".stop()
			return
		#await get_tree().create_timer(1).timeout
		$"../Timer".start()
		await $"../Timer".timeout
		if !Input.is_action_pressed("interact"):
			$"../Timer".stop()
			return
		execute_this_code = false
		player.execute_this_code = true
		$"../Player/Head/Camera3D/InteractRay".enabled = true
		$Camera3D.current = false
		$"../Player/Head/Camera3D".make_current()
		print("good")
1 Like

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