Picking up, dropping, and throwing a box in Godot 3D

Godot Version

Godot 4.4

Question

Hello! I have been having issues with implementing a simple pick-up and drop object system. I want my player to be able to push against an object, pick it up, drop it, or throw it. I am unsure how to achieve this. I managed to write code that detects the player touching the object (a box) but not the actual picking up part.

1 Like

You can add a Marker3d as players hand and set the objects position into the hand when its colliding with the raycast and a button is clicked.Also use a group to identify the pickable obj or you can just pickup the entire map:D(Actually that happened to me once:) )

1 Like

Thankfully I have implemented so far something very similar to what you are suggesting. The problem is that the object will teleport to the Marker3D “hands” of the player, but not follow them at all, it’s more of a teleport than actually holding it.

I want to know if there is a method to have a RigidBody3D follow that Marker3D without constant teleportation to it.

1 Like

Hey, sorry for the late reply.

First, make sure the Marker3D is a child of the player’s Camera3D. When you try to pick up an object, also check if the left mouse button is being held. Add an Area3D at the same position as the Marker3D, and check if it detects with any object (physics body)that’s in the “pickable” group.

If all of that is true, let the object set his position to the Marker3D. What’s actually happening is that the RayCast detects the object from the center of the screen (or wherever you’ve placed it), but once the object moves to the Marker3D’s position, it’s no longer under the ray, so it doesn’t get detected again.

Here’s a basic example of how the main function could look:

#Player code from chatgpt:) I lazy to do that...

var is_any_body = false
var picked_object = null

func _ready():
    $Marker3D/Area3D.body_entered.connect(_on_body_entered)
    $Marker3D/Area3D.body_exited.connect(_on_body_exited)

func _physics_process(delta):
    if picked_object:
        if $RayCast3D.is_colliding() and $RayCast3D.get_collider() == picked_object and is_any_body:
            picked_object.global_transform.origin = $Marker3D.global_transform.origin
        else:
            picked_object = null  # Drop the object if conditions are no longer met

func _input(event):
    if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
        if $RayCast3D.is_colliding():
            var obj = $RayCast3D.get_collider()
            if obj.is_in_group("pickable"):
                await get_tree().create_timer(0.1).timeout
                if is_any_body:
                    picked_object = obj  # Start tracking the object

func _on_body_entered(body):
    is_any_body = true

func _on_body_exited(body):
    is_any_body = false






                
2 Likes

Hope that work.There are many fancy ways to do that but ,I think it’s the easiest way possible :smiley:

1 Like

Very interesting! While waiting for responses I actually managed to discover another interesting solution in the midst. I gave the player an extra Area3D, a pickup area, that detects any bodies within it using the get_overlapping_bodies() function.

If the player holds MOUSE 2 while the pickup area can detect a body in any group related to pick up-able objects, it will teleport the box into the hand area. This keeps it above the player’s head, while still being detected by the pickup area. So the box can be constantly updated above the player times delta, which looks smooth and nice. When MOUSE 2 is released, it applies an impulse to the body and flings it out of the pickup area.

1 Like

Yeah,I was very late :slight_smile: