Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Sinais em C#
Para uma explicação detalhada de sinais em geral, veja a seção Usando sinais no tutorial passo a passo.
Os sinais são implementados usando eventos do C#, a maneira idiomática de representar o padrão observer no C#. Esta é a maneira recomendada de usar sinais no C# e o foco desta página.
Em alguns casos, é necessário usar as APIs mais antigas Connect() e Disconnect(). Veja Usando Connect e Disconnect para mais detalhes.
Se você encontrar uma exceção System.ObjectDisposedException ao manipular um sinal, pode ser que esteja faltando a desconexão de um sinal. Veja Desconexão automática quando o receptor é liberado (freed) para mais detalhes.
Sinais como eventos C#
Para fornecer maior segurança de tipos (type-safety), os sinais do Godot também estão todos disponíveis por meio de eventos. Você pode manipular esses eventos, como qualquer outro evento, com os operadores += e -=.
Timer myTimer = GetNode<Timer>("Timer");
myTimer.Timeout += () => GD.Print("Timeout!");
Além disso, você sempre pode acessar os nomes dos sinais associados a um tipo de nó por meio de sua classe aninhada SignalName. Isso é útil quando, por exemplo, você deseja aguardar (await) por um sinal (veja palavra-chave await).
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
Sinais personalizados como eventos C#
Para declarar um evento personalizado em seu script C#, use o atributo [Signal] em um tipo de delegate público. Observe que o nome deste delegate precisa terminar com EventHandler.
[Signal]
public delegate void MySignalEventHandler();
[Signal]
public delegate void MySignalWithArgumentEventHandler(string myString);
Uma vez feito isso, o Godot criará automaticamente os eventos apropriados nos bastidores. Você poderá então usar os referidos eventos da mesma forma que faria com qualquer outro sinal do Godot. Observe que os eventos são nomeados usando o nome do seu delegate menos a parte final EventHandler.
public override void _Ready()
{
MySignal += () => GD.Print("Hello!");
MySignalWithArgument += SayHelloTo;
}
private void SayHelloTo(string name)
{
GD.Print($"Hello {name}!");
}
Aviso
Se você quiser se conectar a esses sinais no editor, precisará compilar (rebuild) o projeto para que eles apareçam.
Você pode clicar no botão Build no canto superior direito do editor para fazer isso.
Emissão de sinal
Para emitir sinais, use o método EmitSignal. Observe que, assim como nos sinais definidos pelo motor, os nomes dos seus sinais personalizados estão listados na classe aninhada SignalName.
public void MyMethodEmittingSignals()
{
EmitSignal(SignalName.MySignal);
EmitSignal(SignalName.MySignalWithArgument, "World");
}
Em contraste com outros eventos do C#, você não pode usar Invoke para disparar eventos vinculados a sinais do Godot.
Os sinais suportam argumentos de qualquer tipo compatível com Variant.
Consequentemente, qualquer Node ou RefCounted será compatível automaticamente, mas objetos de dados personalizados precisarão herdar de GodotObject ou de uma de suas subclasses.
using Godot;
public partial class DataObject : GodotObject
{
public string MyFirstString { get; set; }
public string MySecondString { get; set; }
}
Valores vinculados
Às vezes, você vai querer vincular valores a um sinal quando a conexão for estabelecida, em vez de (ou além de) quando o sinal for emitido. Para fazer isso, você pode usar uma função anônima, como no exemplo a seguir.
Aqui, o sinal Button.Pressed não recebe nenhum argumento. Mas queremos usar o mesmo método ModifyValue tanto para o botão "mais" quanto para o botão "menos". Portanto, vinculamos o valor modificador no momento em que estamos conectando os sinais.
public int Value { get; private set; } = 1;
public override void _Ready()
{
Button plusButton = GetNode<Button>("PlusButton");
plusButton.Pressed += () => ModifyValue(1);
Button minusButton = GetNode<Button>("MinusButton");
minusButton.Pressed += () => ModifyValue(-1);
}
private void ModifyValue(int modifier)
{
Value += modifier;
}
Criação de sinais em tempo de execução
Por fim, você pode criar sinais personalizados diretamente enquanto seu jogo está rodando. Use o método AddUserSignal para isso. Esteja ciente de que ele deve ser executado antes de qualquer uso dos referidos sinais (seja conectando-se a eles ou emitindo-os). Além disso, note que os sinais criados dessa forma não estarão visíveis na classe aninhada SignalName.
public override void _Ready()
{
AddUserSignal("MyCustomSignal");
EmitSignal("MyCustomSignal");
}
Usando Connect e Disconnect
Em geral, não é recomendado usar Connect() e Disconnect(). Essas APIs não oferecem tanta segurança de tipos quanto os eventos. No entanto, elas são necessárias para conectar-se a sinais definidos por GDScript e para passar ConnectFlags.
No exemplo a seguir, pressionar o botão pela primeira vez imprime Greetings!. O sinalizador OneShot desconecta o sinal, portanto, pressionar o botão novamente não fará nada.
public override void _Ready()
{
Button button = GetNode<Button>("GreetButton");
button.Connect(Button.SignalName.Pressed, Callable.From(OnButtonPressed), (uint)GodotObject.ConnectFlags.OneShot);
}
public void OnButtonPressed()
{
GD.Print("Greetings!");
}
Desconexão automática quando o receptor é liberado (freed)
Normalmente, quando qualquer GodotObject é liberado (como qualquer Node), o Godot desconecta automaticamente todas as conexões associadas a esse objeto. Isso acontece tanto para os emissores de sinais quanto para os receptores de sinais.
Por exemplo, um nó com este código imprimirá "Hello!" quando o botão for pressionado e, em seguida, se liberará. Liberar o nó desconecta o sinal, de modo que pressionar o botão novamente não faz nada:
public override void _Ready()
{
Button myButton = GetNode<Button>("../MyButton");
myButton.Pressed += SayHello;
}
private void SayHello()
{
GD.Print("Hello!");
Free();
}
Quando um receptor de sinal é liberado enquanto o emissor do sinal ainda está vivo, em alguns casos a desconexão automática não acontecerá:
O sinal está conectado a uma expressão lambda que captura uma variável.
O sinal é um sinal personalizado.
As seções seguintes explicam esses casos em mais detalhes e incluem sugestões de como desconectar manualmente.
Nota
A desconexão automática é totalmente confiável se um emissor de sinal for liberado antes que qualquer um de seus receptores seja liberado. Com um estilo de projeto que prefira esse padrão, as limitações acima podem não ser uma preocupação.
Sem desconexão automática: uma expressão lambda que captura uma variável
Se você se conectar a uma expressão lambda que captura variáveis, o Godot não conseguirá identificar que a lambda está associada à instância que a criou. Isso faz com que este exemplo tenha um comportamento potencialmente inesperado:
Timer myTimer = GetNode<Timer>("../Timer");
int x = 0;
myTimer.Timeout += () =>
{
x++; // This lambda expression captures x.
GD.Print($"Tick {x} my name is {Name}");
if (x == 3)
{
GD.Print("Time's up!");
Free();
}
};
Tick 1, my name is ExampleNode
Tick 2, my name is ExampleNode
Tick 3, my name is ExampleNode
Time's up!
[...] System.ObjectDisposedException: Cannot access a disposed object.
No tick 4, a expressão lambda tenta acessar a propriedade Name do nó, mas o nó já foi liberado. Isso causa a exceção.
Para desconectar, mantenha uma referência ao delegate criado pela expressão lambda e passe-o para o operador -=. Por exemplo, este nó se conecta e desconecta usando os métodos de ciclo de vida _EnterTree e _ExitTree:
[Export]
public Timer MyTimer { get; set; }
private Action _tick;
public override void _EnterTree()
{
int x = 0;
_tick = () =>
{
x++;
GD.Print($"Tick {x} my name is {Name}");
if (x == 3)
{
GD.Print("Time's up!");
Free();
}
};
MyTimer.Timeout += _tick;
}
public override void _ExitTree()
{
MyTimer.Timeout -= _tick;
}
Neste exemplo, Free faz com que o nó saia da árvore, o que chama _ExitTree. O método _ExitTree desconecta o sinal, de modo que _tick nunca mais será chamado.
Os métodos de ciclo de vida a serem usados dependem do que o nó faz. Outra opção é conectar-se aos sinais no _Ready e desconectar no Dispose.
Nota
O Godot usa a propriedade Delegate.Target para determinar com qual instância um delegate está associado. Quando uma expressão lambda não captura uma variável, o Target do delegate gerado é a instância que criou o delegate. Quando uma variável é capturada, o Target aponta para um tipo gerado que armazena a variável capturada. É isso que quebra a associação. Se você quiser ver se um delegate será limpo automaticamente, tente verificar o seu Target.
O método Callable.From não afeta o Delegate.Target, portanto, conectar uma lambda que captura variáveis usando o método Connect não funciona melhor do que usar +=.
Sem desconexão automática: um sinal personalizado
A conexão a um sinal personalizado usando o operador += não se desconecta automaticamente quando o nó receptor é liberado.
Para desconectar, use o operador -= em um momento apropriado. Por exemplo:
[Export]
public MyClass Target { get; set; }
public override void _EnterTree()
{
Target.MySignal += OnMySignal;
}
public override void _ExitTree()
{
Target.MySignal -= OnMySignal;
}
Outra solução é usar o método Connect, que se desconecta automaticamente com sinais personalizados:
[Export]
public MyClass Target { get; set; }
public override void _EnterTree()
{
Target.Connect(MyClass.SignalName.MySignal, Callable.From(OnMySignal));
}