Enforcing one to one interaction for Area2D

Godot Version

v4.6.stable.mono.official [89cea1439]

Question

In my project when enemy and player projectile (Area2D) hit each other, they both disappear. Which is happening as expected. Player projectile reacts with enemy projectile (player projectile monitorable property is set to false).

    private void OnAreaEntered(Area2D area)
    {
        HideProjectile();
        ProjectileHit?.Invoke();
    }

    private void HideProjectile()
    {
        Visible = false;
        SetDeferred("monitoring", false);
        SetDeferred("monitorable", false);
        SetPhysicsProcess(false);
    }

The issue I have is that I want this relation to be one to one. Meaning if player projectile hits enemy projectile, player projectile disappears and the first enemy projectile to be hit disappears. But the real outcome is that entire group of projectiles can disappear. This is the most noticeable in scenarios where single point spawns large amount of enemy projectiles.

I’ve already tried to set boolean flag in OnAreaEntered but the way those signals are queued doesn’t work out as I would want, and it still works the same with or without the flag. I’ve also tried to unsubscribe from the event in first line of OnAreaEntered but engine doesn’t like that and throws exceptions.

Maybe you can put the projectile-removing code on the player projectile. Then, in the physics process function (instead of using signals) remove the player projectile and the first overlapping area enemy projectile

1 Like

I’ve made initial test, it seems to be working (burst of projectiles that goes in all directions is no longer destroyed with single player projectile).

For future generations, it goes like this:

public override void _PhysicsProcess(double delta)
    {
         // you might want to stop physics process if projectile is not active
         foreach (var area in GetOverlappingAreas())
         {
             if (area is Projectile otherProjectile)
             {
                 HandleProjectileHit();
                 otherProjectile.HandleProjectileHit();
                 return;
             }
         }
    }

I need to rethink my current implementation, since I have single Projectile script, and ProjectileBasic and ProjectileSeeking that inherits it, and it’s shared between player and enemy, since there is no real difference in behavior, only different masks and layers. But now there is a difference. But this is something that I can figure out.

If someone has better ideas feel free to post them, but I think this is as good as it’s going to get.

1 Like

The issue with having the same logic for both player and enemy projectiles is that you need the projectiles to know if they’ve already been paired with another projectile to be removed.

I think the same issue would happen, but in reverse, if you had a lot of player projectiles and one enemy projectile. The player projectiles might all hit the one enemy projectile in the same frame and all be removed instead of just one.

1 Like

Currently I have removed all Area2D detection signals, and added a flag for player projectiles (they still share so much code that I don’t see the reason to make separate classes), and that flag is used for _PhysicsProcess to check for Area2D (meaning enemy projectiles).

Enemy projectiles don’t need to be aware of any other Area2D. Both player and enemy projectiles check for body collision through signals. For now it works as expected.

Is if because you have them a groupe and bullet kill that groupe because I’m writing a new godot that has groupes but still has diffrent object in that groupe if so I’ll upload it see if it help you out. I don’t know if that’s the problem you trying to fix. Let me know