Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Guida di stile di GDScript
Questa guida di stile elenca le convenzioni per scrivere codice elegante in GDScript. L'obiettivo è incoraggiare la scrittura di codice pulito e leggibile e promuovere la conformità tra progetti, discussioni e tutorial. Ci auguriamo che questo possa supportare anche lo sviluppo di strumenti di formattazione automatica.
Poiché GDScript è simile a Python, questa guida si ispira alla guida di stile di programmazione PEP 8.
Le guide di stile non sono intese come regole rigide. A volte, potresti non essere in grado di applicare alcune delle linee guida riportate di seguito. In tal caso, usa il tuo buon senso e chiedi consiglio ad altri sviluppatori.
In generale, è più importante mantenere consistente il codice nei progetti e tra il team piuttosto che seguire questa guida alla lettera.
Nota
L'editor di script integrato in Godot utilizza molte di queste convenzioni come predefinito. Lascia che ti aiuti.
Ecco un esempio completo di una classe basato su queste linee guida:
class_name StateMachine
extends Node
## Hierarchical State machine for the player.
##
## Initializes states and delegates engine callbacks ([method Node._physics_process],
## [method Node._unhandled_input]) to the state.
signal state_changed(previous, new)
@export var initial_state: Node
var is_active = true:
set = set_is_active
@onready var _state = initial_state:
set = set_state
@onready var _state_name = _state.name
func _init():
add_to_group("state_machine")
func _enter_tree():
print("this happens before the ready method!")
func _ready():
state_changed.connect(_on_state_changed)
_state.enter()
func _unhandled_input(event):
_state.unhandled_input(event)
func _physics_process(delta):
_state.physics_process(delta)
func transition_to(target_state_path, msg={}):
if not has_node(target_state_path):
return
var target_state = get_node(target_state_path)
assert(target_state.is_composite == false)
_state.exit()
self._state = target_state
_state.enter(msg)
Events.player_state_changed.emit(_state.name)
func set_is_active(value):
is_active = value
set_physics_process(value)
set_process_unhandled_input(value)
set_block_signals(not value)
func set_state(value):
_state = value
_state_name = _state.name
func _on_state_changed(previous, new):
print("state changed")
state_changed.emit()
class State:
var foo = 0
func _init():
print("Hello!")
Formattazione
Codifica e caratteri speciali
Usa i caratteri di avanzamento riga (LF) per separare le righe, non CRLF o CR. (predefinito dell'editor)
Usa un carattere di avanzamento riga alla fine di ogni file. (predefinito dell'editor)
Usa la codifica UTF-8 senza byte order mark. (predefinito dell'editor)
Usa Tabulazioni invece di spazi per l'indentazione. (predefinito dell'editor)
Indentazione
Ogni livello di indentazione dovrebbe essere più grande di un'unità rispetto al blocco che lo contiene.
Bene:
for i in range(10):
print("hello")
Male:
for i in range(10):
print("hello")
for i in range(10):
print("hello")
Usa 2 livelli di indentazione per distinguere le righe di continuazione dai regolari blocchi di codice.
Bene:
effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT)
Male:
effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT)
Le eccezioni a questa regola sono array, dizionari ed enumerazioni. Usa un singolo livello di indentazione per distinguere le righe di continuazione:
Bene:
var party = [
"Godot",
"Godette",
"Steve",
]
var character_dict = {
"Name": "Bob",
"Age": 27,
"Job": "Mechanic",
}
enum Tile {
BRICK,
FLOOR,
SPIKE,
TELEPORT,
}
Male:
var party = [
"Godot",
"Godette",
"Steve",
]
var character_dict = {
"Name": "Bob",
"Age": 27,
"Job": "Mechanic",
}
enum Tile {
BRICK,
FLOOR,
SPIKE,
TELEPORT,
}
Virgola finale
Usa una virgola finale sull'ultima riga in array, dizionari ed enumerazioni. Questo facilita il refactoring e migliora le differenze (diffs) nel controllo versione, in quanto non c'è bisogno di modificare l'ultima riga quando si aggiungono nuovi elementi.
Bene:
var array = [
1,
2,
3,
]
Male:
var array = [
1,
2,
3
]
Le virgole finali non sono necessarie nelle liste in una sola riga, quindi non aggiungerle in questo caso.
Bene:
var array = [1, 2, 3]
Male:
var array = [1, 2, 3,]
Righe vuote
Circonda le funzioni e le definizioni di classe con due righe vuote:
func heal(amount):
health += amount
health = min(health, max_health)
health_changed.emit(health)
func take_damage(amount, effect=null):
health -= amount
health = max(0, health)
health_changed.emit(health)
Utilizza una riga vuota all'interno delle funzioni per separare le sezioni logiche.
Nota
Noi utilizziamo una sola riga tra le definizioni di classi e funzioni nel riferimento classi e nei brevi frammenti di codice presenti in questa documentazione.
Lunghezza delle righe
Mantieni le singole righe di codice al di sotto di 100 caratteri.
Se possibile, cerca di mantenere il testo sotto 80 caratteri per riga. Ciò aiuta a leggere il codice su schermi piccoli e di aprire due script affiancati in un editor di testo esterno. Ad esempio, quando si esamina una revisione differenziale.
Una sola istruzione per riga
Evita di combinare più istruzioni su una singola riga, comprese le istruzioni condizionali, per aderire alle linee guida di stile GDScript per la leggibilità.
Bene:
if position.x > width:
position.x = 0
if flag:
print("flagged")
Male:
if position.x > width: position.x = 0
if flag: print("flagged")
L'unica eccezione a questa regola è l'operatore ternario:
next_state = "idle" if is_on_floor() else "fall"
Formattare le istruzioni su più righe per migliorarne la leggibilità
In presenza di istruzioni if particolarmente lunghe o espressioni ternarie annidate, suddividerle su più righe ne migliora la leggibilità. Poiché le righe di continuazione fanno comunque parte della stessa espressione, si dovrebbero utilizzare 2 livelli di indentazione anziché uno.
GDScript consente di scrivere istruzioni su più righe tramite parentesi o barre rovesciate. Le parentesi sono preferite in questa guida di stile perché facilitano il refactoring. Con le barre rovesciate, è necessario assicurarsi che l'ultima riga non finisca mai con una barra rovesciata. Con le parentesi, non è necessario preoccuparsi di ciò.
Quando si suddivide un'espressione condizionale su più righe, le parole chiave and/or si devono posizionare all'inizio della continuazione di riga, non alla fine della riga precedente.
Bene:
var angle_degrees = 135
var quadrant = (
"northeast" if angle_degrees <= 90
else "southeast" if angle_degrees <= 180
else "southwest" if angle_degrees <= 270
else "northwest"
)
var position = Vector2(250, 350)
if (
position.x > 200 and position.x < 400
and position.y > 300 and position.y < 400
):
pass
Male:
var angle_degrees = 135
var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest"
var position = Vector2(250, 350)
if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400:
pass
Evita parentesi inutili
Evita le parentesi nelle espressioni e nelle istruzioni condizionali. A meno che non siano necessarie per l'ordine delle operazioni o per suddividerle su più righe, riducono solo la leggibilità.
Bene:
if is_colliding():
queue_free()
Male:
if (is_colliding()):
queue_free()
Operatori booleani
Preferisci le versioni in inglese semplice degli operatori booleani, poiché sono le più accessibili:
Utilizza
andinvece di&&.Utilizza
orinvece di||.Utilizza
notinvece di!.
Si potrebbero inoltre usare parentesi intorno agli operatori booleani per evitare qualsiasi ambiguità. Facendo così potrebbe essere più facile leggere le espressioni lunghe.
Bene:
if (foo and bar) or not baz:
print("condition is true")
Male:
if foo && bar || !baz:
print("condition is true")
Spazio vuoto
Usa sempre un solo spazio tra gli operatori e dopo le virgole. Inoltre, evita spazi in più nei riferimenti di dizionari e nelle chiamate di funzione. Un'eccezione a questa regola riguarda le dichiarazioni di dizionari su una sola riga, in cui bisogna aggiungere uno spazio dopo la parentesi graffa di apertura e prima di quella di chiusura. In questo modo un dizionario è più facile da distinguere visivamente da un array, poiché i caratteri [] appaiono simili a {} con la maggior parte dei font.
Bene:
position.x = 5
position.y = target_position.y + 10
dict["key"] = 5
my_array = [4, 5, 6]
my_dictionary = { key = "value" }
print("foo")
Male:
position.x=5
position.y = mpos.y+10
dict ["key"] = 5
myarray = [4,5,6]
my_dictionary = {key = "value"}
print ("foo")
Non usare spazi per allineare le espressioni verticalmente:
x = 100
y = 100
velocity = 500
Virgolette
Utilizza le virgolette doppie, a meno che le virgolette singole consentano l'escape di meno caratteri in una determinata stringa. Vedi gli esempi seguenti:
# Normal string.
print("hello world")
# Use double quotes as usual to avoid escapes.
print("hello 'world'")
# Use single quotes as an exception to the rule to avoid escapes.
print('hello "world"')
# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"")
Numeri
Non omettere lo zero iniziale o finale nei numeri in virgola mobile. Se no, ciò li renderà meno leggibili e più difficili da distinguere a prima vista dagli interi.
Bene:
var float_number = 0.234
var other_float_number = 13.0
Male:
var float_number = .234
var other_float_number = 13.
Utilizza il minuscolo per le lettere nei numeri esadecimali, poiché la loro altezza ridotta rende i numeri più facili da leggere.
Bene:
var hex_number = 0xfb8c0b
Male:
var hex_number = 0xFB8C0B
Approfitta dei trattini bassi nei letterali in GDScript per rendere più leggibili i numeri grandi.
Bene:
var large_number = 1_234_567_890
var large_hex_number = 0xffff_f8f8_0000
var large_bin_number = 0b1101_0010_1010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12345
Male:
var large_number = 1234567890
var large_hex_number = 0xfffff8f80000
var large_bin_number = 0b110100101010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12_345
Convenzioni di denominazione
Queste convenzioni di denominazione seguono lo stile del Godot Engine. Se non rispettate, il proprio codice andrà in conflitto con le convenzioni esistenti, portando a codice incoerente. Ecco una tabella riassuntiva:
Tipo |
Convenzione |
Esempio |
|---|---|---|
Nomi di file |
snake_case |
|
Nomi di classi |
PascalCase |
|
Nomi di nodi |
PascalCase |
|
Funzioni |
snake_case |
|
Variabili |
snake_case |
|
Segnali |
snake_case |
|
Costanti |
CONSTANT_CASE |
|
Nomi di enum |
PascalCase |
|
Membri di enum |
CONSTANT_CASE |
|
Nomi di file
Utilizza lo snake_case per i nomi dei file. Per le classi con nomi, converti il nome della classe PascalCase a snake_case:
# This file should be saved as `weapon.gd`.
class_name Weapon
extends Node
# This file should be saved as `yaml_parser.gd`.
class_name YAMLParser
extends Object
Questo corrisponde con il modo in cui i file C++ sono denominati nel codice sorgente di Godot. Questo permette anche di evitare problemi di distinzione tra maiuscole e minuscole che possono sorgere durante l'esportazione di un progetto da Windows ad altre piattaforme.
Classi e nodi
Utilizza il PascalCase per i nomi di classi e di nodi:
extends CharacterBody3D
Utilizza inoltre il PascalCase quando si carica una classe in una costante o in una variabile:
const Weapon = preload("res://weapon.gd")
Funzioni e variabili
Utilizzare il snake_case per i nomi di funzioni e di variabili:
var particle_effect
func load_level():
Anteporre un trattino basso (_) alle funzioni dei metodi virtuali che l'utente deve sovrascrivere, alle funzioni private e alle variabili private:
var _counter = 0
func _recalculate_path():
Segnali
Utilizza il passato indicativo per i nomi dei segnali:
signal door_opened
signal score_changed
Costanti ed enumerazioni
Scrivi le costanti in CONSTANT_CASE, vale a dire in maiuscolo con un trattino basso (_) per separare le parole:
const MAX_SPEED = 200
Utilizza il PascalCase per i nomi delle enumerazioni e mantienili al singolare, in quando rappresentano un tipo. Utilizza il CONSTANT_CASE per i loro membri, in quanto sono costanti:
enum Element {
EARTH,
WATER,
AIR,
FIRE,
}
Scrivi le enumerazioni con ogni elemento su una riga separata. Così è più facile aggiungere commenti di documentazione sopra ogni elemento, oltre a rendere più pulite le differenze (diffs) nel controllo versioni quando si aggiungono o rimuovono elementi.
Bene:
enum Element {
EARTH,
WATER,
AIR,
FIRE,
}
Male:
enum Element { EARTH, WATER, AIR, FIRE }
Ordine del codice
Questa sezione si concentra sull'ordine del codice. Per la formattazione, consulta Formattazione. Per le convenzioni di denominazione, consulta Convenzioni di denominazione.
Suggeriamo di organizzare il codice GDScript in questo modo:
01. @tool, @icon, @static_unload
02. class_name
03. extends
04. ## doc comment
05. signals
06. enums
07. constants
08. static variables
09. @export variables
10. remaining regular variables
11. @onready variables
12. _static_init()
13. remaining static methods
14. overridden built-in virtual methods:
1. _init()
2. _enter_tree()
3. _ready()
4. _process()
5. _physics_process()
6. remaining virtual methods
15. overridden custom methods
16. remaining methods
17. inner classes
E di definire i metodi e le variabili di una classe nel seguente ordine, in base ai loro modificatori di accesso:
1. public
2. private
Abbiamo ottimizzato l'ordine per rendere facile leggere il codice dall'alto verso il basso, per aiutare gli sviluppatori che leggono il codice per la prima volta a capirne come funziona e per evitare errori riguardanti l'ordine di dichiarazione delle variabili.
Questo ordine di codice segue quattro regole generali:
Le proprietà e i segnali vengono prima, seguiti dai metodi.
Pubblico viene prima di privato.
I callback virtuali vengono prima dell'interfaccia della classe.
Le funzioni di costruzione e inizializzazione degli oggetti,
_inite_ready, vengono prima delle funzioni che modificano l'oggetto in fase di esecuzione.
Dichiarazione di classe
Se il codice si deve eseguire nell'editor, inserisci l'annotazione @tool sulla prima riga dello script.
Prosegui con un @icon facoltativo e poi class_name se necessario. È possibile trasformare un file GDScript in un tipo globale nel progetto usando class_name. Per ulteriori informazioni, consulta Registrazione di classi con nome. Se la classe deve essere una classe astratta, aggiungi @abstract prima della parola chiave class_name.
Poi, aggiungi la parola chiave extends se la classe estende un tipo integrato.
Successivamente, è possibile facoltativamente inserire commenti di documentazione per la classe . Permettono di spiegare il ruolo della classe ad altri colleghi, come funziona e come altri sviluppatori dovrebbero usarla, ad esempio.
@abstract
class_name MyNode
extends Node
## A brief description of the class's role and functionality.
##
## The description of the script, what it can do,
## and any further detail.
Per le classi interne, usa dichiarazioni su una sola riga:
## A brief description of the class's role and functionality.
##
## The description of the script, what it can do,
## and any further detail.
@abstract class MyNode extends Node:
pass
Segnali e proprietà
Scrivi le dichiarazioni dei segnali, seguite dalle proprietà, vale a dire dalle variabili membro, dopo la docstring.
Le enumerazioni dovrebbero venire dopo i segnali, così da poterle utilizzare come indicazioni di esportazione per le altre proprietà.
Poi, scrivi le costanti, le variabili esportate più le variabili pubbliche, private e onready, in quest'ordine.
signal player_spawned(position)
enum Job {
KNIGHT,
WIZARD,
ROGUE,
HEALER,
SHAMAN,
}
const MAX_LIVES = 3
@export var job: Job = Job.KNIGHT
@export var max_health = 50
@export var attack = 5
var health = max_health:
set(new_health):
health = new_health
var _speed = 300.0
@onready var sword = get_node("Sword")
@onready var gun = get_node("Gun")
Nota
GDScript valuta le variabili @onready subito prima del callback _ready. È utile per memorizzare in anticipo le dipendenze dei nodi, ovvero per ottenere i nodi figlio nella scena su cui dipende la classe. Lo mostra l'esempio sopra.
Variabili membro
Non dichiarare variabili membro se sono utilizzate solo localmente in un metodo, poiché ciò rende il codice più difficile da seguire. Dichiarale invece come variabili locali nel corpo del metodo.
Variabili locali
Dichiara le variabili locali il più vicino possibile al loro primo utilizzo. Questo rende più facile seguire il codice, senza dover scorrere troppo per trovare il punto in cui viene dichiarata la variabile.
Metodi e funzioni statiche
Dopo le proprietà della classe vengono i metodi.
Comincia con il metodo di callback _init(), che il motore chiamerà quando creerà l'oggetto in memoria. Prosegui con il metodo di callback _ready(), che Godot chiama quando aggiunge un nodo all'albero di scene.
Queste funzioni dovrebbero apparire per prime perché mostrano come l'oggetto viene inizializzato.
Altri callback virtuali integrati, come _unhandled_input() e _physics_process, dovrebbero venire in seguito. Questi controllano il ciclo principale dell'oggetto e le sue interazioni con il motore di gioco.
Il resto dell'interfaccia della classe, metodi pubblici e privati, viene dopo, in quest'ordine.
func _init():
add_to_group("state_machine")
func _ready():
state_changed.connect(_on_state_changed)
_state.enter()
func _unhandled_input(event):
_state.unhandled_input(event)
func transition_to(target_state_path, msg={}):
if not has_node(target_state_path):
return
var target_state = get_node(target_state_path)
assert(target_state.is_composite == false)
_state.exit()
self._state = target_state
_state.enter(msg)
Events.player_state_changed.emit(_state.name)
func _on_state_changed(previous, new):
print("state changed")
state_changed.emit()
Tipizzazione statica
GDScript supporta facoltativamente la tipizzazione statica.
Tipi dichiarati
Per dichiarare il tipo di una variabile, usa <variabile>: <tipo>:
var health: int = 0
Per dichiarare il tipo restituito di una funzione, usa -> <tipo>:
func heal(amount: int) -> void:
Tipi dedotti
Nella maggior parte dei casi è possibile lasciare che sia il compilatore a dedurre il tipo, tramite :=. Si consiglia := quando il tipo è scritto sulla stessa riga dell'assegnazione, altrimenti è preferibile scrivere il tipo esplicitamente.
Bene:
# The type can be int or float, and thus should be stated explicitly.
var health: int = 0
# The type is clearly inferred as Vector3.
var direction := Vector3(1, 2, 3)
Includi il suggerimento di tipo quando il tipo è ambiguo e omettilo quando è ridondante.
Male:
# Typed as int, but it could be that float was intended.
var health := 0
# The type hint has redundant information.
var direction: Vector3 = Vector3(1, 2, 3)
# What type is this? It's not immediately clear to the reader, so it's bad.
var value := complex_function()
In alcuni casi, il tipo deve essere dichiarato esplicitamente, altrimenti il comportamento non sarà quello previsto perché il compilatore potrà utilizzare solo il tipo restituito della funzione. Ad esempio, get_node() non può dedurre un tipo a meno che la scena o il file del nodo non siano caricati in memoria. In questo caso, è necessario impostare il tipo esplicitamente.
Bene:
@onready var health_bar: ProgressBar = get_node("UI/LifeBar")
Male:
# The compiler can't infer the exact type and will use Node
# instead of ProgressBar.
@onready var health_bar := get_node("UI/LifeBar")
Alternativamente, è possibile usare la parola chiave as per convertire il tipo restituito; tale tipo sarà utilizzato per dedurre il tipo della variabile.
@onready var health_bar := get_node("UI/LifeBar") as ProgressBar
# health_bar will be typed as ProgressBar
Nota
Questa opzione è considerata più di tipo sicuro rispetto ai suggerimenti per i tipi, ma anche meno null-safe poiché converte silenziosamente la variabile in null in caso di incompatibilità con il tipo in fase di esecuzione, senza errori/avvisi.
Spaziatura dei commenti
I commenti normali (
#) e i commenti di documentazione (##) dovrebbero iniziare con uno spazio, ma non il codice che si disabilita. Inoltre, i commenti delle regioni di codice (#region/#endregion) devono seguire questa sintassi precisa, quindi non dovrebbero iniziare con uno spazio.Utilizzare uno spazio per i commenti normali e di documentazione aiuta a distinguere i commenti di testo dal codice disabilitato.
Bene:
Male:
Nota
Nell'editor di script, per alternare i commenti sul codice selezionato, premi Ctrl + K. Questa scorciatoia aggiunge o rimuove un singolo simbolo
#prima di qualsiasi codice sulle righe selezionate.Preferisci scrivere commenti su una riga a sé stante piuttosto che commenti in linea (commenti scritti sulla stessa riga del codice). I commenti in linea sono più adatti per commenti brevi, in genere composti da poche parole al massimo:
Bene:
Male: