0 votes

I have two Area2Ds.

The first area, which I call DamageArea, is the child of the player's Kinematic Body, and deals with taking damage from enemies, enemy attacks, and hazards. Each of these types of objects have their own collision layers, which are masks for this Area. The Area itself is of course on the Player layer.

The other, which I call Sight, is the child of every enemy's Kinematic Body, and deals with detecting the player when the player enters this area. Effectively, it's the enemy's line of sight. It's on its own collision layer, "Uninteractables," which is a layer which should not interact with anything. It detects the player by having the player collision layer as its only collision mask.

Now, by the Godot API,

collision_layer: This describes the layers that the object appears in. By default, all bodies are on layer 1.

collision_mask: This describes what layers the body will scan for collisions. If an object isn’t in one of the mask layers, the body will ignore it. By default, all bodies scan layer 1.

By this logic, I thought the interaction between these two Area2D's would be that Sight detects whenever DamageArea enters Sight. DamageArea, meanwhile, would not detect Sight at all.

However, this isn't what I am observing.

Whenever DamageArea enters Sight, Sight does detect it, but it also causes the player to be damaged. But, DamageArea does not have the "Uninteractables" mask. Shouldn't this not be the case?

Here's relevant code.
First, for Sight:

extends Area2D

var sight;

onready var host = get_parent();

func _ready():
    connect("area_entered", self, "on_area_entered");
    connect("area_exited", self, "on_area_exited");
    pass

func on_area_entered(area):
    if(host.state == 'attack'):
        return;
    var other = area.get_parent();
    if(other.tag == "player"):
        host.target = other;
        host.state = 'chase';
    pass

func on_area_exited(area):
    if(host.state == 'attack'):
        return;
    var other = area.get_parent();
    if(other.tag == "player"):
        host.target = null;
        host.state = 'default';
    pass

Now, DamageArea:

extends Area2D

onready var host = get_parent();

func _ready():
    connect("area_entered", self, "on_area_entered");
    pass

func on_area_entered(area):
    #if(("sight" in area)):
    #   return;
    var object = area.get_parent();

    if(host.state != 'hurt'):
        host.velocity.x = 0;
        host.velocity.y = 0;
        host.states['hurt'].hurt_timer = 7;
        host.state = 'hurt';
        host.hp -= object.damage;
        if(object.position.x >= host.position.x):
            if(host.Direction != 1):
                host.scale.x = host.scale.x * -1;
            host.Direction = 1;
        else:
            if(host.Direction != -1):
                host.scale.x = host.scale.x * -1;
            host.Direction = -1;
    pass

Note the commented out segment

#if(("sight" in area)):
    #   return;

The code works perfectly fine with this included, as the Sight area is the only node in my project with "sight" as a variable, which confirms that this is indeed the issue. However, I'd rather fully understand how Areas work than implement this workaround. Is my original understanding of Area2D correct and I'm just missing something, or is this behavior intended?

Thanks in advance!

in Engine by (53 points)

1 Answer

+1 vote

As far as i know, you can't make a body "collide" with anotherone, without the other to be noticed about it. This would for example affect the result if the bodies involved in collision where rigidbodies.

In my opinion, you should not make the player receieve damage when a body enters to the DamageArea. Instead, i would add a function like get_damage(damage) to the DamageArea, and inside that function i would make player recieve damage. When a something that should damage the palyer detects a collission with something, it could ask if that body has the function get_damage, with body.has_method('get_damage') and if it does, it can call it with body.get_damage(damage) with damage set to the value you want.

At least, that is what i always do.

by (3,408 points)

It is supposed to detect it according to the official documentation:

The area’s physics layer(s). Collidable objects can exist in any of 32
different layers. A contact is detected if object A is in any of the
layers that object B scans, or object B is in any layers that object A
scans
.

https://docs.godotengine.org/en/latest/classes/class_area2d.html#class-area2d-property-collision-layer

However, it's counter-intuitive, and you should be able to have one Area2D detect another one without that one detecting the first. Unfortunately, you have to do the masking/filtering manually.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.