Riferimento GDScript

GDScript è un linguaggio di programmazione di alto livello, orientato agli oggetti, imperativo e gradualmente tipizzato, creato per Godot. Utilizza una sintassi basata sull'indentazione simile a linguaggi come Python. Il suo obiettivo è essere ottimizzato e strettamente integrato con Godot Engine, consentendo una grande flessibilità per la creazione e integrazione di contenuti.

GDScript è completamente indipendente da Python e non si basa su di esso.

Storia

Nota

La documentazione sulla storia di GDScript è stata spostata in Domande frequenti.

Esempio di GDScript

Alcune persone imparano meglio dando un'occhiata alla sintassi, quindi ecco un semplice esempio di come appare GDScript.

# Everything after "#" is a comment.
# A file is a class!

# (optional) icon to show in the editor dialogs:
@icon("res://path/to/optional/icon.svg")

# (optional) class definition:
class_name MyClass

# Inheritance:
extends BaseClass


# Member variables.
var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
var other_dict = {key = "value", other_key = 2}
var typed_var: int
var inferred_type := "String"

# Constants.
const ANSWER = 42
const THE_NAME = "Charly"

# Enums.
enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# Built-in vector types.
var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)


# Functions.
func some_function(param1, param2, param3):
    const local_const = 5

    if param1 < local_const:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("Fail!")

    for i in range(20):
        print(i)

    while param2 != 0:
        param2 -= 1

    match param3:
        3:
            print("param3 is 3!")
        _:
            print("param3 is not 3!")

    var local_var = param1 + 3
    return local_var


# Functions override functions with the same name on the base/super class.
# If you still want to call them, use "super":
func something(p1, p2):
    super(p1, p2)


# It's also possible to call another function in the super class:
func other_something(p1, p2):
    super.something(p1, p2)


# Inner class
class Something:
    var a = 10


# Constructor
func _init():
    print("Constructed!")
    var lv = Something.new()
    print(lv.a)

Se hai avuto esperienze con linguaggi staticamente tipizzati come C, C++, o C# ma non hai mai usato un linguaggio dinamicamente tipizzato, è consigliato che tu legga questo tutorial: GDScript: Un'introduzione ai linguaggi dinamici.

Identificatori

Una qualsiasi stringa che si limita solo a caratteri alfabetici (dalla a alla z e dalla A alla Z), cifre (da 0 a 9) e _ può essere un identificatore. Inoltre, gli identificatori non devono iniziare con una cifra. Gli identificatori fanno distinzione tra maiuscole e minuscole (foo è diverso da FOO).

Gli identificatori possono anche contenere la maggior parte dei caratteri Unicode che fanno parte di UAX#31. Ciò consente di utilizzare nomi di identificatori scritti in lingue diverse dall'inglese. I caratteri Unicode considerati "confondibili" con i caratteri ASCII, nonché le emoji, non sono ammessi negli identificatori.

Parole chiave

Di seguito è riportato l'elenco di parole chiave supportate dal linguaggio. Siccome le parole chiave sono riservate (token), non si possono usare come identificatori. Gli operatori (come in, not, and o or) e i nomi dei tipi integrati, elencati nelle prossime sezioni, sono anch'essi riservati.

Le parole chiave sono definite nel tokenizer di GDScript in caso si voglia dare un'occhiata in dettaglio.

Parola chiave

Descrizione

if

Vedere if/else/elif.

elif

Vedere if/else/elif.

else

Vedere if/else/elif.

for

Vedere for.

while

Vedere while.

match

Vedere match.

when

Used by pattern guards in match statements.

break

Esce dall'esecuzione del ciclo for o while attuale.

continue

Salta immediatamente alla prossima iterazione del ciclo for o while.

pass

Usato quando un istruzione è richiesta sintatticamente ma non si desidera che del codice venga eseguito, per esempio in una funzione vuota.

return

Restituisce un valore da una funzione.

class

Definisce una classe interna. Vedere Inner classes.

class_name

Definisce lo script come una classe accessibile globalmente con il nome specificato. Consultare Registering named classes.

extends

Definisce quale classe estendere con la classe attuale.

is

Verifica se una variabile estende una determinata classe, o è di un determinato tipo integrato.

in

Verifica se un valore si trova all'interno di una stringa, un array, un intervallo, un dizionario o un nodo. Se utilizzato con for, li itera invece di verificarli.

as

Converte il valore in un certo tipo, se possibile.

self

Si riferisce all'istanza della classe attuale. Vedere self.

super

Risolve l'ambito del metodo padre. Vedere Inheritance.

signal

Definisce un segnale. Vedere Signals.

func

Definisce una funzione. Vedere Functions.

static

Definisce una funzione statica o una variabile membro statica.

const

Definisce una costante. Vedere Constants.

enum

Definisce un enumerazione. Vedere Enums.

var

Definisce una variabile. Vedere Variables.

breakpoint

Ausilio per i punti d'interruzione del debugger nell'editor. A differenza dei punti d'interruzione creati cliccando nella barra laterale, breakpoint è memorizzato nello script stesso. Ciò lo rende persistente su macchine diverse quando si utilizza il controllo versione.

preload

Precarica una classe o una variabile. Vedere Classes as resources.

await

Attende il completamento di un segnale o di una coroutine. Vedere Awaiting signals or coroutines.

yield

Precedentemente utilizzato per le coroutine. Conservato come parola chiave per il momento della transizione.

assert

Asserisce una condizione, registra l'errore in caso di fallimento. Ignorato nelle build non di debug. Vedere Assert keyword.

void

Utilizzato per indicare che una funzione non restituisce alcun valore.

PI

Costante PI.

TAU

Costante TAU.

INF

Costante di infinito. Usata per confronti e come risultato di calcoli.

NAN

Costante NAN ("Not A Number", non un numero). Utilizzato come risultato quando i calcoli sono impossibili.

Operatori

Di seguito è riportato l'elenco degli operatori supportati e la loro precedenza. Tutti gli operatori binari sono associativi a sinistra, incluso l'operatore **. Ciò significa che 2 ** 2 ** 3 è uguale a (2 ** 2) ** 3. Utilizzare le parentesi per specificare esplicitamente la precedenza desiderata, ad esempio 2 ** (2 ** 3). L'operatore ternario if/else è associativo a destra.

Operatore

Descrizione

( )

Raggruppamento (priorità più alta)

Le parentesi non sono proprio un operatore, ma consentono di specificare esplicitamente la precedenza di un'operazione.

x[index]

Iscrizione

x.attribute

Riferimento di attributo

foo()

Chiamata di funzione

await x

Awaiting signals or coroutines

x is Node
x is not Node

Verifica di tipo

Vedere anche la funzione is_instance_of().

x ** y

Potenza

Moltiplica x per se stesso y volte, in modo simile alla chiamata della funzione pow().

~x

Operazione bit a bit NOT

+x
-x

Identità / negazione

x * y
x / y
x % y

Moltiplicazione / Divisione / Resto

L'operatore % è utilizzato anche per formattare le stringhe.

Nota: Questi operatori hanno lo stesso comportamento di C++, il che potrebbe risultare inaspettato per gli utenti che provengono da Python, JavaScript, ecc. Vedere la nota dettagliata dopo la tabella.

x + y
x - y

Addizione (o Concatenazione) / Sottrazione

x << y
x >> y

Spostamento di bit

x & y

Operazione bit a bit AND

x ^ y

Operazione bit a bit XOR

x | y

Operazione bit a bit OR

x == y
x != y
x < y
x > y
x <= y
x >= y

Confronto

Vedere la nota dettagliata dopo la tabella.

x in y
x not in y

Verifica di inclusione

in è utilizzato anche con la parola chiave for come parte della sintassi.

not x
!x

NOT booleano e il suo alias non consigliato

x and y
x && y

AND booleano e il suo alias non consigliato

x or y
x || y

OR booleano e il suo alias non consigliato

true_expr if cond else false_expr

if / else ternario

x as Node

Conversione di tipo

x = y
x += y
x -= y
x *= y
x /= y
x **= y
x %= y
x &= y
x |= y
x ^= y
x <<= y
x >>= y

Assegnazione (priorità più bassa)

Non è possibile utilizzare un operatore di assegnazione all'interno di un'espressione.

Nota

Il comportamento di alcuni operatori potrebbe differire da quello previsto:

  1. Se entrambi gli operandi dell'operatore / sono int, viene effettuata una divisione intera anziché una divisione frazionaria. Ad esempio 5 / 2 == 2, non 2.5. Se ciò non è desiderato, utilizzare almeno un float (x / 2.0), una conversione di tipo (float(x) / y) o moltiplicare per 1.0 (x * 1.0 / y).

  2. L'operatore % è disponibile solo per gli int, per i float utilizzare la funzione fmod().

  3. Per valori negativi, l'operatore % e fmod() utilizzano il troncamento invece di arrotondare verso meno infinito. Ciò significa che il resto ha un segno. Se è necessario il resto in senso matematico, utilizzare invece le funzioni posmod() e fposmod().

  4. Gli operatori == e != a volte consentono di confrontare valori di tipi diversi (ad esempio, 1 == 1.0 è vero), ma in altri casi possono causare un errore all'esecuzione. In caso di dubbi sui tipi degli operandi, è possibile utilizzare in modo sicuro la funzione is_same() (notando però che è più stringente su tipi e riferimenti). Per confrontare numeri in virgola mobile, utilizzare invece le funzioni is_equal_approx() e is_zero_approx().

Letterali

Esempio(i)

Descrizione

null

Valore nullo

false, true

Valori booleani

45

