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.

Quando usare scene anziché script

Abbiamo già trattato le differenze tra scene e script. Gli script definiscono un'estensione di classe del motore con codice imperativo, le scene con codice dichiarativo.

Pertanto le capacità di ogni sistema sono diverse. Le scene possono definire come una classe estesa viene inizializza , ma non quale sia il suo comportamento effettivo. Le scene sono spesso utilizzate insieme a uno script: la scena dichiara una composizione di nodi e lo script aggiunge un comportamento con codice imperativo.

Tipi anonimi

È possibile definire completamente il contenuto di una scena attraverso solo uno script. Questo è, in sostanza, ciò che fa l'editor Godot, soltanto nel costruttore di C++ dei suoi oggetti.

Tuttavia, scegliere quale usare può essere un dilemma. La creazione di istanze di script è identico alla creazione di classi nel motore, mentre la gestione delle scene richiede una modifica nell'API:

const MyNode = preload("my_node.gd")
const MyScene = preload("my_scene.tscn")
var node = Node.new()
var my_node = MyNode.new() # Same method call.
var my_scene = MyScene.instantiate() # Different method call.
var my_inherited_scene = MyScene.instantiate(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene.

Inoltre, gli script funzioneranno un po' più lentamente delle scene a causa delle differenze di velocità tra il codice del motore e quello degli script. Più grande e complesso è il nodo, di più sono i motivi per costruirlo come scena.

Tipi con nome

Gli script si possono registrare come un nuovo tipo all'interno dell'editor stesso. Ciò li visualizza come un nuovo tipo nella finestra di dialogo per creare nodi o risorse, con un'icona facoltativa. In questo modo, l'utilizzo dello script da parte dell'utente diventa molto più semplice. Anziché dover...

  1. Conoscere il tipo di base dello script che vorrebbero utilizzare.

  2. Crea un'istanza di quel tipo di base.

  3. Aggiungere lo script al nodo.

Con uno script registrato, il tipo da script diventa invece un'opzione di creazione, come gli altri nodi e risorse del sistema. La finestra di dialogo di creazione include persino una barra di ricerca per cercare il tipo per nome.

Ci sono due sistemi per registrare tipi:

  • Tipi personalizzati

    • Solo per editor. I nomi dei tipi non sono accessibili in fase di esecuzione.

    • Non supporta tipi personalizzati ereditati.

    • Uno strumento di inizializzazione. Crea il nodo con lo script, niente più.

    • L'editor non è consapevole del tipo dello script o della sua relazione con altri tipi del motore o degli script.

    • Consente agli utenti di definire un'icona.

    • Funziona con tutti i linguaggi di scripting perché gestisce le risorse Script in modo astratto.

    • Impostato tramite EditorPlugin.add_custom_type.

  • Classi da script

    • Accessibili nell'editor e in fase di esecuzione.

    • Visualizza le relazioni di ereditarietà per intero.

    • Crea il nodo con lo script, ma può anche modificarne i tipi o estendere il tipo dall'editor.

    • L'editor è a conoscenza delle relazioni di ereditarietà tra script, classi da script e classi C++ del motore.

    • Consente agli utenti di definire un'icona.

    • Gli sviluppatori del motore devono aggiungere manualmente il supporto per i linguaggi (sia l'esposizione dei nomi sia l'accessibilità in fase di esecuzione).

    • L'editor analizza le cartelle del progetto e registra tutti i nomi esposti per tutti i linguaggi di scripting. Ogni linguaggio di scripting deve implementare il proprio supporto per esporre queste informazioni.

Entrambe le metodologie aggiungono nomi alla finestra di dialogo di creazione, ma le classi da script, in particolare, consentono agli utenti di accedere al nome del tipo senza caricare la risorsa script. La creazione di istanze e l'accesso a costanti o metodi statici sono fattibili dovunque.

Con caratteristiche come queste, si potrebbe desiderare che il loro tipo sia uno script senza scena, vista la facilità d'uso che offre agli utenti. Chi sviluppa estensioni o crea strumenti interni per i designer troverà la lavoro più semplice in questo modo.

D'altra parte, ciò significa dover ricorrere soprattutto a una programmazione imperativa.

Prestazioni di Script vs PackedScene

Un ultimo aspetto da considerare nella scelta tra scene e script è la velocità di esecuzione.

Con l'aumentare delle dimensioni degli oggetti, le dimensioni necessarie degli script per crearli e inizializzarli aumentano notevolmente. La creazione di gerarchie di nodi ne è un esempio. La logica di ogni nodo potrebbe essere lunga parecchie centinaia di righe di codice.

L'esempio di codice seguente crea un nuovo Node, ne cambia il nome, gli assegna uno script, imposta il suo futuro genitore come proprietario in modo che sia salvato sul disco insieme a esso e infine lo aggiunge come figlio del nodo Main:

# main.gd
extends Node

func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("child.gd")
    add_child(child)
    child.owner = self

Un codice di script come questo è molto più lento del codice in C++ del motore. Ogni istruzione effettua una chiamata all'API di scripting, il che comporta numerose "ricerche" sul back-end per trovare la logica da eseguire.

Le scene aiutano a evitare questo problema di prestazioni. PackedScene, il tipo base da cui ereditano le scene, definisce risorse che utilizzano dati serializzati per creare oggetti. Il motore può elaborare le scene in gruppi sul back-end e fornire prestazioni molto migliori rispetto agli script.

Conclusione

Alla fine, il migliore approccio è considerare quanto segue:

  • Se si desidera creare uno strumento di base che verrà riutilizzato in diversi progetti e che sarà probabilmente utilizzato da persone di tutti i livelli di competenza (inclusi coloro che non si definiscono "programmatori"), allora è probabile che dovrebbe essere uno script, possibilmente uno con un nome/icona personalizzati.

  • Se si desidera creare un concetto specifico per il proprio gioco, allora dovrebbe sempre essere una scena. Le scene sono più facili da rintracciare/modificare e offrono maggiore sicurezza rispetto agli script.

  • Se si desidera dare un nome a una scena, è ancora possibile farlo, più o meno, dichiarando una classe da script e associandole una scena come costante. Lo script diventa, di fatto, uno spazio di nomi:

    # game.gd
    class_name Game # extends RefCounted, so it won't show up in the node creation dialog.
    extends RefCounted
    
    const MyScene = preload("my_scene.tscn")
    
    # main.gd
    extends Node
    func _ready():
        add_child(Game.MyScene.instantiate())