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...
Notificações em Godot
Todo Object na Godot implementa um método _notification. Seu propósito é permitir que o Object responda a uma variedade de retornos de chamada (callbacks) em nível de engine que possam se relacionar a ele. Por exemplo, se a engine diz a um CanvasItem para "desenhar" (draw), ela chamará _notification(NOTIFICATION_DRAW).
Algumas destas notificações, como desenhar, são úteis para sobrepor em scripts. Tanto que o Godot expões muitas delas com funções dedicadas:
_ready():NOTIFICATION_READY_enter_tree():NOTIFICATION_ENTER_TREE_exit_tree():NOTIFICATION_EXIT_TREE_process(delta):NOTIFICATION_PROCESS_physics_process(delta):NOTIFICATION_PHYSICS_PROCESS_draw():NOTIFICATION_DRAW
O que os usuários talvez não percebam é que notificações existem para tipos além de Node. Por exemplo:
Object::NOTIFICATION_POSTINITIALIZE: uma chamada de retorno que aciona durante a inicialização do objeto. Não acessível a scripts.
Object::NOTIFICATION_PREDELETE: um callback acionado antes que a engine exclua um Object, ou seja, um "destrutor".
E muitas das chamadas de retorno que existem em Nós não têm métodos dedicados mas ainda são bastante úteis.
Node::NOTIFICATION_PARENTED: um callback acionado sempre que você adiciona um nó filho a outro nó.
Node::NOTIFICATION_UNPARENTED: um callback acionado sempre que você remove um nó filho de outro nó.
O método universal _notification() fornece acesso a todas essas notificações personalizadas.
Nota
Os métodos na documentação rotulados como "virtual" também são destinados a serem substituídos por scripts.
A clássico exemplo é o método _init em Object. Embora ele não tenha um equivalente NOTIFICATION_*, a engine ainda chama o método. A maioria das linguagens (exceto C#) depende dele como um construtor.
Então, quando você deve usar cada uma dessas notificações ou funções virtuais?
_process vs. _physics_process vs. *_input
Use _process() quando precisar de um delta de tempo dependente da taxa de quadros entre frames. Se o código que atualiza os dados do objeto precisa ser atualizado o mais frequentemente possível, este é o local adequado. Verificações lógicas recorrentes e cache de dados costumam ser executados aqui, mas tudo depende da frequência necessária para as avaliações. Se não precisarem ser executadas a cada frame, implementar um loop baseado em timeout de Timer também é uma opção.
# Allows for recurring operations that don't trigger script logic
# every frame (or even every fixed frame).
func _ready():
var timer = Timer.new()
timer.autostart = true
timer.wait_time = 0.5
add_child(timer)
timer.timeout.connect(func():
print("This block runs every 0.5 seconds")
)
using Godot;
public partial class MyNode : Node
{
// Allows for recurring operations that don't trigger script logic
// every frame (or even every fixed frame).
public override void _Ready()
{
var timer = new Timer();
timer.Autostart = true;
timer.WaitTime = 0.5;
AddChild(timer);
timer.Timeout += () => GD.Print("This block runs every 0.5 seconds");
}
}
using namespace godot;
class MyNode : public Node {
GDCLASS(MyNode, Node)
public:
// Allows for recurring operations that don't trigger script logic
// every frame (or even every fixed frame).
virtual void _ready() override {
Timer *timer = memnew(Timer);
timer->set_autostart(true);
timer->set_wait_time(0.5);
add_child(timer);
timer->connect("timeout", callable_mp(this, &MyNode::run));
}
void run() {
UtilityFunctions::print("This block runs every 0.5 seconds.");
}
};
Use _physics_process() quando precisar de um delta de tempo independente da taxa de quadros entre frames. Se o código precisa de atualizações consistentes ao longo do tempo, independentemente da velocidade de execução, este é o local adequado. Operações recorrentes envolvendo cinemática e transformações de objetos devem ser executadas aqui.
Embora seja possível, para obter o melhor desempenho você deve evitar verificações de entrada nesses callbacks. _process() e _physics_process() serão executados em todas as oportunidades (não entram em repouso por padrão). Em contraste, os callbacks *_input() serão executados apenas nos frames em que a engine realmente detectar entrada.
Você pode verificar ações de entrada dentro dos callbacks de entrada da mesma forma. Se precisar de delta de tempo, pode obtê-lo através dos métodos correspondentes.
# Called every frame, even when the engine detects no input.
func _process(delta):
if Input.is_action_just_pressed("ui_select"):
print(delta)
# Called during every input event.
func _unhandled_input(event):
match event.get_class():
"InputEventKey":
if Input.is_action_just_pressed("ui_accept"):
print(get_process_delta_time())
using Godot;
public partial class MyNode : Node
{
// Called every frame, even when the engine detects no input.
public void _Process(double delta)
{
if (Input.IsActionJustPressed("ui_select"))
{
GD.Print(delta);
}
}
// Called during every input event. Equally true for _input().
public void _UnhandledInput(InputEvent @event)
{
switch (@event)
{
case InputEventKey:
if (Input.IsActionJustPressed("ui_accept"))
{
GD.Print(GetProcessDeltaTime());
}
break;
}
}
}
using namespace godot;
class MyNode : public Node {
GDCLASS(MyNode, Node)
public:
// Called every frame, even when the engine detects no input.
virtual void _process(double p_delta) override {
if (Input::get_singleton->is_action_just_pressed("ui_select")) {
UtilityFunctions::print(p_delta);
}
}
// Called during every input event. Equally true for _input().
virtual void _unhandled_input(const Ref<InputEvent> &p_event) override {
Ref<InputEventKey> key_event = event;
if (key_event.is_valid() && Input::get_singleton->is_action_just_pressed("ui_accept")) {
UtilityFunctions::print(get_process_delta_time());
}
}
};
_init vs. initialization vs. export
Se o script inicializa sua própria subárvore de nós, sem uma cena, esse código deve ser executado em _init(). Outras inicializações de propriedades ou independentes da SceneTree também devem rodar aqui.
Nota
O equivalente em C# ao método _init() do GDScript é o construtor.
O _init() é disparado antes de _enter_tree() ou _ready(), mas depois que um script cria e inicializa suas propriedades. Ao instanciar uma cena, os valores das propriedades serão configurados de acordo com a seguinte sequência:
Atribuição do valor inicial: a propriedade recebe seu valor de inicialização ou o valor padrão caso nenhum seja especificado. Se existir um setter, ele não será utilizado.
_init()atribuição: o valor da propriedade é substituído por quaisquer atribuições feitas em_init(), disparando o setter.Atribuição de valor exportado: o valor de uma propriedade exportada é novamente substituído por qualquer valor definido no Inspetor, acionando o setter.
# test is initialized to "one", without triggering the setter.
@export var test: String = "one":
set(value):
test = value + "!"
func _init():
# Triggers the setter, changing test's value from "one" to "two!".
test = "two"
# If you set test to "three" from the Inspector, it would trigger
# the setter, changing test's value from "two!" to "three!".
using Godot;
public partial class MyNode : Node
{
private string _test = "one";
[Export]
public string Test
{
get { return _test; }
set { _test = $"{value}!"; }
}
public MyNode()
{
// Triggers the setter, changing _test's value from "one" to "two!".
Test = "two";
}
// If you set Test to "three" in the Inspector, it would trigger
// the setter, changing _test's value from "two!" to "three!".
}
using namespace godot;
class MyNode : public Node {
GDCLASS(MyNode, Node)
String test = "one";
protected:
static void _bind_methods() {
ClassDB::bind_method(D_METHOD("get_test"), &MyNode::get_test);
ClassDB::bind_method(D_METHOD("set_test", "test"), &MyNode::set_test);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "test"), "set_test", "get_test");
}
public:
String get_test() { return test; }
void set_test(String p_test) { test = p_test + "!"; }
MyNode() {
// Triggers the setter, changing _test's value from "one" to "two!".
set_test("two");
}
// If you set test to "three" in the Inspector, it would trigger
// the setter, changing test's value from "two!" to "three!".
};
Como resultado, instanciar um script versus uma cena pode afetar tanto a inicialização quanto o número de vezes que a engine chama o setter.
_ready vs. _enter_tree vs. NOTIFICATION_PARENTED
Ao instanciar uma cena conectada à primeira cena executada, o Godot instanciará os nós descendo pela árvore (realizando chamadas a _init()) e construirá a árvore de cima para baixo a partir da raiz. Isso faz com que as chamadas a _enter_tree() se propaguem pela árvore. Quando a árvore estiver completa, os nós folha chamarão _ready. Um nó chamará esse método apenas quando todos os seus nós filhos tiverem terminado de chamar os seus. Isso então gera uma propagação inversa, subindo de volta até a raiz da árvore.
Ao instanciar um script ou uma cena autônoma, os nós não são adicionados à SceneTree logo na criação, então nenhum callback de _enter_tree() é disparado. Em vez disso, apenas a chamada de _init() ocorre. Quando a cena é adicionada à SceneTree, as chamadas de _enter_tree() e _ready() ocorrem.
Se precisar acionar um comportamento que ocorra quando nós forem associados a outro nó, independentemente de isso ocorrer dentro da cena principal/ativa ou não, você pode usar a notificação PARENTED. Por exemplo, aqui está um trecho que conecta um método de um nó a um sinal personalizado do nó pai sem falhar. Isso é útil em nós centrados em dados que podem ser criados em tempo de execução.
extends Node
var parent_cache
func connection_check():
return parent_cache.has_user_signal("interacted_with")
func _notification(what):
match what:
NOTIFICATION_PARENTED:
parent_cache = get_parent()
if connection_check():
parent_cache.interacted_with.connect(_on_parent_interacted_with)
NOTIFICATION_UNPARENTED:
if connection_check():
parent_cache.interacted_with.disconnect(_on_parent_interacted_with)
func _on_parent_interacted_with():
print("I'm reacting to my parent's interaction!")
using Godot;
public partial class MyNode : Node
{
private Node _parentCache;
public bool ConnectionCheck()
{
return _parentCache.HasUserSignal("InteractedWith");
}
public override void _Notification(int what)
{
switch ((long)what)
{
case NotificationParented:
_parentCache = GetParent();
if (ConnectionCheck())
{
_parentCache.Connect("InteractedWith", Callable.From(OnParentInteractedWith));
}
break;
case NotificationUnparented:
if (ConnectionCheck())
{
_parentCache.Disconnect("InteractedWith", Callable.From(OnParentInteractedWith));
}
break;
}
}
private void OnParentInteractedWith()
{
GD.Print("I'm reacting to my parent's interaction!");
}
}
using namespace godot;
class MyNode : public Node {
GDCLASS(MyNode, Node)
Node *parent_cache = nullptr;
void on_parent_interacted_with() {
UtilityFunctions::print("I'm reacting to my parent's interaction!");
}
public:
void connection_check() {
return parent_cache->has_user_signal("interacted_with");
}
void _notification(int p_what) {
switch (p_what) {
case NOTIFICATION_PARENTED:
parent_cache = get_parent();
if (connection_check()) {
parent_cache->connect("interacted_with", callable_mp(this, &MyNode::on_parent_interacted_with));
}
break;
case NOTIFICATION_UNPARENTED:
if (connection_check()) {
parent_cache->disconnect("interacted_with", callable_mp(this, &MyNode::on_parent_interacted_with));
}
break;
}
}
};