Numero intero in base 10 (decimale)

0x8f51

Numero intero in base 16 (esadecimale)

0b101010

Numero intero in base 2 (binario)

3.14, 58.1e-10

Numero a virgola mobile (reale)

"Ciao", 'Salve'

Stringhe regolari

"""Ciao""", '''Salve'''

Stringhe regolari a virgolette triple

r"Ciao", r'Salve'

Stringhe grezze

r"""Ciao""", r'''Salve'''

Stringhe grezze a triple virgolette

&"name"

StringName

^"Node/Label"

NodePath

Ci sono anche due costrutti che sembrano letterali, ma in realtà non lo sono:

Esempio

Descrizione

$NodePath

Abbreviazione di get_node("NodePath")

%UniqueNode

Abbreviazione di get_node("%UniqueNode")

I numeri interi e in virgola mobile possono essere separati da _ per renderli più leggibili. I seguenti modi di scrittura dei numeri sono tutti validi:

12_345_678  # Equal to 12345678.
3.141_592_7  # Equal to 3.1415927.
0x8080_0000_ffff  # Equal to 0x80800000ffff.
0b11_00_11_00  # Equal to 0b11001100.

Le stringhe letterali regolari possono contenere le seguenti sequenze di escape:

** Sequenza di escape**

Si espande a

\n

Nuova riga (avanzamento di riga)

\t

Carattere di tabulazione orizzontale

\r

Ritorno a capo

\a

Avviso (bip/campanello)

\b

Cancella indietro

\f

Formfeed page break

\v

Carattere di tabulazione verticale

\"

Doppie virgolette

\'

Singola virgoletta

\\

Barra rovesciata

\uXXXX

Codice Unicode UTF-16 XXXX (esadecimale, senza distinzione tra maiuscole e minuscole)

\UXXXXXX

Codice Unicode UTF-32 XXXXXX (esadecimale, senza distinzione tra maiuscole e minuscole)

Esistono due modi per rappresentare un carattere Unicode con escape superiore a 0xFFFF:

Inoltre, all'interno di una stringa, utilizzando \ seguito da un carattere di nuova riga, è possibile continuare nella riga successiva, senza inserire un carattere di nuova riga nella stringa stessa.

