Tutorial: Uso de Señales en Godot 4

1. Player recoge monedas → HUD suma puntaje

Escena:

  • Player (CharacterBody2D)

  • Coin (Area2D con CollisionShape2D)

  • HUD (Control con Label ScoreLabel)

Código de la moneda (coin.gd)

extends Area2D

signal coin_collected(points: int)

@export var points: int = 10

func _on_body_entered(body: Node) -> void:
    if body.is_in_group("player"):
        coin_collected.emit(points)
        queue_free()  # destruye la moneda

Código en el HUD (hud.gd)

extends Control

var score: int = 0
@onready var score_label: Label = $ScoreLabel

func add_points(points: int) -> void:
    score += points
    score_label.text = str(score)

Conexión de la señal (en Player o LevelManager)

func _ready():
    for coin in get_tree().get_nodes_in_group("coins"):
        coin.coin_collected.connect($HUD.add_points)

2. Player ataca → enemigo recibe daño

Escena:

  • Player con una Sword (Area2D)

  • Enemy (CharacterBody2D con script enemy.gd)

Código en la espada (sword.gd)

extends Area2D

signal hit_enemy(damage: int)
@export var damage: int = 25

func _on_body_entered(body: Node) -> void:
    if body.is_in_group("enemy"):
        hit_enemy.emit(damage)

Código en el enemigo (enemy.gd)

extends CharacterBody2D

var health: int = 100

func take_damage(amount: int) -> void:
    health -= amount
    print("Enemy HP:", health)
    if health <= 0:
        queue_free() # enemigo muere

Conexión

func _ready():
    $Player/Sword.hit_enemy.connect($Enemy.take_damage)

3. Player recibe daño → HUD actualiza vida

Escena:

  • Player (CharacterBody2D con vida)

  • EnemyAttack (Area2D)

  • HUD (Control con barra de vida HealthBar)

Código del player (player.gd)

extends CharacterBody2D

signal health_changed(new_health: int)

var max_health: int = 100
var health: int = 100

func take_damage(amount: int) -> void:
    health -= amount
    health = max(health, 0)
    health_changed.emit(health)
    if health <= 0:
        print("Game Over")

Código del HUD (hud.gd)

extends Control

@onready var health_bar: ProgressBar = $HealthBar

func update_health(value: int) -> void:
    health_bar.value = value

Conexión

func _ready():
    $Player.health_changed.connect($HUD.update_health)

Código del ataque enemigo (enemy_attack.gd)

extends Area2D
@export var damage: int = 20

func _on_body_entered(body: Node) -> void:
    if body.is_in_group("player"):
        body.take_damage(damage)

4. Player cruza puerta → cambiar de escena

Escena:

  • Door (Area2D con CollisionShape2D)

  • Player

Código en la puerta (door.gd)

extends Area2D

signal player_entered

@export var next_scene: String = "res://scenes/room2.tscn"

func _on_body_entered(body: Node) -> void:
    if body.is_in_group("player"):
        player_entered.emit()

Conexión para cambiar escena (ejemplo en LevelManager.gd)

extends Node

func _ready():
    $Door.player_entered.connect(change_scene)

func change_scene():
    var new_scene = load($Door.next_scene)
    get_tree().change_scene_to_packed(new_scene)

Conclusiones

  • Emitir señal: signal nombre(parametros) y luego emit(parametros).

  • Conectar señal: nodo.signal.connect(función_receptora).

  • Beneficio: Se evita acoplar directamente Player ↔ HUD, Enemy ↔ Player, etc.

0
Subscribe to my newsletter

Read articles from Jorge Leonardo Cespedes Tapia directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jorge Leonardo Cespedes Tapia
Jorge Leonardo Cespedes Tapia

I software engineer. Developer Python. I read books. I watch movies. I writer fiction. I am a black cat. And You?