Trying to make a system for lifting/grabbing objects

Godot Version

4.3

Question

I'm trying to make a system so my character can pick up items. specifically items that are in the "canGrab" group. For this, I created an Empty Node in my character where a Raycast and a Marker3D are located. In this empty node I added the following script

extends Node3D

@export var raycast: RayCast3D
@export var grabPos: Node3D
var picked = null


func _physics_process(delta: float) -> void:
	if Input.is_action_just_pressed("Interact"):
		can_grab()

	if Input.is_action_just_pressed("DropObj"):
		if picked:
			release_object()

	if picked:
		picked.global_transform.origin = grabPos.global_transform.origin
		picked.global_transform.basis = grabPos.global_transform.basis

func can_grab() -> void:
	if raycast.is_colliding():
		var collider = raycast.get_collider()
		if collider.is_in_group("canGrab"):
			if picked:
				release_object()
			picked = collider

func release_object() -> void:
	if picked:
		picked = null

When I first tried it I thought it was working fine, but it wasn’t. When I grab the object, it can get stuck in other objects in the environment. And when I drop it, it sometimes gets thrown out.

Here is a video of the problem.

In release_object(), you could explicitly set the position of the object so it doesn’t fly off.

Alternatively, maybe you could disable the CollisionShape of the object while it’s picked up so it no longer interacts with other objects. Then re-enable it inside release_object()

Thanks for answering.
I tried what you told me about specifying the position before dropping the object, but it worked the same (I probably didn’t know how to implement it, I’m still a bit new to this).
Instead, I made the object be in “sleeping” mode while I pick it up, so it doesn’t fly away when I drop it.

As for the collisions you told me, what I wanted was that when picking up the object, it could collide with the environment, something it didn’t do, or not correctly.
So I came up with the idea of ​​implementing a CollisionShape3D inside the player that is invisible and disabled. But when picking up an object it takes the shape of the object.
With this it works much better, although it still has its errors. I don’t know how functional or adequate this is, but it’s already progress.

Now this is my script

extends Node3D

@export var raycast: RayCast3D
@export var grabPos: Marker3D
var picked : RigidBody3D = null
@export var altCollision: CollisionShape3D

func _ready() -> void:
	altCollision.visible = false
	altCollision.disabled = true

func _physics_process(delta: float) -> void:
	if Input.is_action_just_pressed("Interact"):
		can_grab()

	if Input.is_action_just_pressed("DropObj"):
		if picked:
			release_object()

	if picked:
		picked.global_transform.origin = grabPos.global_transform.origin
		picked.global_transform.basis = grabPos.global_transform.basis
		picked.sleeping = true
		
		altCollision.visible = true
		altCollision.disabled = false
		altCollision.global_transform.origin = picked.global_transform.origin
		altCollision.global_transform.basis = picked.global_transform.basis


func can_grab() -> void:
	if raycast.is_colliding():
		var collider = raycast.get_collider()
		if collider.is_in_group("canGrab"):
			if picked:
				release_object()
			picked = collider
			picked.sleeping = false
			
			for child in picked.get_children():
				if child is CollisionShape3D:
					altCollision.shape = child.shape
					print(altCollision.shape)
					break
			
			picked.set_collision_layer_value(2, false)
			picked.set_collision_layer_value(5, true)



func release_object() -> void:
	if picked:
		picked.sleeping = false
		altCollision.visible = false
		altCollision.disabled = true
		picked.set_collision_layer_value(2, true)
		picked.set_collision_layer_value(5, false)
		picked = null

1 Like