Scripting (continuazione)

Processare

Non c’è necessità di eseguire codice che viene eseguito in ogni momento, dato che molte azioni in Godot vengono eseguite tramite callback di funzioni virtuali.

In ogni caso, è comune che ci sia la necessità di eseguire uno script ad ogni frame. Ci sono due tipi di esecuzioni: durante il processo di inattività e durante il processo fisico.

Il processo d’inattività si attiva quando il metodo Node._process() viene trovato nello script. Può essere attivato o fermato con la funzione Node.set_process().

Questo metodo verrà chiamato ogni volta che viene disegnato un frame:

func _process(delta):
    # Do something...
    pass
public override void _Process(float delta)
{
    // Do something...
}

È importante tenere a mente che la frequenza con cui ``_process()``sarà chiamato dipende da quanti fotogrammi al secondo (FPS) è in esecuzione la vostra applicazione. Questo valore può variare nel tempo e nei dispositivi.

To help manage this variability, the delta parameter contains the time elapsed in seconds as a floating-point number since the previous call to _process().

Questo parametro può essere usato per essere sicuri che le cose ci mettano sempre la stessa durata di tempo, indifferentemente dagli FPS del gioco.

For example, movement is often multiplied with a time delta to make movement speed both constant and independent of the frame rate.

Il processo fisico con `` _physics_process () `` è simile, ma dovrebbe essere usato per processi che devono accadere prima di ogni step fisico, come controllare un personaggio. Viene sempre eseguito prima di un step fisico e viene chiamato a intervalli regolari: 60 volte al secondo di default. Puoi cambiare l’intervallo da Project Settings, sotto Physics -> Common -> Physics Fps.

Tuttavia la funzione _process() non è sincronizzata con la fisica. Il suo frame rate non è costante e dipende dall’hardware e dall’ottimizzazione del gioco. Viene eseguito dopo il passaggio della fisica nei giochi a thread singolo.

Un semplice modo per vedere la funzione _process() al lavoro è creando una scena con un singolo nodo Etichetta, con il seguente script:

extends Label

var accum = 0

func _process(delta):
    accum += delta
    text = str(accum) # 'text' is a built-in label property.
public class CustomLabel : Label
{
    private float _accum;

    public override void _Process(float delta)
    {
        _accum += delta;
        Text = _accum.ToString(); // 'Text' is a built-in label property.
    }
}

Il quale mostrerà il contatore incrementare ogni frame.

Gruppi

I gruppi in Godot funzionano come tag che potreste aver trovato in altri software. Un nodo può essere aggiunto a quanti gruppi si desidera. Questa è una funzione utile per organizzare scene di grandi dimensioni. Ci sono due modi per aggiungere nodi ai gruppi. Il primo è dall’interfaccia utente, usando il pulsante Gruppi sotto il pannello Nodi:

../../_images/groups_in_nodes.png

Ed il secondo modo è da codice. Il seguente script vorrebbe aggiungere il nodo corrente al gruppo enemies appena appare nell’albero di scena.

func _ready():
    add_to_group("enemies")
public override void _Ready()
{
    base._Ready();

    AddToGroup("enemies");
}

In questa maniera, se il giocatore è scoperto ad entrare furtivamente all’interno di una base nemica, tutti i nemici possono essere notificati del suono dell’allarme usando SceneTree.call_group():

func _on_discovered(): # This is a purely illustrative function.
    get_tree().call_group("enemies", "player_was_discovered")
public void _OnDiscovered() // This is a purely illustrative function.
{
    GetTree().CallGroup("enemies", "player_was_discovered");
}

Il codice qua sopra chiama la funzione player_was_discovered su ogni membro del gruppo enemies.

È anche possibile acquisire la lista completa di nodi enemies chiamando SceneTree.get_nodes_in_group():

var enemies = get_tree().get_nodes_in_group("enemies")
var enemies = GetTree().GetNodesInGroup("enemies");

La classe SceneTree fornisce molte funzioni utili, come l’integrazione con le scene, la gerarchia dei loro nodi ed anche un gruppo di nodi. Essa permette di spostare facilmente le scene, oppure di ricaricarle, o anche di chiudere il gioco, metterlo in pausa o rimuoverlo dalla pausa. Può anche venire usato con segnali di interesse. Quindi, se hai tempo, prova a dargli una occhiata!

Notifiche

Godot ha un suo sistema di notifiche. Solitamente non sono necessarie per lo scripting, siccome è a basso livello e vengono fornite le funzioni virtuali. È comunque buono sapere che esistono. Per esempio, tu potresti aggiungere la funzione Object._notification() al tuo script:

func _notification(what):
    match what:
        NOTIFICATION_READY:
            print("This is the same as overriding _ready()...")
        NOTIFICATION_PROCESS:
            print("This is the same as overriding _process()...")
public override void _Notification(int what)
{
    base._Notification(what);

    switch (what)
    {
        case NotificationReady:
            GD.Print("This is the same as overriding _Ready()...");
            break;
        case NotificationProcess:
            var delta = GetProcessDeltaTime();
            GD.Print("This is the same as overriding _Process()...");
            break;
    }
}

La documentazione di ogni classe presente in Class Reference mostra le notifiche che possono ricevere. Però, in molti casi GDScript fornisce funzioni più semplici e sovrascrivibili.

Overridable functions

Such overridable functions, which are described as follows, can be applied to nodes:

func _enter_tree():
    # When the node enters the Scene Tree, it becomes active
    # and  this function is called. Children nodes have not entered
    # the active scene yet. In general, it's better to use _ready()
    # for most cases.
    pass

func _ready():
    # This function is called after _enter_tree, but it ensures
    # that all children nodes have also entered the Scene Tree,
    # and became active.
    pass

func _exit_tree():
    # When the node exits the Scene Tree, this function is called.
    # Children nodes have all exited the Scene Tree at this point
    # and all became inactive.
    pass

func _process(delta):
    # This function is called every frame.
    pass

func _physics_process(delta):
    # This is called every physics frame.
    pass
public override void _EnterTree()
{
    // When the node enters the Scene Tree, it becomes active
    // and  this function is called. Children nodes have not entered
    // the active scene yet. In general, it's better to use _ready()
    // for most cases.
    base._EnterTree();
}

public override void _Ready()
{
    // This function is called after _enter_tree, but it ensures
    // that all children nodes have also entered the Scene Tree,
    // and became active.
    base._Ready();
}

public override void _ExitTree()
{
    // When the node exits the Scene Tree, this function is called.
    // Children nodes have all exited the Scene Tree at this point
    // and all became inactive.
    base._ExitTree();
}

public override void _Process(float delta)
{
    // This function is called every frame.
    base._Process(delta);
}

public override void _PhysicsProcess(float delta)
{
    // This is called every physics frame.
    base._PhysicsProcess(delta);
}

Come detto prima, è meglio usare queste funzioni invece delle notifiche di sistema.

Creare nodi

Per creare un nodo da codice, usare il metodo .new(), come per ogni altro dato di tipo classe. Per esempio:

var s
func _ready():
    s = Sprite.new() # Create a new sprite!
    add_child(s) # Add it as a child of this node.
private Sprite _sprite;

public override void _Ready()
{
    base._Ready();

    _sprite = new Sprite(); // Create a new sprite!
    AddChild(_sprite); // Add it as a child of this node.
}

Per eliminare un nodo, che sia dentro o fuori la scena, deve essere usato free():

func _someaction():
    s.free() # Immediately removes the node from the scene and frees it.
public void _SomeAction()
{
    _sprite.Free(); // Immediately removes the node from the scene and frees it.
}

Quando un nodo viene liberato, vengono liberati anche i suoi nodi figli. Per questo motivo, eliminare manualmente i nodi è più semplice di quanto sembri. Libera il nodo base ed ogni suo sotto-nodo lo seguerà.

Potrebbe verificarsi una situazione in cui noi vogliamo eliminare un nodo che è attualmente «bloccato», perchè sta emettendo un segnale o chiamando una funzione. Questo causerà il crash del gioco. Il debugger di Godot scoverà spesso queste situazioni e ti avvertirà.

Il modo più sicuro per eliminare un nodo è usando Node.queue_free(). Questo cancella i nodi in modo sicuro quando non vengono usati.

func _someaction():
    s.queue_free() # Removes the node from the scene and frees it when it becomes safe to do so.
public void _SomeAction()
{
    _sprite.QueueFree(); // Removes the node from the scene and frees it when it becomes safe to do so.
}

Instanziare scene

Istanziare una scena da codice viene fatto in due passaggi. Il primo è caricare la scena dal tuo hard drive:

var scene = load("res://myscene.tscn") # Will load when the script is instanced.
var scene = GD.Load<PackedScene>("res://myscene.tscn"); // Will load when the script is instanced.

Il precaricamento può essere più conveniente, siccome accade nella fase di analisi (solo GDScript):

var scene = preload("res://myscene.tscn") # Will load when parsing the script.

Ma scene non è ancora un nodo. È impacchettato in una risorsa speciale chiamata PackedScene. Per creare il nodo, bisogna chiamare la funzione PackedScene.instance(). Questo ritorna l’albero di nodi che possono essere aggiunti alla scena attiva:

var node = scene.instance()
add_child(node)
var node = scene.Instance();
AddChild(node);

Il vantaggio di questo processo diviso in due passaggi è che la PackedScene potrebbe essere caricata e pronta all’uso così che tu possa creare quante istanze desideri. È molto utile per istanziare velocemente un grande numero di nemici, proiettili e altre entità nella scena attiva.

Registra script come classi

Godot ha una funzione «Script Class» per registrare singoli script con l’editor. Di default, puoi accedere agli script senza nome solo caricando direttamente il file.

Puoi nominare uno script e registrarlo come un tipo nell’editor con la parola chiave class_name seguita dal nome della classe. Puoi aggiungere una virgola ed opzionalmente un percorso ad un’immagine da usare come icona. Potrai ora trovare il tuo nuovo tipo nella finestra di creazione di nodi o risorse.

extends Node

# Declare the class name here
class_name ScriptName, "res://path/to/optional/icon.svg"

func _ready():
    var this = ScriptName           # reference to the script
    var cppNode = MyCppNode.new()   # new instance of a class named MyCppNode

    cppNode.queue_free()
../../_images/script_class_nativescript_example.png

Avvertimento

In Godot 3.1:

  • Solo GDScript e NativeScript, cioè, C++ e altri linguaggi ramificati da GDNative, possono registrare gli script.
  • Solo GDScript crea variabili globali per ogni script nominato.