Reputation: 35
I am trying to create an FPS game in godot 4, and I run into an issue where the RayCast does not collide with a temporary object that would give the player ammo. When I try to interact, nothing occurs, and when I have checked, the RayCast is not colliding.
I tried changing the collision mask and the collision layer, neither have worked.
Here's the code for the player:
extends CharacterBody3D
var health = 100.0
var VC_REP = 0.0
var RB_REP = 0.0
var ammo = 30
var ammo_cap = 150
var mags = 5
var quest_title = "Operation: Viewpoint"
var quest_desc = "Infiltrate Outlook Alpha"
var speed
const SPRINT_SPEED = 8.0
const WALK_SPEED = 5.0
const JUMP_VELOCITY = 4.5
const SENSITIVITY = 0.003
const BOB_FREQ = 2.0
const BOB_AMP = 0.08
var t_bob = 0.0
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = 9.8
var bullet = load("res://bullet.tscn")
var instance
@onready var head = $Head
@onready var camera = $Head/Camera3D
@onready var gun_anim = $"../Player/Head/Camera3D/ar1/AnimationPlayer"
@onready var gunbarrel = $"../Player/Head/Camera3D/ar1/M4a1/RayCast3D"
@onready var interaction_ray = $Head/Camera3D/RayCast3D
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _unhandled_input(event):
if event is InputEventMouseMotion:
head.rotate_y(-event.relative.x * SENSITIVITY)
camera.rotate_x(-event.relative.y * SENSITIVITY)
camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-40), deg_to_rad(60))
func _physics_process(delta):
# Add the gravity.
if not is_on_floor():
velocity.y -= gravity * delta
# Handle jump.
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
if Input.is_action_pressed("sprint"):
speed = SPRINT_SPEED
else:
speed = WALK_SPEED
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var input_dir = Input.get_vector("left", "right", "up", "down")
var direction = (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if is_on_floor():
if direction:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = lerp(velocity.x, direction.x * speed, delta * 7.0)
velocity.z = lerp(velocity.z, direction.z * speed, delta * 7.0)
else:
velocity.x = lerp(velocity.x, direction.x * speed, delta * 3.0)
velocity.z = lerp(velocity.z, direction.z * speed, delta * 3.0)
if is_on_floor() and health == 0:
velocity.x = 0
velocity.z = 0
t_bob += delta * velocity.length() * float(is_on_floor())
camera.transform.origin = _headbob(t_bob)
if Input.is_action_pressed("shoot"):
if (!gun_anim.is_playing() and ammo != 0):
gun_anim.play("Shoot")
instance = bullet.instantiate()
instance.position = gunbarrel.global_position
instance.transform.basis = gunbarrel.global_transform.basis
get_parent().add_child(instance)
ammo -= 1
if (Input.is_action_just_pressed("reload") and ammo == 0 and mags != 0):
ammo += 30
mags -= 1
if interaction_ray.get_collider() != null and interaction_ray.get_collider().is_in_group("Ammo"):
print("Ammo")
print(ammo)
print(mags)
move_and_slide()
func _headbob(time) -> Vector3:
var pos = Vector3.ZERO
pos.y = sin(time * BOB_FREQ) * BOB_AMP
return pos
And here is the code for the temporary object.
extends MeshInstance3D
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func give_ammo(mags):
mags += 3
queue_free()
return mags
The RayCast is supposed to call the give_ammo()
function, and use mags
to call the variable.
Any help would be much appreciated, thank you in advance!
Upvotes: 1
Views: 1581
Reputation: 40285
RayCast3D
does physics queries. MeshInstance3D
is concerned with graphics.
These Node
s do not interact.
Create instead one of these:
Area3D
(and set RayCast3D
to collide areas and not bodies), and add CollisionShape3D
or CollisionPolygon3D
children. This would be my recommendation for collectible items.PhysicsBody3D
(e.g. StaticBody3D
), and add CollisionShape3D
or CollisionPolygon3D
children. This is the most common case.CSG*
and set their use_collision
to true
. Might not have the best performance, but sometimes works better.GridMap
might also work. It is overkill for this usecase.So the RayCast3D
can detect them.
I have an explanation of what kind of Node
to use for different use cases elsewhere.
You might still add the MeshInstance3D
as a child of them. In particular, since Area3D
and PhysicsBody3D
are only concerned with physics, they don't have a graphical representation by default.
You also want to make sure that the collision mask of the raycast overlaps the collision layer of whatever you want it to detect.
If you are still having problems with the detection, you can enable "Visible Collision Shapes" in the "Debug" menu, so Godot renders the colliders during testing, so you can see what is going on.
If you need more help setting up your physics objects, I'll refer you to: How do I detect collisions in Godot?.
Upvotes: 2