I am making a 2D game where the player can attack enemies using a designated Area2D node as an attack area. I set up signals to manage attack phases and ensure proper collision layers and masks

At first, it worked fine when I connected signals from the Godot editor itself. Then I realized that connecting the signals via scripting is way easier, since I'll be duplicating the same enemy many times. But now the player can't even interact with the enemy at all, and I don't know why?

Player Script: The player emits attack_started and attack_ended signals to manage attack phases, with the attack area's monitoring toggled accordingly.

extends Area2D

signal attack_started
signal attack_ended

export var speed = 300
var screen_size := Vector2()
var can_attack := true
var attack_cooldown := 0.5
var is_attacking := false
var swing_sound := preload("res://sound/whoosh-transitions-sfx-03-118230.wav")

func _ready():
    screen_size = get_viewport_rect().size
    $AnimatedSprite.connect("animation_finished", self, "_on_AnimatedSprite_animation_finished")
    $SwingSound.stream = swing_sound

func _process(delta):

func _on_AnimatedSprite_animation_finished():
    if $AnimatedSprite.animation == "attack":
        is_attacking = false
        can_attack = true

func handle_movement_input(delta):
    var direction := Vector2()
    # Movement input handling code...

func handle_attack_input():
    if Input.is_action_just_pressed("attack") and can_attack:
        print("Emitting attack_started")
        is_attacking = true
        can_attack = false
        yield(get_tree().create_timer(attack_cooldown), "timeout")
        print("Emitting attack_ended")

Enemy Script: The enemy listens for attack_started and attack_ended signals, to become vulnerable to attacks only during the player's attack animation.

extends Area2D

var vulnerable_to_attack := false
var health := 100

func _ready():

func connect_to_player_signals():
    var player = get_tree().get_root().find_node("Player", true, false)
    if player:
        player.connect("attack_started", self, "_on_attack_started")
        player.connect("attack_ended", self, "_on_attack_ended")

func _on_EnemySoldier_area_entered(area):
    if area.is_in_group("player_attacks") and vulnerable_to_attack:

func _on_attack_started():
    vulnerable_to_attack = true

func _on_attack_ended():
    vulnerable_to_attack = false

func take_damage(amount):
    health -= amount
    if health <= 0:

func die():

Attempts to Resolve the Issue:

  • Verified that collision layers and masks are correctly set for the player's attack area and the enemy.

  • Used print statements to confirm signal emission and reception.

  • Made sure that the attack area monitoring is set to true.

  • Made sure all the variable names are exactly correct including spaces and capitalization.

  • Made sure the nodes enter the scene in the right order.

  • Turned on collision shape visibility in-game via the debug editor.

I'm just not sure why it's not being detected. I checked everything from the signal connections, to the node order, to the names of everything, to the logic, to the collision shapes themselves actually being visible and intersecting via the debug editor, but I can't find the issue. I've been debugging it with ChatGPT 4 but not even it knows.

Help is appreciated :`)

Collision masks

  • \$\begingroup\$ You shuld be able to debug this. I would start by adding a print in _on_EnemySoldier_area_entered like this prints(vulnerable_to_attack). My hypothesis being that the area enter is happening before vulnerable_to_attack set to true. \$\endgroup\$
    – Theraot
    Commented Feb 20 at 17:17


You must log in to answer this question.

Browse other questions tagged .