Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Usando sinais

Nesta lição, veremos os sinais. São mensagens que os nós emitem quando algo específico acontece com eles, como um botão sendo pressionado. Outros nós podem se conectar a esse sinal e chamar uma função quando o evento ocorrer.

Os sinais são um mecanismo de delegação embutido no Godot que permite que um objeto do jogo reaja a uma mudança em outro sem que eles façam referência um ao outro. O uso de sinais limita o acoplamento e mantém seu código flexível.

For example, you might have a life bar on the screen that represents the player's health. When the player takes damage or uses a healing potion, you want the bar to reflect the change. To do so, in Godot, you would use signals.

Nota

Conforme mencionado na introdução, os sinais são a versão de Godot do padrão do observador. Você pode aprender mais sobre isso aqui: https://gameprogrammingpatterns.com/observer.html

Vamos agora usar um sinal para fazer nosso ícone Godot da lição anterior (Capturando os controles de entrada do jogador) se mover e parar ao pressionar um botão.

Configuração da cena

To add a button to our game, we will create a new main scene which will include both a Button and the sprite_2d.tscn scene we created in the Criando seu primeiro script lesson.

Crie uma nova cena acessando o menu Cena -> Nova cena.

../../_images/signals_01_new_scene.webp

In the Scene dock, click the 2D Scene button. This will add a Node2D as our root.

../../_images/signals_02_2d_scene.webp

In the FileSystem dock, click and drag the sprite_2d.tscn file you saved previously onto the Node2D to instantiate it.

../../_images/signals_03_dragging_scene.png

We want to add another node as a sibling of the Sprite2D. To do so, right-click on Node2D and select Add Child Node.

../../_images/signals_04_add_child_node.webp

Search for the Button node and add it.

../../_images/signals_05_add_button.webp

O nó é pequeno por padrão. Clique e arraste a alça inferior direita do Button na viewport para redimensioná-lo.

../../_images/signals_06_drag_button.png

Se você não vir as alças, verifique se a ferramenta de seleção está ativa na barra de ferramentas.

../../_images/signals_07_select_tool.webp

Clique e arraste o botão para aproximá-lo do sprite.

You can also write a label on the Button by editing its Text property in the Inspector. Enter Toggle motion.

../../_images/signals_08_toggle_motion_text.webp

Sua árvore da cena e o Viewport devem se parecer com isso.

../../_images/signals_09_scene_setup.png

Save your newly created scene as node_2d.tscn, if you haven't already. You can then run it with F6 (Cmd + R on macOS). At the moment, the button will be visible, but nothing will happen if you press it.

Conectando um sinal no editor

Here, we want to connect the Button's "pressed" signal to our Sprite2D, and we want to call a new function that will toggle its motion on and off. We need to have a script attached to the Sprite2D node, which we do from the previous lesson.

Você pode conectar sinais no painel do Nó. Selecione o nó Botão e, no lado direito do editor, clique na aba "Nó" ao lado do Inspetor.

../../_images/signals_10_node_dock.webp

O painel exibe uma lista de sinais disponíveis no nó selecionado.

../../_images/signals_11_pressed_signals.webp

Clique duas vezes no sinal "pressed" para abrir a janela de conexão do nó.

../../_images/signals_12_node_connection.png

There, you can connect the signal to the Sprite2D node. The node needs a receiver method, a function that Godot will call when the Button emits the signal. The editor generates one for you. By convention, we name these callback methods "_on_node_name_signal_name". Here, it'll be "_on_button_pressed".

Nota

Ao conectar sinais por meio do encaixe do nó do editor, você pode usar dois modos. O simples permite apenas conectar-se a nós que possuem um script anexado a eles e cria uma nova função de retorno de chamada neles.

../../_images/signals_advanced_connection_window.png

O avançado permite conectar-se a qualquer nó e qualquer função integrada, adicionar argumentos ao retorno de chamada e definir opções. Você pode alternar o modo no canto inferior esquerdo da janela clicando no botão Avançado.

Clique no botão Conectar para concluir a conexão do sinal e vá para a área de trabalho Script. Você deve ver o novo método com um ícone de conexão na margem esquerda.

../../_images/signals_13_signals_connection_icon.webp

Se você clicar no ícone, uma janela aparecerá e exibirá informações sobre a conexão. Este recurso está disponível apenas ao conectar nós no editor.

../../_images/signals_14_signals_connection_info.webp

Vamos substituir a linha com a palavra-chave pass com o código que alternará o movimento do nó.

Our Sprite2D moves thanks to code in the _process() function. Godot provides a method to toggle processing on and off: Node.set_process(). Another method of the Node class, is_processing(), returns true if idle processing is active. We can use the not keyword to invert the value.

func _on_button_pressed():
    set_process(not is_processing())

Esta função alternará o processamento e, por sua vez, o movimento do ícone ligado e desligado ao pressionar o botão.