Una stringa racchiusa tra virgolette di un tipo (ad esempio ") può contenere virgolette di un altro tipo (ad esempio '`) senza escape. Le stringhe tra virgolette triple consentono di evitare l'escape di un massimo di due virgolette consecutive dello stesso tipo (a meno che non siano adiacenti ai bordi della stringa).

Le stringhe letterali grezze codificano sempre la stringa così come appare nel codice sorgente. Questo è particolarmente utile per le espressioni regolari. Una stringa letterale grezza non elabora le sequenze di escape, tuttavia riconosce \\ e \" (\') e le sostituisce con se stessa. Pertanto, una stringa può avere una virgoletta che corrisponde a quella iniziale, ma solo se preceduta da una barra rovesciata.

print("\tchar=\"\\t\"")  # Prints `    char="\t"`.
print(r"\tchar=\"\\t\"") # Prints `\tchar=\"\\t\"`.

Nota

Some strings cannot be represented using raw string literals: you cannot have an odd number of backslashes at the end of a string or have an unescaped opening quote inside the string. However, in practice this doesn't matter since you can use a different quote type or use concatenation with a regular string literal.

GDScript supporta anche la formattazione di stringhe.

Annotazioni

Le annotazioni sono token speciali in GDScript che agiscono come modificatori di uno script o del suo codice e possono influenzare il modo in cui lo script è trattato dal motore Godot o dall'editor.

Ogni annotazione comincia con il carattere @ ed è specificata da un nome. Una descrizione dettagliata e un esempio per ogni annotazione sono disponibili nel riferimento classe di GDScript.

Ad esempio, è possibile utilizzarle per esportare un valore nell'editor:

@export_range(1, 100, 1, "or_greater")
var ranged_var: int = 50

Per ulteriori informazioni sull'esportazione delle proprietà, leggere l'articolo Esportazioni in GDScript.

Qualsiasi espressione costante compatibile con il tipo di argomento richiesto può essere passata come valore di un argomento di un'annotazione:

const MAX_SPEED = 120.0

@export_range(0.0, 0.5 * MAX_SPEED)
var initial_speed: float = 0.25 * MAX_SPEED

Le annotazioni si possono specificare una per riga o tutte sulla stessa riga. Influiscono sull'istruzione successiva che non è un'annotazione. Le annotazioni possono ricevere argomenti tra parentesi e separati da virgole.

Entrambi i seguenti sono equivalenti:

@annotation_a
@annotation_b
var variable

@annotation_a @annotation_b var variable

L'annotazione @onready

When using nodes, it's common to desire to keep references to parts of the scene in a variable. As scenes are only warranted to be configured when entering the active scene tree, the sub-nodes can only be obtained when a call to Node._ready() is made.

var my_label


func _ready():
    my_label = get_node("MyLabel")

This can get a little cumbersome, especially when nodes and external references pile up. For this, GDScript has the @onready annotation, that defers initialization of a member variable until _ready() is called. It can replace the above code with a single line:

@onready var my_label = get_node("MyLabel")

Avvertimento

Applicare @onready e una qualsiasi annotazione @export alla stessa variabile non funziona come ci si aspetterebbe. L'annotazione @onready imposterà il valore predefinito dopo che @export abbia effetto, sovrascrivendolo:

@export var a = "init_value_a"
@onready @export var b = "init_value_b"

func _init():
    prints(a, b) # init_value_a <null>

func _notification(what):
    if what == NOTIFICATION_SCENE_INSTANTIATED:
        prints(a, b) # exported_value_a exported_value_b

func _ready():
    prints(a, b) # exported_value_a init_value_b

Pertanto, viene generato l'avviso ONREADY_WITH_EXPORT, il quale viene trattato come un errore per impostazione predefinita. Si sconsiglia di disabilitarlo o ignorarlo.

Commenti

Tutto ciò si trova tra un # e la fine della riga è ignorato e considerato un commento.

# This is a comment.

Suggerimento

Nell'editor di script Godot, le parole chiave speciali vengono evidenziate nei commenti per attirare l'attenzione dell'utente su commenti specifici:

  • Critici (appaiono in rosso): ALERT, ATTENTION, CAUTION, CRITICAL, DANGER, SECURITY

  • Avvisi (appaiono in giallo): BUG, DEPRECATED, FIXME, HACK, TASK, TBD, TODO, WARNING

  • Informazioni (appaiono in verde): INFO, NOTE, NOTICE, TEST, TESTING

Queste parole chiave sono sensibili alle maiuscole e alle minuscole, quindi si devono scrivere in maiuscolo affinché siano riconosciute:

# In the example below, "TODO" will appear in yellow by default.
# The `:` symbol after the keyword is not required, but it's often used.

# TODO: Add more items for the player to choose from.

L'elenco delle parole chiave evidenziate e i relativi colori si possono modificare nella sezione Editor di testo > Tema > Indicatori di commenti delle Impostazioni dell'editor.

Utilizza due simboli cancelletto (##) invece di uno (#) per aggiungere un commento di documentazione, il quale apparirà nella documentazione dello script e nella descrizione dell'ispettore di una variabile esportata. I commenti di documentazione si devono inserire direttamente sopra un elemento documentabile (ad esempio una variabile membro) o all'inizio di un file. Sono disponibili anche opzioni di formattazione dedicate. Consultare Commenti di documentazione in GDScript per i dettagli.

## This comment will appear in the script documentation.
var value

## This comment will appear in the inspector tooltip, and in the documentation.
@export var exported_value

Regioni di codice

Le regioni di codice sono tipi speciali di commenti che l'editor di script interpreta come regioni riducibili. Ciò significa che, dopo aver scritto commenti di regione di codice, è possibile ridurre ed espandere la regione cliccando sulla freccia che appare a sinistra del commento. Questa freccia appare all'interno di un quadrato viola per essere distinguibile dalla riduzione standard del codice.

La sintassi è la seguente:

# Important: There must be *no* space between the `#` and `region` or `endregion`.

# Region without a description:
#region
...
#endregion

# Region with a description:
#region Some description that is displayed even when collapsed
...
#endregion

Suggerimento

Per creare rapidamente una regione di codice, selezionare più righe nell'editor di script, fare clic destro sulla selezione e scegliere Crea regione di codice. La descrizione della regione verrà selezionata automaticamente per la modifica.

È possibile annidare regioni di codice all'interno di altre regioni di codice.

Ecco un esempio concreto di utilizzo delle regioni di codice:

# This comment is outside the code region. It will be visible when collapsed.
#region Terrain generation
# This comment is inside the code region. It won't be visible when collapsed.
func generate_lakes():
    pass

func generate_hills():
    pass
#endregion

#region Terrain population
func place_vegetation():
    pass

func place_roads():
    pass
#endregion

This can be useful to organize large chunks of code into easier to understand sections. However, remember that external editors generally don't support this feature, so make sure your code is easy to follow even when not relying on folding code regions.

Nota

Individual functions and indented sections (such as if and for) can always be collapsed in the script editor. This means you should avoid using a code region to contain a single function or indented section, as it won't bring much of a benefit. Code regions work best when they're used to group multiple elements together.

Continuazione di riga

Una riga di codice in GDScript può essere continuata sulla riga successiva utilizzando una barra rovesciata (\). Aggiungendone una alla fine di una riga, il codice sulla riga successiva si comporterà come se continuasse dalla riga precedente. Ecco un esempio:

var a = 1 + \
2

Una riga può essere continuata più volte in questo modo:

var a = 1 + \
4 + \
10 + \
4

Tipi integrati

I tipi integrati sono allocati nello stack. Vengono passati per valore. Ciò significa che una copia viene creata a ogni assegnazione o quando vengono passati come argomenti alle funzioni. Le eccezioni sono Object, Array, Dictionary e array impacchettati (come PackedByteArray), che vengono passati per riferimento e quindi condivisi. Tutti gli array, Dictionary e alcuni oggetti (Node, Resource) hanno un metodo duplicate() che consente di crearne una copia.

Tipi integrati di base

Una variabile in GDScript si può assegnare a diversi tipi integrati.

null

null è un tipo di dati vuoto che non contiene informazioni e non può esservi assegnato alcun altro valore.

Solo i tipi che ereditano da Object possono avere un valore null (Object è quindi definito un tipo "nullable"). I tipi Variant devono avere sempre un valore valido e quindi non possono avere un valore null.

bool

Abbreviazione di "booleano", può contenere solo true o false.

int

Abbreviazione di "intero", memorizza numeri interi (positivi e negativi). È memorizzato come valore a 64 bit, equivalente a int64_t in C++.

float

Memorizza numeri reali (con decimali) utilizzando valori in virgola mobile (o fluttuante). È memorizzato come valore a 64 bit, equivalente a double in C++. Nota: attualmente, strutture dati come Vector2, Vector3 e PackedFloat32Array memorizzano valori float a precisione singola a 32 bit.

String

Una sequenza di caratteri in formato Unicode.

StringName

An immutable string that allows only one instance of each name. They are slower to create and may result in waiting for locks when multithreading. In exchange, they're very fast to compare, which makes them good candidates for dictionary keys.

NodePath

Un percorso pre-analizzato verso un nodo o una proprietà di un nodo. Può essere facilmente assegnato a una stringa e viceversa. Sono utili per interagire con l'albero per ottenere un nodo o per influenzare proprietà come con Tweens.

Tipi vettoriali integrati

Vector2

Tipo di vettore 2D contenente i campi x e y. Vi si può accedere anche come un array.

Vector2i

Uguale a Vector2, ma le componenti sono intere. Utile per rappresentare elementi in una griglia 2D.

Rect2

Tipo di rettangolo 2D contenente due campi di tipo vettore: position e size. Contiene anche un campo end che corrisponde a position + size.

Vector3

Tipo di vettore 3D contenente i campi x, y e z. Anche questo si può accedere come un array.

Vector3i

Uguale a Vector3, ma con componenti interi. Utile per indicizzare elementi in una griglia 3D.

Transform2D

Matrice 3×2 utilizzata per le trasformazioni 2D.

Plane

Tipo di piano 3D in forma normalizzata che contiene un campo di tipo vettore normal e una distanza scalare d.

Quaternion

Il quaternione è un tipo di dato usato per rappresentare una rotazione 3D. È utile per interpolare rotazioni.

AABB

Bounding box (o riquadro 3D) allineato agli assi contiene 2 campi di tipo vettore: position e size. Contiene anche un campo end che corrisponde a position + size.

Basis

Matrice 3x3 utilizzata per rotazioni e cambi di scala in 3D. Contiene 3 campi di tipo vettore (x, y e z) e vi si può anche accedere come un array di vettori 3D.

Transform3D

Trasformazione 3D contenente un campo di tipo Basis basis e un campo di tipo Vector3 origin.

Tipi integrati del motore

Color

Il tipo di dati Color contiene i campi r, g, b e a. Vi si può anche accedere come h, s e v per tonalità/saturazione/valore.

RID

ID risorsa (RID). I server usano RID generici per fare riferimento a dati opachi.

Object

Classe base per tutto ciò che non è un tipo integrato.

Tipi integrati contenitori

Array

Sequenza generica di tipi arbitrari di oggetti, inclusi altri array o dizionari (vedere sotto). L'array si ridimensiona dinamicamente. Gli array sono indicizzati a partire dall'indice 0. Gli indici negativi contano dalla fine.

var arr = []
arr = [1, 2, 3]
var b = arr[1] # This is 2.
var c = arr[arr.size() - 1] # This is 3.
var d = arr[-1] # Same as the previous line, but shorter.
arr[0] = "Hi!" # Replacing value 1 with "Hi!".
arr.append(4) # Array is now ["Hi!", 2, 3, 4].

Array tipizzati

Godot 4.0 ha aggiunto il supporto per gli array tipizzati. Durante le operazioni di scrittura, Godot verifica che i valori degli elementi corrispondano al tipo specificato, in modo che l'array non contenga valori non validi. L'analizzatore statico di GDScript tiene conto degli array tipizzati, tuttavia metodi di array come front() e back() restituiranno comunque il tipo Variant.

Gli array tipizzati hanno la sintassi Array[Type], dove Type può essere qualsiasi tipo Variant, classe nativa o utente, o enumerazione. I tipi di array annidati (come Array[Array[int]]) non sono supportati.

var a: Array[int]
var b: Array[Node]
var c: Array[MyClass]
var d: Array[MyEnum]
var e: Array[Variant]

Array e Array[Variant] sono la stessa cosa.

Nota

Gli array vengono passati per riferimento, quindi il tipo di elementi nell'array è anche un attributo della struttura in memoria a cui una variabile fa riferimento in fase di esecuzione. Il tipo statico di una variabile limita le strutture a cui può fare riferimento. Pertanto, non è possibile assegnare un array con un tipo di elementi diverso, anche se il tipo è un sottotipo del tipo richiesto.

Se è necessario convertire un array tipizzato, si può creare un nuovo array e utilizzare il metodo Array.assign():

var a: Array[Node2D] = [Node2D.new()]

# (OK) You can add the value to the array because `Node2D` extends `Node`.
var b: Array[Node] = [a[0]]

# (Error) You cannot assign an `Array[Node2D]` to an `Array[Node]` variable.
b = a

# (OK) But you can use the `assign()` method instead. Unlike the `=` operator,
# the `assign()` method copies the contents of the array, not the reference.
b.assign(a)

Una unica eccezione è stata fatta per il tipo Array (Array[Variant]), per comodità dell'utente e per compatibilità con il codice vecchio. Nonostante ciò, le operazioni su array non tipizzati sono considerate non sicure.

Array impacchettati

Gli array impacchettati (PackedArray) sono generalmente più veloci da iterare e modificare rispetto a un Array tipizzato dello stesso tipo (ad esempio, PackedInt64Array rispetto a Array[int]) e consumano meno memoria. Nel peggior caso, si prevede che siano veloci quanto un Array non tipizzato. D'altra parte, gli Array non-impacchettati (tipizzati o meno) offrono metodi aggiuntivi come Array.map, che non sono disponibili per i PackedArray. Consultare il riferimento classi per i dettagli sui metodi disponibili. Gli Array tipizzati sono generalmente più veloci da iterare e modificare rispetto agli Array non tipizzati.

While all Arrays can cause memory fragmentation when they become large enough, if memory usage and performance (iteration and modification speed) is a concern and the type of data you're storing is compatible with one of the Packed Array types, then using those may yield improvements. However, if you do not have such concerns (e.g. the size of your array does not reach the tens of thousands of elements) it is likely more helpful to use regular or typed Arrays, as they provide convenience methods that can make your code easier to write and maintain (and potentially faster if your data requires such operations a lot). If the data you will store is of a known type (including your own defined classes), prefer to use a typed Array as it may yield better performance in iteration and modification compared to an untyped Array.

Dictionary

Contenitore associativo che contiene valori referenziati da chiavi uniche.

var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}
d["Hi!"] = 0
d = {
    22: "value",
    "some_key": 2,
    "other_key": [2, 3, 4],
    "more_key": "Hello"
}

Lua-style table syntax is also supported. Lua-style uses = instead of : and doesn't use quotes to mark string keys (making for slightly less to write). However, keys written in this form can't start with a digit (like any GDScript identifier), and must be string literals.

var d = {
    test22 = "value",
    some_key = 2,
    other_key = [2, 3, 4],
    more_key = "Hello"
}

Per aggiungere una chiave a un dizionario esistente, accedervi come una chiave esistente e assegnargli un valore:

var d = {} # Create an empty Dictionary.
d.waiting = 14 # Add String "waiting" as a key and assign the value 14 to it.
d[4] = "hello" # Add integer 4 as a key and assign the String "hello" as its value.
d["Godot"] = 3.01 # Add String "Godot" as a key and assign the value 3.01 to it.

var test = 4
# Prints "hello" by indexing the dictionary with a dynamic key.
# This is not the same as `d.test`. The bracket syntax equivalent to
# `d.test` is `d["test"]`.
print(d[test])

Nota

Si può utilizzare la sintassi tra parentesi quadre per accedere alle proprietà di qualsiasi Object, non solo dei dizionari. Tenete presente che ciò causerà un errore di script quando si tenta di indicizzare una proprietà inesistente. Per evitare ciò, utilizzare i metodi Object.get() e Object.set().

Signal

Un segnale è un messaggio che può essere emesso da un oggetto a chi desidera ascoltarlo. Il tipo Signal può essere utilizzato per passare l'emettitore in giro.

I segnali sono meglio utilizzati ricavandoli da oggetti reali, ad esempio $Button.button_up.

Callable

Un chiamabile contiene un oggetto e una funzione, il che utile per passare funzioni come fossero valori (ad esempio per la connessione di segnali).

Ottenere un metodo come membro restituisce un chiamabile. var x = $Sprite2D.rotate imposterà il valore di x su un chiamabile avente $Sprite2D come oggetto e rotate come metodo.

È possibile richiamarlo tramite il metodo call: x.call(PI).

Variabili

Le variabili possono esistere come membri di classe o locali alle funzioni. Sono create con la parola chiave var e può, facoltativamente, essere loro assegnato un valore al momento dell'inizializzazione.

var a # Data type is 'null' by default.
var b = 5
var c = 3.8
var d = b + c # Variables are always initialized in direct order (see below).

Le variabili possono facoltativamente avere una specificazione di tipo. Quando si specifica un tipo, la variabile sarà obbligata ad avere sempre lo stesso tipo, e tentando di assegnare un valore incompatibile verrà genererà un errore.

I tipi vengono specificati nella dichiarazione delle variabili utilizzando il simbolo : (due punti) dopo il nome della variabile, seguito dal tipo.

var my_vector2: Vector2
var my_node: Node = Sprite2D.new()

Se la variabile viene inizializzata all'interno della dichiarazione, è possibile dedurne il tipo ed è quindi possibile omettere il nome del tipo:

var my_vector2 := Vector2() # 'my_vector2' is of type 'Vector2'.
var my_node := Sprite2D.new() # 'my_node' is of type 'Sprite2D'.

La deduzione del tipo è possibile solo se il valore assegnato ha un tipo definito, altrimenti verrà generato un errore.

I tipi validi sono:

  • Tipi integrati (Array, Vector2, int, String, ecc.).

  • Classi del motore (Node, Resource, RefCounted, ecc.).

  • Nomi di costanti se contengono una risorsa di script (MyScript se è stato dichiarato const MyScript = preload("res://my_script.gd")).

  • Altre classi nello stesso script, rispettando l'ambito (InnerClass.NestedClass se è stato dichiarato class NestedClass all'interno di class InnerClass nello stesso ambito).

  • Classi di script dichiarate con la parola chiave class_name.

  • Autoload registrati come singleton.

