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.

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:

E muitas das chamadas de retorno que existem em Nós não têm métodos dedicados mas ainda são bastante úteis.

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")
    )

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())

_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:

  1. 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.

  2. _init() atribuição: o valor da propriedade é substituído por quaisquer atribuições feitas em _init(), disparando o setter.

  3. 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!".

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!")