Antes de testar o jogo, precisamos simplificar nossa função _process() para mover o nó automaticamente e não esperar pela entrada do usuário. Substitua-o pelo seguinte código, que vimos duas lições atrás:

func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

Your complete sprite_2d.gd code should look like the following.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())

Execute a cena agora e clique no botão para ver o sprite andar e parar.

Conectando um sinal via código

Você pode conectar sinais via código em vez de usar o editor. Isso é necessário quando você cria nós ou instancia cenas dentro de um script.

Vamos usar um nó diferente aqui. Godot tem um nó Timer que é útil para implementar o tempo de resfriamento de habilidades, recarga de armas e muito mais.

Head back to the 2D workspace. You can either click the "2D" text at the top of the window or press Ctrl + F1 (Ctrl + Cmd + 1 on macOS).

In the Scene dock, right-click on the Sprite2D node and add a new child node. Search for Timer and add the corresponding node. Your scene should now look like this.

../../_images/signals_15_scene_tree.png

With the Timer node selected, go to the Inspector and enable the Autostart property.

../../_images/signals_18_timer_autostart.png

Click the script icon next to Sprite2D to jump back to the scripting workspace.

../../_images/signals_16_click_script.png

Precisamos fazer duas operações para conectar os nós via código:

  1. Get a reference to the Timer from the Sprite2D.

  2. Call the connect() method on the Timer's "timeout" signal.

Nota

To connect to a signal via code, you need to call the connect() method of the signal you want to listen to. In this case, we want to listen to the Timer's "timeout" signal.

We want to connect the signal when the scene is instantiated, and we can do that using the Node._ready() built-in function, which is called automatically by the engine when a node is fully instantiated.

Para obter uma referência a um nó relativo ao atual, usamos o método Node.get_node(). Podemos armazenar a referência em uma variável.

func _ready():
    var timer = get_node("Timer")

The function get_node() looks at the Sprite2D's children and gets nodes by their name. For example, if you renamed the Timer node to "BlinkingTimer" in the editor, you would have to change the call to get_node("BlinkingTimer").

We can now connect the Timer to the Sprite2D in the _ready() function.

func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)

The line reads like so: we connect the Timer's "timeout" signal to the node to which the script is attached. When the Timer emits timeout, we want to call the function _on_timer_timeout(), that we need to define. Let's add it at the bottom of our script and use it to toggle our sprite's visibility.

Nota

By convention, we name these callback methods in GDScript as "_on_node_name_signal_name" and in C# as "OnNodeNameSignalName". Here, it'll be "_on_timer_timeout" for GDScript and OnTimerTimeout() for C#.

func _on_timer_timeout():
    visible = not visible

A propriedade visible é um booleano que controla a visibilidade do nosso nó. A linha visible = not visible alterna o valor. Se visible for true, torna-se false, e vice-versa.

Se você executar a cena agora, verá que o sprite liga e desliga, em intervalos de um segundo.

Script completo

That's it for our little moving and blinking Godot icon demo! Here is the complete sprite_2d.gd file for reference.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())


func _on_timer_timeout():
    visible = not visible

Sinais personalizados

Nota

Esta seção é uma referência sobre como definir e usar seus próprios sinais e não se baseia no projeto criado nas lições anteriores.

Você pode definir sinais personalizados em um script. Digamos, por exemplo, que você deseja mostrar uma tela de game over quando a saúde do jogador chegar a zero. Para fazer isso, você pode definir um sinal chamado "died" ou "health_depleted" quando a saúde chegar a 0.

extends Node2D

signal health_depleted

var health = 10

Nota

Como os sinais representam eventos que acabaram de ocorrer, geralmente usamos um verbo de ação no pretérito em seus nomes.

Seus sinais funcionam da mesma forma que os integrados: eles aparecem na guia Nó e você pode se conectar a eles como qualquer outro.

../../_images/signals_17_custom_signal.png

To emit a signal in your scripts, call emit() on the signal.

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

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

extends Node

signal health_changed(old_value, new_value)

var health = 10

Nota

Os argumentos de sinal aparecem na aba nó do editor e o Godot pode usá-los para gerar funções de retorno de chamada para você. No entanto, você ainda pode emitir qualquer número de argumentos ao emitir sinais. Então cabe a você emitir os valores corretos.

To emit values along with the signal, add them as extra arguments to the emit() function:

func take_damage(amount):
    var old_health = health
    health -= amount
    health_changed.emit(old_health, health)

Resumo

Qualquer nó em Godot emite sinais quando algo específico acontece com eles, como um botão sendo pressionado. Outros nós podem se conectar a sinais individuais e reagir a eventos selecionados.

Os sinais têm muitos usos. Com eles, você pode reagir a um nó entrando ou saindo do mundo do jogo, a uma colisão, a um personagem entrando ou saindo de uma área, a um elemento da interface que muda de tamanho e muito mais.

Por exemplo, um Area2D representando uma moeda emite um sinal body_entered sempre que o corpo físico do jogador entra em forma de colisão, permitindo que você saiba quando o jogador a coletou.

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