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.
Por exemplo, você pode ter uma barra de vida na tela que representa a saúde do jogador. Quando o jogador sofre dano ou usa uma poção de cura, você deseja que a barra reflita a mudança. Para fazer isso, em Godot, você usaria sinais.
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¶
Para adicionar um botão ao nosso jogo, criaremos uma nova cena "principal" que incluirá um botão e a cena Sprite.tscn
que criamos em lições anteriores.
Crie uma nova cena acessando o menu Cena -> Nova cena.
No painel Cena, clique no botão Cena 2D. Isso adicionará um Node2D como nossa raiz.
No painel Arquivos, clique e arraste o arquivo Sprite.tscn
que você salvou anteriormente para o Node2D para instanciá-lo.
Queremos adicionar outro nó como irmão do Sprite. Para fazer isso, clique com o botão direito do mouse em Node2D e selecione Adicionar Nó Filho.
Pesquise o tipo de nó Button e adicione-o.
O nó é pequeno por padrão. Clique e arraste a alça inferior direita do Button na viewport para redimensioná-lo.
Se você não vir as alças, verifique se a ferramenta de seleção está ativa na barra de ferramentas.
Clique e arraste o botão para aproximá-lo do sprite.
Você também pode escrever um rótulo no Button editando sua propriedade Texto no Inspector. Digite "Alternar movimento".
Sua árvore da cena e o Viewport devem se parecer com isso.
Salve sua cena recém-criada. Você pode executá-lo com F6. No momento, o botão estará visível, mas nada acontecerá se você pressioná-lo.
Conectando um sinal no editor¶
Aqui, queremos conectar o sinal "pressionado"(pressed) do botão ao nosso Sprite e queremos chamar uma nova função que ativará e desativará seu movimento. Precisamos ter um script anexado ao nó Sprite, o que fizemos na lição anterior.
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.
O painel exibe uma lista de sinais disponíveis no nó selecionado.
Clique duas vezes no sinal "pressed" para abrir a janela de conexão do nó.
Nela, você pode conectar o sinal ao nó Sprite. O nó precisa de um método receptor, uma função que o Godot chamará quando o Button emitir o sinal. O editor gera um para você. Por convenção, chamamos esses métodos de callback de "_on_NodeName_signal_name". Aqui, será "_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.
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.
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.
Vamos substituir a linha com a palavra-chave pass
com o código que alternará o movimento do nó.
Nosso Sprite se move graças ao código na função _process()
. Godot fornece um método para ativar e desativar o processamento: Node.set_process(). Outro método da classe Node, is_processing()
, retorna true
se o processamento ocioso estiver ativo. Podemos usar a palavra-chave not
para inverter o valor.
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
O seu código ``Sprite.gd``completo deve se parecer com o seguinte.
extends Sprite
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.
No Espaço de Trabalho 3D, você pode trabalhar com malhas (meshes), luzes e projetar níveis para jogos 3D. Pressione Ctrl + F2 (ou Alt + 2 no macOS) para acessá-lo.
No painel da Cena, clique com o botão direito do mouse no nó Sprite e adicione um novo nó filho. Pesquise por Timer e adicione o nó correspondente. Sua cena agora deve ficar assim.
Com o nó Timer selecionado, vá para o Inspetor e marque a propriedade Autostart.
Clique no ícone de script ao lado de Sprite para voltar ao espaço de trabalho de script.
Precisamos fazer duas operações para conectar os nós via código:
Obter uma referência ao Timer do Sprite.
Chamar o método
connect()
do Timer.
Nota
Para se conectar a um sinal via código, você precisa chamar o método connect()
do nó que deseja escutar. Neste caso, queremos ouvir o sinal de "timeout" do Timer.
Queremos conectar o sinal quando a cena for instanciada, e podemos fazer isso usando a função interna Node._ready(), que é chamada automaticamente pelo mecanismo quando um nó é totalmente instanciado .
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")
A função get_node()
olha para os filhos do Sprite e obtém os nós pelo nome. Por exemplo, se você renomeou o nó Timer para "BlinkingTimer" no editor, você teria que mudar a chamada para get_node("BlinkingTimer")
.
Agora podemos conectar o Timer ao Sprite com a função _ready()
.
func _ready():
var timer = get_node("Timer")
timer.connect("timeout", self, "_on_Timer_timeout")
A linha fica assim: conectamos o sinal "timeout" do Timer ao nó ao qual o script está anexado (self
). Quando o Timer emitir "timeout", queremos chamar a função "_on_Timer_timeout", que precisamos definir. Vamos adicioná-lo na parte inferior do nosso script e usá-lo para alternar a visibilidade do nosso sprite.
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¶
Isso é tudo para a nossa pequena demonstração de ícone do Godot em movimento e piscando! Aqui está o arquivo Sprite.gd
completo para referência.
extends Sprite
var speed = 400
var angular_speed = PI
func _ready():
var timer = get_node("Timer")
timer.connect("timeout", self, "_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.
Para emitir um sinal em seus scripts, chame emit_signal()
.
func take_damage(amount):
health -= amount
if health <= 0:
emit_signal("health_depleted")
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)
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.
Para emitir valores junto com o sinal, adicione-os como argumentos extras à função emit_signal()
:
func take_damage(amount):
var old_health = health
health -= amount
emit_signal("health_changed", 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.