`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:
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.
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.
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:
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:
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.
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")