Projectiles don't react to environment and other bodies

Godot Version

v4.5.stable.mono.official [876b29033]

Question

The issue - projectile behaves as intended, until it encounters obstacle. It should turn itself off, eventually deal damage if encounters object that has HealthComponent and proper layer. Instead it tries to continue to move forever.

I wouldn’t be surprised if I just don’t understand layer/mask system, and I need someone to help me out here. I have project built with modular components, so I need to show everything, so please bear with me.

Physics layers:

  1. Environment (like terrain)
  2. Player
  3. Mobs
  4. Hazards
  5. Pickups
  6. Friendly Projectiles

Projectile - CharacterBody2D. Contains CollisionShape2D, DamageZoneComponent, and Area2D (DamageZoneComponent references it in code).
CharacterBody2D - layer 6, mask 1, 3.
Area2D - layer 6, mask 1, 3. (same as above)

Projectile code:

public partial class Projectile : CharacterBody2D
{
    [ExportSubgroup("Nodes")]
    [Export] private Area2D detectionZone;
    [ExportSubgroup("Settings")]
    [Export] private float speed = 200f;
    private int direction;

    public override void _Ready()
    {
        detectionZone.BodyEntered += OnBodyEntered;
    }

    public void Create(int direction)
    {
        this.direction = direction;
    }

    public override void _PhysicsProcess(double delta)
    {
        Velocity = new Vector2(direction * speed, Velocity.Y);
        MoveAndSlide();
    }

    private void OnBodyEntered(Node2D body)
    {
        QueueFree();
    }
}

DamageZoneComponent code:

public partial class DamageZoneComponent : Node
{
    [ExportSubgroup("Nodes")]
    [Export] private Area2D damageZone;
    [Export] private int damageValue;
    /// <summary>
    /// overrides damage value
    /// </summary>
    [Export] private bool instakill = false;

    public override void _Ready()
    {
        if (damageZone != null)
            damageZone.BodyEntered += OnBodyEntered;
    }

    public void OnBodyEntered(Node2D body)
    {
        var healthComponent = body.GetNodeOrNull<HealthComponent>("HealthComponent");
        if (healthComponent != null)
        {
            healthComponent.Damage(damageValue, instakill);
        }
    }
}

The only non-terrain obstacle that I have implemented in my dev level is an enemy (CharacterBody2D), who has CollisionShape2D and HealthComponent. CharacterBody2D of the mob is on layer 3, with mask 1. So it should work, because Projectile targets layer 3 (and 1).

Does the detectionZone have a shape that is larger than the CharacterBody2D’s?

And are you sure you need a CharacterBody2D for your projectile? If the detectionZone is supposed to QueueFree() the projectile upon hitting something, and it is masking the same collision layers, I am not sure what its CharacterBody2D should actually be able to collide with.

1 Like

CharacterBody2D has velocity, so controlling the projectile is very easy. Area2D (DetectionZone) has exactly the same shape as CollisionShape2D. If there is something wrong with it, it might explain problems when hitting physics bodies (like the mob), but it doesn’t explain issues with the terrain.

If they have the same shape and the same masks, the CharacterBody2D’s collision will prevent the Area2D from ever overlapping with other PhysicsBodies.

And if your terrain is no PhysicsBody, what is it? An area?

1 Like

Body entered signal doesn’t catch areas. And yeah, you shouldn’t be using character body for projectiles because move_and_slide call is rather expensive. If projectiles explode on touch sliding is not important anyway (in fact it’s unwanted) so at least use move_and_collide instead.

1 Like

:backhand_index_pointing_up: And you should disable the CharacterBody2D’s masks then, since they aren’t needed. (In addition to removing its CollisionShape.)

1 Like

Terrain is TileMapLayer.

It can ignore areas, since everything that it should collide with has CollisionShape2D. Except terrain, I don’t really know how terrain collision works.

If not CharacterBody2D, then what? Internet recommended CharacterBody2D since you can easily edit velocity.

Just plain area. Altering position according to velocity is literally one line of code.

Body entered should catch terrain though.

1 Like

With collisions? If it can collide with the terrain then it’s the same problem as with the enemy: The collision prevents the area from detecting the terrain.

1 Like

What’s the use of collision shape on the projectile anyway if hit is detected by a child area?

Drop the whole character body thing and make the projectile just a simple area.

1 Like

I have changed CharacterBody2D to Area2D, and removed the child Area2D. DamageZoneComponent now references parent Area2D.

I have changed _PhysicsProcess to compensate:

    public override void _PhysicsProcess(double delta)
    {
        Position += new Vector2(direction * speed, 0) * (float)delta;
    }

And it just works. Both terrain and mob. I haven’t even increased the Area2D shape. I also had no need to change layers or masks.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.