Nota

Sebbene Variant sia una specificazione di tipo valida, non è un tipo vero e proprio. Significa solo che non esiste un tipo impostato ed è equivalente a non avere affatto un tipo statico. Pertanto, solitamente, dedurre il tipo come Variant non è consentito, poiché è probabile che sia un sbaglio.

È possibile disattivare questa verifica, o renderla solo un avviso, modificandola nelle impostazioni del progetto. Consultare Sistema di avvertimento in GDScript per i dettagli.

Ordine di inizializzazione

Le variabili membro vengono inizializzate nel seguente ordine:

  1. A seconda del tipo statico della variabile, la variabile è null (variabili non tipizzate e oggetti) oppure ha un valore predefinito del tipo (0 per int, false per bool, ecc.).

  2. I valori specificati vengono assegnati nell'ordine delle variabili nello script, dall'alto verso il basso.

    • (Solamente per le classi derivate da Node) Se l'annotazione @onready viene applicata a una variabile, la sua inizializzazione viene posticipata al passaggio 5.

  3. Se definito, il metodo _init() viene chiamato.

  4. Per l'istanziazione di istanze di scene e di risorse, i valori esportati vengono assegnati.

  5. (Solamente per le classi derivate da Node) Le variabili @onready vengono inizializzate.

  6. (Solamente per le classi derivate da Node) Se definito, il metodo _ready() viene chiamato.

Avvertimento

È possibile specificare un'espressione complessa per inizializzare una variabile, incluse chiamate di funzioni. Assicurarsi che le variabili siano inizializzate nell'ordine corretto, altrimenti i loro valori potrebbero essere sovrascritti. Ad esempio:

var a: int = proxy("a", 1)
var b: int = proxy("b", 2)
var _data: Dictionary = {}

func proxy(key: String, value: int):
    _data[key] = value
    print(_data)
    return value

func _init() -> void:
    print(_data)

Stamperà:

{ "a": 1 }
{ "a": 1, "b": 2 }
{  }

Per risolvere questo problema, spostare la definizione della variabile _data sopra la definizione di a o rimuovere l'assegnazione del dizionario vuoto (= {}).

Variabili statiche

Una variabile membro di una classe può essere dichiarata statica:

static var a

Le variabili statiche appartengono alla classe, non alle istanze. Ciò significa che le variabili statiche condividono i valori tra più istanze, a differenza delle normali variabili membro.

Dall'interno di una classe, è possibile accedere alle variabili statiche da qualsiasi funzione, sia statica sia non statica. Dall'esterno della classe, è possibile accedere alle variabili statiche attraverso la classe stessa o un'istanza (la seconda opzione è sconsigliata in quanto meno leggibile).

Nota

Le annotazioni @export e @onready non possono essere applicate a una variabile statica. Le variabili locali non possono essere statiche.

L'esempio seguente definisce una classe Person con una variabile statica con il nome max_id. max_id viene incrementato nella funzione _init(). Facendo ciò, è più facile monitorare il numero di istanze di Person nel gioco.

# person.gd
class_name Person

static var max_id = 0

var id
var name

func _init(p_name):
    max_id += 1
    id = max_id
    name = p_name

In questo codice, si creano due istanze della nostra classe Person e si verifica che la classe e ogni istanza abbiano lo stesso valore di max_id, perché la variabile è statica e accessibile a ogni istanza.

# test.gd
extends Node

func _ready():
    var person1 = Person.new("John Doe")
    var person2 = Person.new("Jane Doe")

    print(person1.id) # 1
    print(person2.id) # 2

    print(Person.max_id)  # 2
    print(person1.max_id) # 2
    print(person2.max_id) # 2

Le variabili statiche possono avere suggerimenti sul tipo, setter e getter:

static var balance: int = 0

static var debt: int:
    get:
        return -balance
    set(value):
        balance = -value

È possibile accedere a una variabile statica della classe padre anche tramite una classe figlia:

class A:
    static var x = 1

class B extends A:
    pass

func _ready():
    prints(A.x, B.x) # 1 1
    A.x = 2
    prints(A.x, B.x) # 2 2
    B.x = 3
    prints(A.x, B.x) # 3 3

Annotazione @static_unload

Poiché le classi GDScript sono risorse, la presenza di variabili statiche in uno script ne impedisce lo scaricamento anche se non ci sono più istanze di quella classe e non ci sono altri riferimenti. Questo può essere importante se le variabili statiche memorizzano grandi quantità di dati o contengono riferimenti ad altre risorse del progetto, come le scene. È consigliabile pulire questi dati manualmente o utilizzare l'annotazione @static_unload se le variabili statiche non memorizzano dati importanti e si possono ripristinare.

Avvertimento

Attualmente, a causa di un bug, gli script non vengono mai liberati, anche se viene utilizzata l'annotazione @static_unload.

Si noti che @static_unload si applica all'intero script (incluse le classi interne) e deve essere posizionato all'inizio dello script, prima di class_name e extends:

@static_unload
class_name MyNode
extends Node

Consultare anche Static functions e Static constructor.

Conversione del tipo

Values assigned to typed variables must have a compatible type. If it's needed to coerce a value to be of a certain type, in particular for object types, you can use the casting operator as.

Casting between object types results in the same object if the value is of the same type or a subtype of the cast type.

var my_node2D: Node2D
my_node2D = $Sprite2D as Node2D # Works since Sprite2D is a subtype of Node2D.

If the value is not a subtype, the casting operation will result in a null value.

var my_node2D: Node2D
my_node2D = $Button as Node2D # Results in 'null' since a Button is not a subtype of Node2D.

For built-in types, they will be forcibly converted if possible, otherwise the engine will raise an error.

var my_int: int
my_int = "123" as int # The string can be converted to int.
my_int = Vector2() as int # A Vector2 can't be converted to int, this will cause an error.

Casting is also useful to have better type-safe variables when interacting with the scene tree:

# Will infer the variable to be of type Sprite2D.
var my_sprite := $Character as Sprite2D

# Will fail if $AnimPlayer is not an AnimationPlayer, even if it has the method 'play()'.
($AnimPlayer as AnimationPlayer).play("walk")

Costanti

Le costanti sono valori che non è possibile modificare durante l'esecuzione del gioco. Il loro valore deve essere noto in fase di compilazione. L'utilizzo della parola chiave const consente di assegnare un nome a un valore costante. Tentare di assegnare un valore a una costante dopo che è stata dichiarata genererà un errore.

Si consiglia di utilizzare le costanti ogni volta che non si prevede che un valore cambi.

const A = 5
const B = Vector2(20, 20)
const C = 10 + 20 # Constant expression.
const D = Vector2(20, 30).x # Constant expression: 20.
const E = [1, 2, 3, 4][0] # Constant expression: 1.
const F = sin(20) # 'sin()' can be used in constant expressions.
const G = x + 20 # Invalid; this is not a constant expression!
const H = A + 20 # Constant expression: 25 (`A` is a constant).

Sebbene il tipo delle costanti sia dedotto dal valore assegnato, è anche possibile aggiungere una specificazione esplicita di tipo:

const A: int = 5
const B: Vector2 = Vector2()

Assegnare un valore di un tipo incompatibile genererà un errore.

È anche possibile creare costanti all'interno di una funzione, il che è utile per assegnare un nome ai valori magici locali.

Enumerazioni

Le enumerazioni sono basicamente una forma abbreviata per le costanti, e sono molto utili se si desidera assegnare numeri interi consecutivi a certe costanti.

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

Se si passa un nome all'enumerazione, tutte le chiavi verranno inserite in un Dictionary costante con quel nome. Ciò significa che tutti i metodi costanti di un dizionario si possono utilizzare anche con un enumerazione con nome.

