Search code examples
godotraycastinggdscript

Is there an event for when a raycast hits an object?


Is there an event for when a Raycast collides with an object? I've been looking all over the internet for it, but there's just no results. Please help!


Solution

  • There isn't. You only need to look at the official documentation on RayCast (or RayCast2D for that matter) to see that they don't define any signals.

    What we do is check for is_colliding on the physics frame (i.e. _physics_process).

    If you prefer a signal, we can do it by attaching an script to it. Something like this:

    extends RayCast
    
    
    signal collided(collider)
    
    
    var last_collider:Object
    
    
    func _physics_process(_delta:float) -> void:
        if not is_colliding():
            last_collider = null
            return
    
        var found_collider:Object = get_collider()
        if found_collider != last_collider:
            last_collider = found_collider
            emit_signal("collided", found_collider)
    

    You could even package it in an EditorPuglin, so it appears as an option when adding a Node in the editor. See Making plugins.


    On the other hand, some people prefer to move away from the editor and do things from code. For that, you can always create the RayCast add it to the scene with add_child, and set its properties from code. Remember to call force_update_transform and force_raycast_update as necesary. Or even use intersect_ray (e.g. get_world().direct_space_state.intersect_ray(start, end)) which is a physics query that you can do at any moment, and does not require allocating a Node.


    Addendum

    what if I want to call a function whenever the raycast STOPS colliding with an object?

    We can use similar code to accommodate more signals. I'll overdo it for show. Remove what you don't want:

    extends RayCast
    
    
    signal entered(new_collider)
    signal exited(old_collider)
    signal collision_begin(new_collider)
    signal collision_stop(old_collider)
    signal collision_change(old_collider, new_collider)
    
    
    var old_collider:Object
    
    
    func _physics_process(_delta:float) -> void:
        var new_collider:Object = null
        if is_colliding():
            new_collider = get_collider()
    
        if new_collider == old_collider:
            return
    
        if old_collider == null:
            emit_signal("collision_begin", new_collider)
            emit_signal("entered", new_collider)
        elif new_collider == null:
            emit_signal("collision_stop", old_collider)
            emit_signal("exited", old_collider)
        else:
            emit_signal("collision_change", old_collider, new_collider)
            emit_signal("exited", old_collider)
            emit_signal("entered", new_collider)
    
        old_collider = new_collider
    

    Here "entered" behave like "collided" from the prior example.

    If you want a signal that happen when a particular object is no longer colliding, that is "exited". If you want a signal when no object is colliding at all, that is "collision_stop". I couldn't tell which case are you interested in.