Sinais

Introdução

Sinais são a versão do Godot do padrão observador. Eles permitem que um nó envie uma mensagem que outros nós podem captar e responder. Por exemplo, ao invés de continuamente verificar um botão para saber se ele está sendo pressionado, o botão pode emitir um sinal quando for pressionado.

Nota

Você pode ler mais sobre o padrão observador aqui: http://gameprogrammingpatterns.com/observer.html

Sinais são um meio de desacoplar seus objetos de jogo, o que leva a um código mais organizado e gerenciável. Ao invés de forçar objetos de jogo a esperar que outros objetos estejam sempre presentes, eles podem emitir sinais para os quais qualquer objeto interessado pode inscrever-se e responder.

Abaixo, você pode ver alguns exemplos de como você pode usar sinais em seus próprios projetos.

Alguns exemplos

Para ver como sinais funcionam, vamos tentar usar um nó Timer. Crie uma nova cena com um Node2D e dois filhos: um Timer e um Sprite. No painel de cenas, renomeie o Node2D para TimerExample.

Para a textura do Sprite, você pode usar o ícone Godot, ou qualquer outra imagem que desejar. Que é feito selecionando Load no menu suspenso do atributo Textura do Sprite. Anexe um script ao nó raiz, mas não adicione nenhum código ainda.

Sua árvore da cena deveria se parecer assim:

../../_images/signals_node_setup.png

Nas propriedades do nó Timer, marque a caixa "On" próximo a Autostart. Isso fará com que o Timer inicie automaticamente quando você executar a cena. Você pode deixar o Wait Time em 1 segundo.

Próximo à aba "Inspector" há uma aba chamada "Nó". Clique nessa aba e você verá todos os sinais que o nó selecionado pode emitir. No caso do nó Timer, o que nos importa é o "timeout". Este sinal é emitido sempre que o Timer chegar à 0.

../../_images/signals_node_tab_timer.png

Clique no sinal "timeout()" e clique em "Conectar...". Você vai ver a seguinte janela, onde você pode definir como você quer conectar o sinal:

../../_images/signals_connect_dialog_timer.png

Do lado esquerdo, você verá os nós na sua cena e pode selecionar o nó que você quer que "escute" para o sinal. Note que o nó Timer está azul, isto é uma indicação visual de que é esse o nó o qual está emitindo o sinal. Selecione o nó raiz.

Aviso

O nó alvo precisa ter um script anexado ou você vai receber uma mensagem de erro.

Se você ativar o Menu Avançado, verá no lado direito que você pode vincular um número arbitrário de argumentos (possíveis) de tipos diferentes. Isso pode ser útil quando você tem mais de um sinal conectado ao mesmo método, pois cada propagação de sinal resultará em valores diferentes para esses argumentos de chamada extras.

Em baixo da janela há um campo rotulado "Método Receptor". Esse é o nome da função no script do nó alvo que você quer usar. Por padrão, Godot vai criar essa função usando a convenção de nomenclatura _on_<node_name>_<signal_name> mas você pode mudá-lo se desejar.

Clique em "Conectar" e você verá que a função foi criada no script:

extends Node2D


func _on_Timer_timeout():
    pass # Replace with function body.
public class TimerExample : Node2D
{
    public void _on_Timer_timeout()
    {
        // Replace with function body.
    }
}

Agora podemos substituir o código placeholder com qualquer código que deseja executar quando o sinal for recebido. Vamos fazer o Sprite piscar:

extends Node2D


func _on_Timer_timeout():
    # Note: the `$` operator is a shorthand for `get_node()`,
    # so `$Sprite` is equivalent to `get_node("Sprite")`.
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
    public void _on_Timer_timeout()
    {
        var sprite = GetNode<Sprite>("Sprite");
        sprite.Visible = !sprite.Visible;
    }
}

Execute a cena e você verá o Sprite ligando e desligando a cada segundo. Você pode mudar a propriedade Wait Time do temporizador para alterar isso.

Conectando sinais por código

Você também pode fazer a conexão do sinal em código ao invés do editor. Isso é geralmente necessário quando você está instanciando nós via código e por isso não pode usar o editor para fazer a conexão.

Primeiro, desconecte o sinal selecionando a conexão na aba "Nó" do timer e clicando em desconectar.

../../_images/signals_disconnect_timer.png

Para fazer a conexão por código, nós podemos usar a função connect. Nós iremos colocá-la em _ready() para que a conexão sejá feita ao rodar. A sintaxe da função é <source_node>.connect(<signal_name>, <target_node>, <target_function_name>). Aqui está o código para nossa conexão com o Temporizador:

extends Node2D


func _ready():
    $Timer.connect("timeout", self, "_on_Timer_timeout")


func _on_Timer_timeout():
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
    public override void _Ready()
    {
        GetNode("Timer").Connect("timeout", this, nameof(_on_Timer_timeout));
    }

    public void _on_Timer_timeout()
    {
        var sprite = GetNode<Sprite>("Sprite");
        sprite.Visible = !sprite.Visible;
    }
}

Sinais personalizados

Você também pode declarar seus próprios sinais no Godot:

extends Node2D


signal my_signal
public class Main : Node2D
{
    [Signal]
    public delegate void MySignal();
}

Quando declarado, seus sinais personalizados aparecerão no Inspetor e podem ser conectados em um nó da mesma forma que os sinais predefinidos.

Para emitir um sinal por código, use a função emit_signal :

extends Node2D


signal my_signal


func _ready():
    emit_signal("my_signal")
public class Main : Node2D
{
    [Signal]
    public delegate void MySignal();

    public override void _Ready()
    {
        EmitSignal(nameof(MySignal));
    }
}

Um sinal também pode opcionalmente declarar um ou mais argumentos. Especifique os nomes dos argumentos entre parênteses:

extends Node


signal my_signal(value, other_value)
public class Main : Node
{
    [Signal]
    public delegate void MySignal(bool value, int other_value);
}

Nota

Estes argumentos aparecem na aba Nó do editor, e a Godot pode usá-los para criar funções para você. Entretanto, você ainda pode emitir qualquer número de argumento quando você emite sinais; é você quem tem que emitir os valores corretos.

Passa passar valores, adiciones-os como o segundo argumento à função emit_signal:

extends Node


signal my_signal(value, other_value)


func _ready():
    emit_signal("my_signal", true, 42)
public class Main : Node
{
    [Signal]
    public delegate void MySignal(bool value, int other_value);

    public override void _Ready()
    {
        EmitSignal(nameof(MySignal), true, 42);
    }
}

Conclusão

No Godot, muitos dos nós embutidos proporcionam sinais que você pode usar para detectar eventos. Por exemplo, um Area2D <class_Area2D>`representando uma moeda emite um sinal ``body_entered` sempre quando o corpo físico do jogador entra na forma de colisão, permitindo você saber quando o jogador coletou ela.

Na próxima seção, Seu primeiro jogo, você criará um jogo completo contendo vários usos de sinais para conectar diferentes componentes do jogo.