Help with picking up items in 3D

Godot Version

4.4

Question

`Hello, I’m trying to make somethign similar to TES:Oblivion style item pickup mechanics. Currently, I’vve found out that you can do RayCast3d to detect whether you are looking at an item to pick it up.

At this point, all I want is for me to look at an item, click ‘Use’ and then make the item delete itself.

I’ve attached some photos of how I’ve set things up. I’d really appreciate help as I ahven’t really seen anything similar so far.

Below is my Character Script:

extends CharacterBody3D

@export var speed = 14
@export var walking_speed = 8
@export var sprint_speed = 20
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
@export var able_to_move = true
@export var jump_speed = 5
@export var walking = false
@export var sprinting = false
@onready var raycast = $Head/RayCast3D

func _ready():
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
	
func _physics_process(delta):
	
	if raycast.is_colliding() == true:
		if Input.is_action_just_pressed("Use") and raycast.get_collider().get_parent().is_in_group("items"):
			raycast.get_collider().get_parent().picked_up()
	
	#movement:
	velocity.y += -gravity * delta
	var input = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
	var movement_dir = transform.basis * Vector3(input.x, 0, input.y)
	if able_to_move == true:
		if walking == true:
			velocity.x = movement_dir.x * walking_speed
			velocity.z = movement_dir.z * walking_speed
		elif sprinting == true:
			velocity.x = movement_dir.x * sprint_speed
			velocity.z = movement_dir.z * sprint_speed
		else:
			velocity.x = movement_dir.x * speed
			velocity.z = movement_dir.z * speed
		if is_on_floor() and Input.is_action_just_pressed("jump"):
			velocity.y = jump_speed
		if Input.is_action_just_pressed("walk_run") and walking == false:
			walking = true
		else:
			if Input.is_action_just_pressed("walk_run") and walking == true:
				walking = false
				
	if able_to_move == false:
		velocity.x = 0
		velocity.z = 0
	move_and_slide()

func _input(event):
		
# hides and unhides the mouse when you press tab
	if event.is_action_pressed("sprint") and sprinting == false:
		sprinting = true
	else:
		sprinting = false

there is a picture of how I have my Character scene set up:

Below is the example weapon set up. Weapon is also in a global group named items.:

and the code on the weapon:

extends RigidBody3D

func picked_up():
	queue_free()

My only other idea is maybe the colision masks/laters are not right? hense why I can never get the if statement in the raycast section to give me anything. I’ve tried to print out when it activates, but it never does. Layer 1 - player, Layer 2 - items, Layer 3 - world.

Thanks!

It’s generally a good idea to put a print("got here") or the like inside things like your picked_up() function while you’re trying to figure out why they aren’t working; it will tell you whether the function is never being called, or if it’s being called and not doing what you wanted.

You can also do something like this in your player script:


func try_taking_object():
    if !Input.is_action_just_pressed("Use"):
        return

    if !raycast.is_colliding():
        return

    print("raycast collided")

    var coll: raycast.get_collider()
    var obj:  coll.get_parent()

    if !obj.is_in_group("items"):
        return

    print("object is in the item group")

    if !obj.has_method("picked_up"):
        print("WAT: Object has no picked_up() method!")
        return

    obj.picked_up()

func _physics_process(delta: float) -> void:
    try_taking_object()
    # code continues...

Exploding it out like that is temporary; once you have it working you want to get rid of all the telemetry and tighten it back up, but while you’re trying to figure out why it’s not working, having details on the decisions it makes can be very helpful.

1 Like

Hello, thanks for your advice. This worked further than were I was before but unfortunately I’m still stuck.

I’ve messed around with masks/layers and I believe that was the main reason it was coliding - the ray cast was inside my palyer and therefore not detecting anything else. I can how get the below printout:

Godot Engine v4.4.1.stable.official.49a5bc7b6 - https://godotengine.org
Vulkan 1.4.303 - Forward+ - Using Device #0: NVIDIA - NVIDIA GeForce RTX 3060

raycast colided
raycast colided
raycast colided
raycast colided

Here is the code that I wrote:

func pick_up():
	if !Input.is_action_just_pressed("Use"):
		return
		
	if !raycast.is_colliding():
		return
		
	print ("raycast colided")

	var coll = raycast.get_collider()
	var obj = coll.get_parent()
	
	if !obj.is_in_group("items"):
		return
		
	print ("this is an item")
	
	if !obj.has_method("picked_up"):
		print ("can't pick this up as no function to pick up!")
		return
		
	obj.picked_up()
	
func _physics_process(delta: float) -> void:
	pick_up()

Is the issue here the groups, as it doesn’t get passed that statement? I have them set up like the below on the main node of my weapon:

Thanks!

That warning light on the CollisionShape3D might be something to look at?

I’d try something like:

    if !obj.is_in_group("items"):
        print(str(obj) + " is not an item...")
        return

That should give you a hint as to the type of the object; I wouldn’t be surprised if obj turned out to be pointing at the wrong thing in the hierarchy.

1 Like

Hello, thanks for all the help so far.

I’ve found what the issue was:

	var obj = coll.get_parent()

From my understanding there is no need to get the parent of said weapon. The raycast already colides with the rigidbody and send sa signal to delete it.

Thanks for the advise for having print statements to check what is and isn’t working.

I’ll mark this as complete as I’ll post below my complete section of code, in case someone needs it.

func pick_up():
	if !Input.is_action_just_pressed("Use"):
		return
		
	if !raycast.is_colliding():
		return
		
	print ("raycast colided")

	var obj = raycast.get_collider()
	
	if !obj.is_in_group("items"):
		print (str(obj) + " is not an item")
		return
	print ("this is an item")
	if !obj.has_method("picked_up"):
		print ("can't pick this up as no function to pick up!")
		return
	print(obj.name)
	obj.picked_up()
	print (str(obj) + " is an item")
1 Like

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