RED91
RED91

Reputation: 35

RayCast3d not colliding with object in godot 4.2.1

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

Answers (1)

Theraot
Theraot

Reputation: 40285

RayCast3D does physics queries. MeshInstance3D is concerned with graphics.

These Nodes 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

Related Questions