toggling the holding of an object

Godot Version

Godot 4.6

Question

I’m making a little clone of Portal for fun, and right now I’m working on the boxes.

I have a RigidBody3D that gets picked up and the tutorial I watched used Input.is_action_pressed(“interact”)

which is fine, but I want it to toggle between being held and not being held.

I tried getting this by putting this process into the code for my player:

func _process(delta):
	var holding= false
	var object = interactioncast.get_collider()
	if interactioncast.is_colliding():
		if object.is_in_group("pickup"):
			if Input.is_action_just_pressed("interact"):
				if not holding:
					holding= true
					while holding:
						object.global_position= Socket.global_position
						object.global_rotation= Socket.global_rotation
						object.linear_velocity= Vector3(0.1, 0.5, 0.1)
				if holding:
					holding= false

the issue is that the “while” condition completely freezes my game and only lets the while loop compute. I can’t think of any other way to get it to work because is_action_just_pressed only picks up the object for a small amount of time.

I also need to figure out how to stop the box from clipping through walls but that’s another issue entirely.

Your while should be part of a process or physics_process, but RigidBodies don’t like having their position updated directly, you will have to freeze the object or apply forces to move the RigidBody towards your target position. You will need to keep track of the object with a member variable, not local to _process

var holding_object: RigidBody = null

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("interact"):
		if holding_object: # drop object
			holding_object = null
		else:
			var object = interactioncast.get_collider()
			if interactioncast.is_colliding():
			if object.is_in_group("pickup"):
				holding_object = object

func _physics_process(delta: float) -> void:
	if holding_object:
		holding_object.apply_central_force(<a variable that points towards your target direction>)

A script must run from bottom to top (with the exception of awaits) to start the next frame, your while loop traps the game’s process forever.

1 Like

You don’t need a while loop. Your _process() is already a loop, so take advantage of that.
Also, I would put the event handling in the _unhandled_input() callback instead and handle only necessary logic in the _physics_process().

Something like that should work:

var held_object: Node

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("interact"):
		if held_object:
			held_object = null
		elif interactioncast.is_colliding():
			var interacted_object: Node = interactioncast.get_collider()
			if interacted_object.is_in_group("pickup"):
				held_object = interacted_object

func _physics_process(_delta: float) -> void:
	if held_object:
		held_object.global_position= Socket.global_position
		held_object.global_rotation= Socket.global_rotation
		held_object.linear_velocity= Vector3(0.1, 0.5, 0.1)

And I see @gertkeno already mentioned about directly adjusting RigidBodies - you should avoid that if possible.

1 Like