Importante

Le chiavi in un enumerazione con nome non vengono registrate come costanti globali. Il loro accesso deve essere effettuato anteponendo il nome dell'enumerazione (Name.KEY).

enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}

# Is the same as:
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}
# Access values with State.STATE_IDLE, etc.

func _ready():
    # Access values with Name.KEY, prints '5'
    print(State.STATE_JUMP)
    # Use dictionary methods:
    # prints '["STATE_IDLE", "STATE_JUMP", "STATE_SHOOT"]'
    print(State.keys())
    # prints '{ "STATE_IDLE": 0, "STATE_JUMP": 5, "STATE_SHOOT": 6 }'
    print(State)
    # prints '[0, 5, 6]'
    print(State.values())

Se non si assegna un valore a una chiave di un enumerazione, le verrà assegnato il valore precedente più uno, oppure 0 se è la prima voce dell'enumerazione. Sono consentite più chiavi con lo stesso valore.

Funzioni

Le funzioni appartengono sempre a una classe. La priorità di ambito per la ricerca delle variabili è: locale → membro della classe → globale. La variabile self è sempre disponibile ed è fornita come opzione per accedere ai membri della classe (vedi self), ma non è sempre necessaria (e non dovrebbe essere inviata come primo argomento della funzione, diversamente da Python).

func my_function(a, b):
    print(a)
    print(b)
    return a + b  # Return is optional; without it 'null' is returned.

Una funzione può restituire, tramite la parola chiave return, in qualsiasi punto. Il valore restituito predefinito è null.

Se una funzione contiene una sola riga di codice, può essere scritta su una sola riga:

func square(a): return a * a

func hello_world(): print("Hello World")

func empty_function(): pass

Le funzioni possono anche avere specificazioni di tipo per gli argomenti e per il valore restituito. I tipi per gli argomenti si possono aggiungere in modo simile alle variabili:

func my_function(a: int, b: String):
    pass

Se l'argomento di una funzione ha un valore predefinito, è possibile dedurne il tipo:

func my_function(int_arg := 42, String_arg := "string"):
    pass

Il tipo restituito della funzione può essere specificato dopo l'elenco degli argomenti utilizzando il token di freccia (->):

func my_int_function() -> int:
    return 0

Le funzioni che hanno un tipo devono restituire un valore appropriato. Impostando il tipo come void, la funzione non restituisce nulla. Le funzioni void possono restituire in anticipo con la parola chiave return, ma non possono restituire alcun valore.

func void_function() -> void:
    return # Can't return a value.

Nota

Non-void functions must always return a value, so if your code has branching statements (such as an if/else construct), all the possible paths must have a return. E.g., if you have a return inside an if block but not after it, the editor will raise an error because if the block is not executed, the function won't have a valid value to return.

Funzioni di riferimento

Le funzioni sono valori di prima classe in termini dell'oggetto Callable. Fare riferimento a una funzione per nome senza chiamarla genererà automaticamente il chiamabile corrispondente. Ciò si può utilizzare per passare le funzioni come argomenti.

func map(arr: Array, function: Callable) -> Array:
    var result = []
    for item in arr:
        result.push_back(function.call(item))
    return result

func add1(value: int) -> int:
    return value + 1;

func _ready() -> void:
    var my_array = [1, 2, 3]
    var plus_one = map(my_array, add1)
    print(plus_one) # Prints `[2, 3, 4]`.

Nota

I chiamabili devono essere chiamati con il metodo call(). Non è possibile utilizzare direttamente l'operatore (). Questo comportamento è implementato per evitare problemi di prestazioni nelle chiamate dirette di funzioni.

Funzioni lambda

Le funzioni lambda consentono di dichiarare funzioni che non appartengono a una classe. Invece, viene creato un oggetto Callable e assegnato direttamente a una variabile. Ciò può essere utile per creare chiamabili da passare in giro senza inquinare l'ambito della classe.

var lambda = func (x):
    print(x)

Per chiamare la lambda creata è possibile usare il metodo call():

lambda.call(42) # Prints `42`.

È possibile assegnare un nome alle funzioni Lambda, per motivi di debug (il nome è visualizzato nel Debugger):

var lambda = func my_lambda(x):
    print(x)

È possibile specificare suggerimenti del tipo per le funzioni lambda nello stesso modo in cui si fa per quelle normali:

var lambda := func (x: int) -> void:
    print(x)

