Guida di stile di GDScript
This style guide lists conventions to write elegant GDScript. The goal is to encourage writing clean, readable code and promote consistency across projects, discussions, and tutorials. Hopefully, this will also support the development of auto-formatting tools.
Poiché GDScript è simile a Python, questa guida si ispira alla guida di stile di programmazione PEP 8.
Style guides aren't meant as hard rulebooks. At times, you may not be able to apply some of the guidelines below. When that happens, use your best judgment, and ask fellow developers for insights.
In general, keeping your code consistent in your projects and within your team is more important than following this guide to a tee.
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
Utilizzare i caratteri di avanzamento riga (LF) per interrompere le righe, non CRLF o CR. (predefinito dell'editor)
Utilizzare un carattere di avanzamento riga alla fine di ogni file. (predefinito dell'editor)
Utilizzare la codifica UTF-8 senza byte order mark. (predefinito dell'editor)
Utilizzare 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")
Utilizzare 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. Utilizzare 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
Circondare 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)
Utilizzare 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
Mantenere le singole righe di codice al di sotto di 100 caratteri.
Se possibile, cercare di mantenere il testo sotto gli 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
Evitare di combinare più istruzioni su una singola riga, comprese le istruzioni condizionali, per rispettare le 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
Evitare parentesi inutili
Evitare 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
Preferire le versioni in inglese semplice degli operatori booleani, poiché sono le più accessibili:
Utilizzare
andinvece di&&.Utilizzare
orinvece di||.Utilizzare
notinvece di!.
Si potrebbe inoltre utilizzare le parentesi intorno agli operatori booleani per evitare qualsiasi ambiguità. Questo può rendere più facili da 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
Utilizzare sempre un solo spazio tra gli operatori e dopo le virgole. Inoltre, evitare spazi aggiuntivi 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 è necessario aggiungere uno spazio dopo la parentesi graffa di apertura e prima di quella di chiusura. Ciò rende il 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 utilizzare spazi per allineare le espressioni verticalmente:
x = 100
y = 100
velocity = 500
Virgolette
Use double quotes unless single quotes make it possible to escape fewer characters in a given string. See the examples below:
# 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.
Utilizzare il minuscolo per le lettere nei numeri esadecimali, poiché la loro altezza ridotta rende il numero più facile da leggere.
Bene:
var hex_number = 0xfb8c0b
Male:
var hex_number = 0xFB8C0B
Approfittare 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 integrate, portando a codice non conforme. 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
Utilizzare snake_case per i nomi dei file. Per le classi con nomi, convertire il nome della classe da 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++ vengono 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
Utilizzare PascalCase per i nomi di classi e di nodi:
extends CharacterBody3D
Utilizzare inoltre PascalCase quando si carica una classe in una costante o in una variabile:
const Weapon = preload("res://weapon.gd")
Funzioni e variabili
Utilizzare snake_case per denominare funzioni e 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
Utilizzare il passato indicativo per denominare i segnali:
signal door_opened
signal score_changed
Costanti ed enumerazioni
Scrivere le costanti con CONSTANT_CASE, vale a dire in maiuscolo con un trattino basso (_) per separare le parole:
const MAX_SPEED = 200
Use PascalCase for enum names and keep them singular, as they represent a type. Use CONSTANT_CASE for their members, as they are constants:
enum Element {
EARTH,
WATER,
AIR,
FIRE,
}
Write enums with each item on its own line. This allows adding documentation comments above each item more easily, and also makes for cleaner diffs in version control when items are added or removed.
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, consultare Formattazione. Per le convenzioni di denominazione, consultare 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. subclasses
E di definire i metodi e le variabili della classe nel seguente ordine, in base ai loro modificatori di accesso:
1. public
2. private
Abbiamo ottimizzato l'ordine per semplificare la lettura del codice dall'alto verso il basso, per aiutare gli sviluppatori che leggono il codice per la prima volta a capirne il funzionamento 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, precedono le funzioni che modificano l'oggetto in fase di esecuzione.
Dichiarazione della classe
Se si deve eseguire il codice nell'editor, inserire l'annotazione @tool sulla prima riga dello script.
Follow with the optional @icon then the class_name if necessary. You can turn a
GDScript file into a global type in your project using class_name. For more
information, see Registrazione di classi con nome.
Successivamente, aggiungere 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.
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.
Segnali e proprietà
Scrivere le dichiarazioni dei segnali, seguite dalle proprietà, vale a dire dalle variabili membro, dopo la docstring.
Le enumerazioni dovrebbero venire dopo i segnali, in modo da poterle utilizzare come indicazioni di esportazione per le altre proprietà.
Successivamente, scrivere 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. Ciò è utile per memorizzare in anticipo le dipendenze dei nodi, ovvero per ottenere i nodi figlio nella scena su cui dipende la classe. Questo è ciò che mostra l'esempio sopra.
Variabili membro
Non dichiarare variabili membro se vengono utilizzate solo localmente in un metodo, poiché ciò rende il codice più difficile da seguire. Dichiararle invece come variabili locali nel corpo del metodo.
Variabili locali
Dichiarare 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.
Cominciare con il metodo di callback _init(), che il motore chiamerà quando creerà l'oggetto in memoria. Proseguire 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, utilizzare <variabile>: <tipo>:
var health: int = 0
Per dichiarare il tipo restituito di una funzione, usa -> <tipo>:
func heal(amount: int) -> void:
Tipi dedotti
In most cases, you can let the compiler infer the type using :=.
Prefer := when the type is written on the same line as the assignment,
otherwise prefer writing the type explicitly.
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)
Includere il suggerimento di tipo quando il tipo è ambiguo e ometterlo 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")
In alternativa, è possibile utilizzare la parola chiave as per convertire il tipo restituito; tale tipo verrà 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
This option is considered more type-safe than type hints,
but also less null-safe as it silently casts the variable to null in case of a type mismatch at runtime,
without an error/warning.
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 attivare o disattivare i commenti sul codice selezionato, premere Ctrl + K. Questa scorciatoia aggiunge o rimuove un singolo simbolo
#prima di qualsiasi codice sulle righe selezionate.Prefer writing comments on their own line as opposed to inline comments (comments written on the same line as code). Inline comments are best used for short comments, typically a few words at most:
Bene:
Male: