Raycast VS ShapeCast VS Area

Godot Version

4.3

Question

I’ve been working on implementing a combat system for a 3D game using a handful of tutorials as reference, and each seems to have a different approach to handling hitbox/hurtbox collision. Most I’ve seen recommend using an Area and it’s signals to determine when to do damage, but I’ve now also seen using a raycast, or a rayshape and checking if it is colliding with something in the physics process. Is there a recommended practice to handle this? Is one more performant over the other?

If it matters at all, I’m simply having the character perform a slash animation with the hitbox attached to the characters weapon via bone attachment. I have found that all approaches so far seem to have similar results. I also plan to have enemies damage the player on contact.

The best way is using ShapeCast, its easier than using area. Just setup it, adjust the layers/mask (make it like only enemy`s layer can enter), set the max results to 1. Then you can easily access the enemy:

if shape_cast.is_colliding():
    var body = shape_cast.get_collider(0)

Or if you want to access multi results, and you did not have any layers/masks setup, then do like this:

if shape_cast.is_colliding():
    for i in shape_cast.get_collision_count():
        var body = shape_cast.get_collider(i)
        if body.is_in_group("Enemy"): print("It`s Enemy, Kill Him!")

I appreciate your advice, I think I was leaning towards this solution too. I know that there are a lot of ways to do different things in engine, and I often get stuck considering the best way to do something instead of just getting it done.

On a related note, how do you handle updating health? Do you prefer to update the enemies health from the player class directly, or have the player class call a method from the enemy class that handles the update? It’s something I’ve also been wondering about since both work the same functionally. I store the health stat in a resource, so either way would be performing some sort of call to the resource class.

All of them work, but the best one depends of what you want to do and mostly your preferences:


Area2D

For general collision detection i think is best option, you can use the body_entered, body_exited / area_entered, area_exited signals to detect when the collision happens without need to check every physics frame, but if you want to check manually you can also use Area2D.get_overlapping_bodies() / Area2D.get_overlapping_areas().


RayCast2D

The RayCast2D has two advantages compared to areas: You can get the position where the collision happened and you can do more than one check in the same physics frame. “What you’re means with check more than one time in the same physics frame?” In Godot, the collisions are checked only one time per physics frame, so as example, if you need to check a collision in one position and in the same frame you need to check other position, in this case you can repositionate the raycast and use RayCast2D.force_raycast_update() to do a new physics test in the same frame. The negative side is that require more code than Area2D and also is more computationally expensive.


ShapeCast2D

Has the same advantages and disvantages of the RayCast2D, but is more expensive to use because check for more than a line.


If you’re using just one or two Ray/ShapeCasts this will not destroy your perfomance, that is more important when you have hundreds or thousands of Ray/ShapeCasts.

1 Like

I generally create a function called take_damage in the enemy script and call it when player press attack and enemy detector or shape cast is colliding. I also define a variable, can_attack which acts as a cooldown. I am just providing a tip, you can make the cooldown easily like this:

func attack():
    #Do Something
    can_attack = false
    await get_tree().create_timer(0.2).timeout
    can_attack = true

As a short explanation or example:

  1. Raycast is generally use as object detector which do picking objects. It also use to detect roof sometimes.
  2. Shapecast is generally use to check if enemy is entering player or player doing it to the enemy.
  3. Area is use to do something instantly, like your player fell into the void, it also has area, which emits signal to body entered. And you reset the player position.

This topic is for csharp :wink:

But what he said still valid, you just need to use the C# functions but the logic still the same.

Area is lighter than ShapeCast but doesn’t check if something is already inside. RayCast is also lightweight because it doesn’t involve any shape or area checks, just a single vector. You can cast a ray or shape directly from code without using a node and only call it when needed, rather than every frame.

Appreciate all the advice, I was hoping that there might be a definitive (even if just slightly arbitrary) best practice, but it seems that all of them have good tools for this. At the very least I can stop wondering if I’m making the wrong choice going with one over the others.

1 Like