Note that if you want to return a value from a lambda function, an explicit return is required (you can't omit return):

var lambda = func (x): return x ** 2
print(lambda.call(2)) # Prints `4`.

Le funzioni lambda catturano l'ambiente locale:

var x = 42
var lambda = func ():
    print(x) # Prints `42`.
lambda.call()

Avvertimento

Le variabili locali vengono catturate per valore una sola volta, quando viene creata la lambda. Pertanto, non verranno aggiornate all'interno della lambda se riassegnate nella funzione esterna:

var x = 42
var lambda = func (): print(x)
lambda.call() # Prints `42`.
x = "Hello"
lambda.call() # Prints `42`.

Inoltre, una lambda non può riassegnare una variabile locale esterna. Dopo l'uscita dalla lambda, la variabile rimarrà invariata, perché la cattura nella lambda la oscura implicitamente:

var x = 42
var lambda = func ():
    print(x) # Prints `42`.
    x = "Hello" # Produces the `CONFUSABLE_CAPTURE_REASSIGNMENT` warning.
    print(x) # Prints `Hello`.
lambda.call()
print(x) # Prints `42`.

Tuttavia, se si utilizzano tipi di dati passati per riferimento (array, dizionari e oggetti), le modifiche al contenuto vengono condivise finché non la variabile non viene riassegnata:

var a = []
var lambda = func ():
    a.append(1)
    print(a) # Prints `[1]`.
    a = [2] # Produces the `CONFUSABLE_CAPTURE_REASSIGNMENT` warning.
    print(a) # Prints `[2]`.
lambda.call()
print(a) # Prints `[1]`.

Funzioni statiche

Una funzione può essere dichiarata statica. Quando una funzione è statica, non ha accesso alle variabili membro di un'istanza o a self. Una funzione statica ha accesso a variabili statiche. Le funzioni statiche sono utili anche per creare librerie di funzioni ausiliari:

static func sum2(a, b):
    return a + b

Le funzioni lambda non possono essere dichiarate statiche.

Consultare anche Static variables e Static constructor.

Dichiarazioni e flusso di controllo

Le istruzioni sono standard e possono essere assegnamenti, chiamate di funzioni, strutture di flusso di controllo, ecc. (vedere sotto). Utilizzare ; come separatore tra le istruzioni è completamente facoltativo.

Espressioni

Le espressioni sono sequenze ordinate di operatori e dei loro operandi. Un'espressione di per sé può anche essere un'istruzione, sebbene sia ragionevole utilizzare come istruzioni solo le chiamate, poiché altre espressioni non hanno effetti collaterali.

Le espressioni restituiscono valori che si possono assegnare a obiettivi validi. Gli operandi di un operatore possono essere un'altra espressione. Un'assegnazione non è un'espressione e quindi non restituisce alcun valore.

Ecco alcuni esempi di espressioni:

2 + 2 # Binary operation.
-5 # Unary operation.
"okay" if x > 4 else "not okay" # Ternary operation.
x # Identifier representing variable or constant.
x.a # Attribute access.
x[4] # Subscript access.
x > 2 or x < 5 # Comparisons and logic operators.
x == y + 2 # Equality test.
do_something() # Function call.
[1, 2, 3] # Array definition.
{A = 1, B = 2} # Dictionary definition.
preload("res://icon.png") # Preload builtin function.
self # Reference to current instance.

Identifiers, attributes, and subscripts are valid assignment targets. Other expressions cannot be on the left side of an assignment.

self

È possibile utilizzare self per riferirsi all'istanza attuale ed è spesso equivalente a fare riferimento diretto ai simboli disponibili nello script attuale. Tuttavia, self consente anche di accedere a proprietà, metodi e altri nomi definiti dinamicamente (ovvero che si prevede esistano nei sottotipi della classe attuale o che vengono forniti tramite _set() e/o _get()).

extends Node

func _ready():
    # Compile time error, as `my_var` is not defined in the current class or its ancestors.
    print(my_var)
    # Checked at runtime, thus may work for dynamic properties or descendant classes.
    print(self.my_var)

    # Compile time error, as `my_func()` is not defined in the current class or its ancestors.
    my_func()
    # Checked at runtime, thus may work for descendant classes.
    self.my_func()

Avvertimento

Attenzione: accedere ai membri delle classi figlie nella classe padre è spesso considerato una cattiva pratica, poiché confonde l'area di responsabilità di un certo pezzo di codice, rendendo più difficile ragionare sulle relazioni complessiva tra le parti del gioco. Oltre a ciò, si può semplicemente dimenticare che la classe padre aveva aspettative sui suoi discendenti.

if/else/elif

Le condizioni semplici si creano utilizzando la sintassi if/else/elif. Si permette l'uso di parentesi intorno alle condizioni, ma non è obbligatorio. Data la natura dell'indentazione basata sulla tabulazione, è possibile usare elif al posto di else/if` per mantenere un livello di indentazione.

if (expression):
    statement(s)
elif (expression):
    statement(s)
else:
    statement(s)

Le istruzioni brevi si possono scrivere sulla stessa riga della condizione:

if 1 + 1 == 2: return 2 + 2
else:
    var x = 3 + 3
    return x

A volte, potrebbe essere necessario assegnare un valore iniziale diverso in base a un'espressione booleana. In questi casi, le espressioni if ternarie possono rivelarsi utili:

var x = (value) if (expression) else (value)
y += 3 if y < 10 else -1

Le espressioni if ternarie possono essere annidate per gestire più di 2 casi. Quando si annidano espressioni if ternarie, si consiglia di suddividere l'espressione completa su più righe per preservarne la leggibilità:

var count = 0

var fruit = (
        "apple" if count == 2
        else "pear" if count == 1
        else "banana" if count == 0
        else "orange"
)
print(fruit)  # banana

# Alternative syntax with backslashes instead of parentheses (for multi-line expressions).
# Less lines required, but harder to refactor.
var fruit_alt = \
        "apple" if count == 2 \
        else "pear" if count == 1 \
        else "banana" if count == 0 \
        else "orange"
print(fruit_alt)  # banana

Potrebbe anche essere necessario verificare se un valore è contenuto in qualcosa. Si più utilizzare un'istruzione if combinata con l'operatore in per ottenere questo risultato:

# Check if a letter is in a string.
var text = "abc"
if 'b' in text: print("The string contains b")

# Check if a variable is contained within a node.
if "varName" in get_parent(): print("varName is defined in parent!")

while

I cicli semplici si creano utilizzando la sintassi while. I cicli si possono interrompere tramite break o continuare tramite continue (che passa all'iterazione successiva del ciclo senza eseguire ulteriore codice nell'iterazione in corso):

while (expression):
    statement(s)

for

Per iterare su un intervallo, come un array o una tabella, si utilizza un ciclo for. Quando si itera su un array, l'elemento attuale dell'array viene memorizzato nella variabile del ciclo. Quando si itera su un dizionario, la chiave viene memorizzata nella variabile del ciclo.

for x in [5, 7, 11]:
    statement # Loop iterates 3 times with 'x' as 5, then 7 and finally 11.

var names = ["John", "Marta", "Samantha", "Jimmy"]
for name: String in names: # Typed loop variable.
    print(name) # Prints name's content.

var dict = {"a": 0, "b": 1, "c": 2}
for i in dict:
    print(dict[i]) # Prints 0, then 1, then 2.

for i in range(3):
    statement # Similar to [0, 1, 2] but does not allocate an array.

for i in range(1, 3):
    statement # Similar to [1, 2] but does not allocate an array.

for i in range(2, 8, 2):
    statement # Similar to [2, 4, 6] but does not allocate an array.

for i in range(8, 2, -2):
    statement # Similar to [8, 6, 4] but does not allocate an array.

for c in "Hello":
    print(c) # Iterate through all characters in a String, print every letter on new line.

for i in 3:
    statement # Similar to range(3).

for i in 2.2:
    statement # Similar to range(ceil(2.2)).

Per assegnare valori a un array durante la sua iterazione, è meglio utilizzare for i in array.size().

for i in array.size():
    array[i] = "Hello World"

La variabile del ciclo è locale al ciclo for e assegnarla non modifica il valore dell'array. Gli oggetti passati per riferimento (come i nodi) si possono comunque manipolare chiamando metodi sulla variabile del ciclo.

for string in string_array:
    string = "Hello World" # This has no effect

for node in node_array:
    node.add_to_group("Cool_Group") # This has an effect

match

Un'istruzione match viene utilizzata per diramare l'esecuzione di un programma. È l'equivalente dell'istruzione switch presente in molti altri linguaggi, ma offre alcune funzionalità aggiuntive.

Avvertimento

match è più restrittivo rispetto all'operatore ==. Ad esempio, 1 non corrisponderà a 1.0. L'unica eccezione è la corrispondenza tra String e StringName: ad esempio, la stringa "hello" è considerata uguale allo StringName &"hello".

Sintassi di base

match <test value>:
    <pattern(s)>:
        <block>
    <pattern(s)> when <pattern guard>:
        <block>
    <...>

Corso accelerato per chi ha familiarità con le istruzioni switch

  1. Sostituire switch con match.

  2. Rimuovere case.

  3. Rimuovere eventuali break.

  4. Sostituire default con un singolo trattino basso (_).

Flusso di controllo

Gli schemi vengono abbinati dall'alto verso il basso. Se uno schema corrisponde, verrà eseguito il primo blocco corrispondente. Successivamente, l'esecuzione continua sotto l'istruzione match.

Nota

Il comportamento speciale di continue in match supportato nella versione 3.x è stato rimosso in Godot 4.0.

Sono disponibili i seguenti tipi di schema:

  • Schema letterale

    Corrisponde a un letterale:

    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")
    
  • Schema di espressione

    Corrisponde a un'espressione costante, a un identificatore o a un accesso agli attributo (A.B):

    match typeof(x):
        TYPE_FLOAT:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Schema jolly (wildcard)

    Questo schema corrisponde a tutto. È scritto come un singolo trattino basso (_).

    Può essere utilizzato come equivalente di default di un'istruzione switch in altri linguaggi:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        _:
            print("It's not 1 or 2. I don't care to be honest.")
    
  • Schema vincolante

    Un schema vincolante introduce una nuova variabile. Come lo schema jolly, corrisponde a tutto e assegna anche un nome al valore corrispondente. È particolarmente utile negli schemi di array e di dizionari:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        var new_var:
            print("It's not 1 or 2, it's ", new_var)
    
  • Schema di array

    Corrisponde a un array. Ogni singolo elemento dello schema di array è a sua volta uno schema, quindi è possibile annidarli.

    Viene prima testata la dimensione dell'array: deve essere della stessa dimensione dello schema, altrimenti lo schema non corrisponde.

    Open-ended array: An array can be bigger than the pattern by making the last subpattern ...

    Ogni sotto-schema deve essere separato da virgole.

    match x:
        []:
            print("Empty array")
        [1, 3, "test", null]:
            print("Very specific array")
        [var start, _, "test"]:
            print("First element is ", start, ", and the last is \"test\"")
        [42, ..]:
            print("Open ended array")
    
  • Schema di dizionario

    Funziona allo stesso modo dello schema di array. Ogni chiave deve essere uno schema costante.

    Viene prima testata la dimensione del dizionario: deve essere della stessa dimensione dello schema, altrimenti lo schema non corrisponde.

    Open-ended dictionary: A dictionary can be bigger than the pattern by making the last subpattern ...

    Ogni sotto-schema deve essere separato da una virgola.

    Se non si specifica un valore, viene verificata solo l'esistenza della chiave.

    A value pattern is separated from the key pattern with a :.

    match x:
        {}:
            print("Empty dict")
        {"name": "Dennis"}:
            print("The name is Dennis")
        {"name": "Dennis", "age": var age}:
            print("Dennis is ", age, " years old.")
        {"name", "age"}:
            print("Has a name and an age, but it's not Dennis :(")
        {"key": "godotisawesome", ..}:
            print("I only checked for one entry and ignored the rest")
    
  • Più di uno schema

    È anche possibile specificare più schemi separati da una virgola. Questi schemi non possono contenere vincoli.

    match x:
        1, 2, 3:
            print("It's 1 - 3")
        "Sword", "Splash potion", "Fist":
            print("Yep, you've taken damage")
    

Pattern guards

A pattern guard is an optional condition that follows the pattern list and allows you to make additional checks before choosing a match branch. Unlike a pattern, a pattern guard can be an arbitrary expression.

Only one branch can be executed per match. Once a branch is chosen, the rest are not checked. If you want to use the same pattern for multiple branches or to prevent choosing a branch with too general pattern, you can specify a pattern guard after the list of patterns with the when keyword:

match point:
    [0, 0]:
        print("Origin")
    [_, 0]:
        print("Point on X-axis")
    [0, _]:
        print("Point on Y-axis")
    [var x, var y] when y == x:
        print("Point on line y = x")
    [var x, var y] when y == -x:
        print("Point on line y = -x")
    [var x, var y]:
        print("Point (%s, %s)" % [x, y])
  • If there is no matching pattern for the current branch, the pattern guard is not evaluated and the patterns of the next branch are checked.

  • If a matching pattern is found, the pattern guard is evaluated.

    • Se è vera, allora il corpo del ramo viene eseguito e match termina.

    • Se è falsa, allora vengono verificati gli schemi del ramo successivo.

Classi

Come predefinito, tutti i file di script sono classi senza nome. In questo caso, vi si può solo far riferimento tramite il percorso del file, usando un percorso relativo o assoluto. Ad esempio, se si denomina un file di script character.gd:

# Inherit from 'character.gd'.

extends "res://path/to/character.gd"

# Load character.gd and create a new node instance from it.

var Character = load("res://path/to/character.gd")
var character_node = Character.new()

Registrazione di classi con nome

Si può assegnare un nome alla propria classe per registrarla come nuovo tipo nell'editor di Godot. Per fare ciò, si usa la parola chiave class_name. È possibile facoltativamente utilizzare l'annotazione @icon avente un percorso a un'immagine, per utilizzare tale come icona. La classe apparirà quindi con la sua nuova icona nell'editor:

# item.gd

@icon("res://interface/icons/item.png")
class_name Item
extends Node
../../../_images/class_name_editor_register_example.png

Suggerimento

Le immagini SVG utilizzate come icone personalizzate di nodi devono avere le opzioni di importazione Editor > Scala con la scala dell'editor e Editor > Converti le icone con il tema dell'editor abilitate. Ciò consente alle icone di seguire la scala dell'editor e le impostazioni del tema, se esse sono progettate con la stessa tavolozza di colori usata dalle icone di Godot.

Ecco un esempio di file di classe:

# Saved as a file named 'character.gd'.

class_name Character


var health = 5


func print_health():
    print(health)


func print_this_script_three_times():
    print(get_script())
    print(ResourceLoader.load("res://character.gd"))
    print(Character)

Se si desidera usare anche extends, si possono mantenere entrambi sulla stessa riga:

class_name MyNode extends Node

Named classes are globally registered, which means they become available to use in other scripts without the need to load or preload them:

var player

func _ready():
    player = Character.new()

Nota

Godot inizializza le variabili non statiche ogni volta che viene creata un'istanza, inclusi array e dizionari. Questo è in linea con lo spirito di sicurezza dei thread, poiché gli script si possono inizializzare in thread separati senza che l'utente lo sappia.

Avvertimento

The Godot editor will hide these custom classes with names that begin with the prefix "Editor" in the 'Create New Node' or 'Create New Scene' dialog windows. The classes are available for instantiation at runtime via their class names, but are automatically hidden by the editor windows along with the built-in editor nodes used by the Godot editor.

Ereditarietà

Una classe (memorizzata come file) può ereditare da:

  • Una classe globale.

  • Un altro file di classe.

  • Una classe interna all'interno di un altro file di classe.

L'ereditarietà multipla non è consentita.

L'ereditarietà utilizza la parola chiave extends:

# Inherit/extend a globally available class.
extends SomeClass

# Inherit/extend a named class file.
extends "somefile.gd"

# Inherit/extend an inner class in another file.
extends "somefile.gd".SomeInnerClass

Nota

Se l'ereditarietà non è definita esplicitamente, la classe erediterà come predefinito RefCounted.

Per verificare se un'istanza eredita da una determinata classe, è possibile utilizzare la parola chiave is:

# Cache the enemy class.
const Enemy = preload("enemy.gd")

# [...]

# Use 'is' to check inheritance.
if entity is Enemy:
    entity.apply_damage()

Per chiamare una funzione in una classe padre (vale a dire una classe estesa (extend) dalla classe attuale), utilizzare la parola chiave super:

super(args)

Ciò è particolarmente utile perché le funzioni nelle classi estese sovrascrivono le funzioni con lo stesso nome nelle loro classi padre. Se si desidera chiamarle comunque, è possibile utilizzare super:

func some_func(x):
    super(x) # Calls the same function on the super class.

Se è necessario chiamare una funzione differente dalla classe padre, è possibile specificare il nome della funzione con l'operatore di attributo:

func overriding():
    return 0 # This overrides the method in the base class.

func dont_override():
    return super.overriding() # This calls the method as defined in the base class.

Avvertimento

Uno dei malintesi più comuni è cercare di sovrascrivere i metodi non virtuali del motore, come get_class(), queue_free(), ecc. Questo non è supportato per motivi tecnici.

In Godot 3, è possibile oscurare i metodi del motore in GDScript, e funzionerà se si chiama questo metodo in GDScript. Tuttavia, il motore non eseguirà il codice se il metodo viene chiamato al suo interno in seguito a un evento.

In Godot 4, l'oscuramento non funziona sempre, poiché GDScript ottimizza le chiamate ai metodi nativi. Pertanto, è stato introdotto l'avviso NATIVE_METHOD_OVERRIDE, il quale viene trattato come errore come predefinito. Si sconsiglia vivamente di disabilitare o ignorare l'avviso.

Si noti che ciò non si applica ai metodi virtuali come _ready(), _process() e altri (contrassegnati con il qualificatore virtual nella documentazione e i cui nomi iniziano con un trattino basso). Questi metodi servono specificamente per personalizzare il comportamento del motore e possono essere sovrascritti in GDScript. Segnali e notifiche possono anche essere utili a questo scopo.

Costruttore di classe

Il costruttore della classe, chiamato durante l'istanziazione della classe, è denominato _init. Per chiamare il costruttore della classe padre, è possibile utilizzare anche la sintassi super. Si noti che ogni classe ha un costruttore implicito che viene sempre chiamato (definendo i valori predefiniti delle variabili della classe). super è utilizzato per chiamare il costruttore esplicito:

func _init(arg):
   super("some_default", arg) # Call the custom base constructor.

Questo è meglio spiegato attraverso degli esempi. Si consideri questo scenario:

# state.gd (inherited class).
var entity = null
var message = null


func _init(e=null):
    entity = e


func enter(m):
    message = m


# idle.gd (inheriting class).
extends "state.gd"


func _init(e=null, m=null):
    super(e)
    # Do something with 'e'.
    message = m

Ci sono alcune cose da tenere a mente qui:

  1. Se la classe ereditata (state.gd) definisce un costruttore _init che accetta argomenti (e in questo caso), allora anche la classe ereditaria (idle.gd) deve definire _init e passare i parametri appropriati a _init da state.gd.

  2. idle.gd può avere un numero diverso di argomenti rispetto alla classe padre state.gd.

  3. Nell'esempio sopra, il parametro e passato al costruttore state.gd è lo stesso parametro e passato a idle.gd.

  4. Se il costruttore _init di idle.gd accetta 0 argomenti, deve comunque passare un valore alla classe padre state.gd, anche se non fa nulla. Questo porta al fatto che è possibile passare anche espressioni al costruttore padre, non solo variabili, ad esempio:

    # idle.gd
    
    func _init():
        super(5)
    

Costruttore statico

Un costruttore statico è una funzione statica _static_init che viene chiamata automaticamente quando la classe viene caricata, dopo che le variabili statiche sono state inizializzate:

static var my_static_var = 1

static func _static_init():
    my_static_var = 2

Un costruttore statico non può accettare argomenti e non deve restituire alcun valore.

Classi interne

Un file di classe può contenere classi interne. Le classi interne sono definite usando la parola chiave class. Vengono istanziate tramite la funzione ClassName.new().

# Inside a class file.

# An inner class in this class file.
class SomeInnerClass:
    var a = 5


    func print_value_of_a():
        print(a)


# This is the constructor of the class file's main class.
func _init():
    var c = SomeInnerClass.new()
    c.print_value_of_a()

Classi come risorse

Le classi memorizzate come file vengono trattate come risorse GDScript. Devono essere caricate dal disco per potervi accedere in altre classi. Ciò si ottiene tramite le funzioni load o preload (vedere sotto). Per istanziare una risorsa caricata di una classe, si può chiamare la funzione new sull'oggetto della classe:

# Load the class resource when calling load().
var MyClass = load("myclass.gd")

# Preload the class only once at compile time.
const MyClass = preload("myclass.gd")


func _init():
    var a = MyClass.new()
    a.some_function()

Esportazioni

Nota

La documentazione sulle esportazioni è stata spostata in Proprietà esportate in GDScript.

Proprietà (setter e getter)

Sometimes, you want a class' member variable to do more than just hold data and actually perform some validation or computation whenever its value changes. It may also be desired to encapsulate its access in some way.

A questo scopo, GDScript fornisce una sintassi speciale per definire le proprietà, attraverso le parole chiave set e get dopo la dichiarazione di una variabile. È quindi possibile definire un blocco di codice che verrà eseguito quando si accede alla variabile o quando la variabile viene assegnata.

Esempio:

var milliseconds: int = 0
var seconds: int:
    get:
        return milliseconds / 1000
    set(value):
        milliseconds = value * 1000

Nota

A differenza di setget nelle versioni precedenti di Godot, i metodi set e get vengono sempre chiamati (tranne quanto indicato di seguito), anche quando vi si accede all'interno della stessa classe (con o senza il prefisso self). Ciò rende il comportamento coerente. Se è necessario accedere direttamente al valore, utilizzare un'altra variabile per l'accesso diretto e assicurarsi che il codice della proprietà utilizzi quel nome.

Sintassi alternativa

Esiste anche un'altra notazione per utilizzare le funzioni esistenti della classe, se si desidera suddividere il codice dalla dichiarazione della variabile o riutilizzare il codice su più proprietà (ma non è possibile distinguere per quale proprietà viene chiamato il setter o getter):

var my_prop:
    get = get_my_prop, set = set_my_prop

Questo si può fare anche sulla stessa riga:

var my_prop: get = get_my_prop, set = set_my_prop

Il setter e il getter devono utilizzare la stessa notazione; non è consentito mischiare stili per la stessa variabile.

Nota

Non è possibile specificare suggerimenti del tipo per le funzioni setter e getter sulla stessa riga. Questo è fatto apposta per ridurre il boilerplate. Se la variabile è tipizzata, l'argomento del setter è automaticamente dello stesso tipo e il valore restituito dal getter deve corrispondere. Le funzioni setter o getter separate possono avere suggerimenti del tipo, e i loro tipi devono corrispondere al tipo della variabile o essere più ampi.

Quando i setter o getter non vengono chiamati

Quando una variabile viene inizializzata, il valore iniziale verrà scritto direttamente nella variabile, anche se l'annotazione @onready è applicata alla variabile.

Utilizzando il nome della variabile per impostarla all'interno del proprio setter o per ottenerla all'interno del proprio getter si accederà direttamente al membro sottostante, in modo da non creare un ciclo infinito e evitando di dover dichiarare esplicitamente un'altra variabile:

signal changed(new_value)
var warns_when_changed = "some value":
    get:
        return warns_when_changed
    set(value):
        changed.emit(value)
        warns_when_changed = value

Ciò si applica anche per la sintassi alternativa:

var my_prop: set = set_my_prop

func set_my_prop(value):
    my_prop = value # No infinite recursion.

Avvertimento

Questa eccezione non si propaga ad altre funzioni chiamate nel setter o nel getter. Ad esempio, il seguente codice provocherà un ciclo infinito:

var my_prop:
    set(value):
        set_my_prop(value)

func set_my_prop(value):
    my_prop = value # Infinite recursion, since `set_my_prop()` is not the setter.

Modalità strumento (tool)

Come predefinito, gli script non si eseguono all'interno dell'editor e solo le proprietà esportate si possono modificare. In alcuni casi, è preferibile che siano eseguiti all'interno dell'editor (a condizione che non eseguano codice di gioco o che non lo facciano manualmente). Per questo scopo, si può inserire l'annotazione @tool all'inizio del file:

@tool
extends Button

func _ready():
    print("Hello")

Consultare Running code in the editor per maggiori informazioni.

Avvertimento

Prestare attenzione quando si liberano nodi con queue_free() o free() in uno script strumento (soprattutto il proprietario dello script stesso). Poiché gli script strumento eseguono il loro codice nell'editor, un uso improprio potrebbe causarne l'arresto anomalo.

Gestione della memoria

Godot implementa il conteggio dei riferimenti per liberare determinate istanze che non sono più in uso, al posto di un collettore di memoria (garbage collector) o una gestione puramente manuale. Qualsiasi istanza della classe RefCounted (o qualsiasi classe che la eredita, come Resource) verrà liberata automaticamente quando non sarà più in uso. Per un'istanza di qualsiasi classe che non sia un RefCounted (come Node o il tipo base Object), rimarrà in memoria finché non verrà eliminata con free() (o queue_free() per i nodi).

Nota

Se un Node viene eliminato tramite free() o queue_free(), anche tutti i suoi nodi figlio verranno eliminati ricorsivamente.

Per evitare cicli di riferimento che non si possono liberare, viene fornita la funzione WeakRef per creare riferimenti deboli, il che consentono di accedere all'oggetto senza impedire a un RefCounted di liberarsi. Ecco un esempio:

extends Node

var my_file_ref

func _ready():
    var f = FileAccess.open("user://example_file.json", FileAccess.READ)
    my_file_ref = weakref(f)
    # the FileAccess class inherits RefCounted, so it will be freed when not in use

    # the WeakRef will not prevent f from being freed when other_node is finished
    other_node.use_file(f)

func _this_is_called_later():
    var my_file = my_file_ref.get_ref()
    if my_file:
        my_file.close()

In alternativa, quando non si utilizzano riferimenti, è possibile utilizzare is_instance_valid(instance) per verificare se un oggetto è stato liberato.

Segnali

I segnali sono uno strumento per emettere messaggi da un oggetto a cui altri oggetti possono reagire. Per creare segnali personalizzati per una classe, utilizzare la parola chiave signal.

extends Node


# A signal named health_depleted.
signal health_depleted

Nota

I segnali sono un meccanismo di callback. Svolgono anche il ruolo di osservatori, un modello di progettazione comune nella programmazione. Per ulteriori informazioni, leggere il tutorial sugli osservatori nell'ebook "Game Programming Patterns".

È possibile collegare questi segnali ai metodi nello stesso modo in cui si collegano i segnali integrati dei nodi quali Button o RigidBody3D.

Nell'esempio seguente, viene collegato il segnale health_depleted da un nodo Character a un nodo Game. Quando il nodo Character emette il segnale, verrà chiamato il nodo _on_character_health_depleted del nodo Game:

# game.gd

func _ready():
    var character_node = get_node('Character')
    character_node.health_depleted.connect(_on_character_health_depleted)


func _on_character_health_depleted():
    get_tree().reload_current_scene()

È possibile emettere tanti quanti argomenti si desiderano insieme a un segnale.

Ecco un esempio in cui questo è utile. Si suppone di volere una barra della vita sullo schermo che reagisce ai cambiamenti di salute con un'animazione, ma si desidera mantenere l'interfaccia utente separata dal giocatore nell'albero di scene.

Nello script character.gd, un segnale health_changed è definito ed emesso tramite il metodo Signal.emit(), e da un nodo Game più in alto nell'albero di scena, viene collegato al nodo Lifebar tramite il metodo Signal.connect():

# character.gd

...
signal health_changed


func take_damage(amount):
    var old_health = health
    health -= amount

    # We emit the health_changed signal every time the
    # character takes damage.
    health_changed.emit(old_health, health)
...
# lifebar.gd

# Here, we define a function to use as a callback when the
# character's health_changed signal is emitted.

...
func _on_Character_health_changed(old_value, new_value):
    if old_value > new_value:
        progress_bar.modulate = Color.RED
    else:
        progress_bar.modulate = Color.GREEN

    # Imagine that `animate` is a user-defined function that animates the
    # bar filling up or emptying itself.
    progress_bar.animate(old_value, new_value)
...

Nel nodo Game, si ottengono entrambi i nodi Character e Lifebar, quindi viene collegato il personaggio, il quale emette il segnale, al ricevitore, in questo caso il nodo Lifebar.

# game.gd

func _ready():
    var character_node = get_node('Character')
    var lifebar_node = get_node('UserInterface/Lifebar')

    character_node.health_changed.connect(lifebar_node._on_Character_health_changed)

Ciò consente al nodo Lifebar di reagire ai cambiamenti di punti vita senza doverla accoppiare al nodo Character.

È possibile aggiungere nomi degli argomenti facoltativi tra parentesi dopo la definizione del segnale:

# Defining a signal that forwards two arguments.
signal health_changed(old_value, new_value)

Questi argomenti verranno visualizzati nel pannello dei nodi dell'editor e Godot può utilizzarli per generare funzioni di callback. Tuttavia, è comunque possibile emettere un numero qualsiasi di argomenti quando si emettono segnali; è responsabilità del programmatore emettere i valori corretti.

../../../_images/gdscript_basics_signals_node_tab_1.png

You can also create copies of GDScript Callable objects which accept additional arguments using Callable.bind(). This allows you to add extra information to the connection if the emitted signal itself doesn't give you access to all the data that you need.

When the signal is emitted, the callback method receives the bound values, in addition to those provided by the signal.

Building on the example above, let's say we want to display a log of the damage taken by each character on the screen, like Player1 took 22 damage.. The health_changed signal doesn't give us the name of the character that took damage. So when we connect the signal to the in-game console, we can add the character's name using the bind method:

# game.gd

func _ready():
    var character_node = get_node('Character')
    var battle_log_node = get_node('UserInterface/BattleLog')

    character_node.health_changed.connect(battle_log_node._on_Character_health_changed.bind(character_node.name))

Our BattleLog node receives each bound element as an extra argument:

# battle_log.gd

func _on_Character_health_changed(old_value, new_value, character_name):
    if not new_value <= old_value:
        return

    var damage = old_value - new_value
    label.text += character_name + " took " + str(damage) + " damage."

Attendere segnali o coroutine

The await keyword can be used to create coroutines which wait until a signal is emitted before continuing execution. Using the await keyword with a signal or a call to a function that is also a coroutine will immediately return the control to the caller. When the signal is emitted (or the called coroutine finishes), it will resume execution from the point on where it stopped.

Ad esempio, per interrompere l'esecuzione finché l'utente non preme un pulsante, si può fare qualcosa del genere:

func wait_confirmation():
    print("Prompting user")
    await $Button.button_up # Waits for the button_up signal from Button node.
    print("User confirmed")
    return true

In questo caso, wait_confirmation diventa una coroutine, il che significa che anche il chiamante deve attenderla:

func request_confirmation():
    print("Will ask the user")
    var confirmed = await wait_confirmation()
    if confirmed:
        print("User confirmed")
    else:
        print("User cancelled")

Si noti che richiedere il valore restituito di una coroutine senza await genererà un errore:

func wrong():
    var confirmed = wait_confirmation() # Will give an error.

Tuttavia, se non si dipende dal risultato, è possibile semplicemente chiamarla in modo asincrono, il che non interromperà l'esecuzione e non trasformerà la funzione attuale in una coroutine:

func okay():
    wait_confirmation()
    print("This will be printed immediately, before the user press the button.")

Se si utilizza await con un'espressione che non è un segnale né una coroutine, il valore verrà restituito immediatamente e la funzione non restituirà il controllo al chiamante:

func no_wait():
    var x = await get_five()
    print("This doesn't make this function a coroutine.")

func get_five():
    return 5

Ciò significa anche che, se si restituisce un segnale da una funzione che non è una coroutine, il chiamante sarà costretto ad attendere il segnale:

func get_signal():
    return $Button.button_up

func wait_button():
    await get_signal()
    print("Button was pressed")

Nota

A differenza di yield nelle precedenti versioni di Godot, non è possibile ottenere l'oggetto di stato della funzione. Ciò garantisce la sicurezza del tipo. In questo modo, una funzione non può dichiarare di restituire un int mentre in realtà restituisce un oggetto di stato della funzione durante l'esecuzione.

Parola chiave Assert

La parola chiave assert si può utilizzare per verificare condizioni nelle build di debug. Queste asserzioni sono ignorate nelle build non di debug. Ciò significa che l'espressione passata come argomento non verrà valutata in un progetto esportato in modalità di rilascio. Per questo motivo, le asserzioni non devono contenere espressioni con effetti collaterali. Altrimenti, il comportamento dello script varierà a seconda che venga eseguito in una build di debug o di rilascio.

# Check that 'i' is 0. If 'i' is not 0, an assertion error will occur.
assert(i == 0)

Quando si esegue un progetto dall'editor, il progetto verrà messo in pausa in caso di errore di asserzione.

È possibile passare facoltativamente un messaggio di errore personalizzato da visualizzare se l'asserzione fallisce:

assert(enemy_power < 256, "Enemy is too powerful!")