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...
Eseguire codice nell'editor
Che cos'è @tool?
@tool è una potente riga di codice che, aggiunta all'inizio dello script, lo fa eseguire nell'editor. Puoi anche decidere quali parti dello script eseguire nell'editor, quali nel gioco e quali in entrambi.
Può servire per molte cose, ma è particolarmente utile nella progettazione di livelli, per presentare visivamente cose difficili da prevedere. Ecco alcuni casi d'uso:
Se hai un cannone che spara palle di cannone influenzate dalla fisica (gravità), puoi disegnare la traiettoria della palla di cannone nell'editor, rendendo molto più facile progettare i livelli.
Se hai dei trampolini con altezze variabili di salto, puoi disegnare l'altezza massima di salto che un giocatore raggiungerebbe se saltasse su uno di essi, anche così rendendo più facile progettare i livelli.
Se il tuo giocatore non usa uno sprite, ma si disegna tramite codice, puoi far sì che quel codice di disegno sia eseguito nell'editor per vedere il tuo giocatore.
Pericolo
Gli script @tool sono eseguiti all'interno dell'editor e consentono di accedere all'albero della scena attualmente modificata. Questa è una funzionalità potente che viene con i sui avvertimenti, poiché l'editor non include protezioni contro un potenziale uso improprio degli script @tool. Presta estrema cautela quando manipoli l'albero di scene, soprattutto tramite Node.queue_free, poiché può causare arresti anomali se liberi un nodo mentre l'editor esegue logica che lo riguarda.
Come utilizzare @tool
Per trasformare uno script in uno strumento, aggiungi l'annotazione @tool in alto al codice.
Per verificare se ci si trova attualmente nell'editor, usa: Engine.is_editor_hint().
Ad esempio, se vuoi eseguire del codice solo nell'editor, usa:
if Engine.is_editor_hint():
# Code to execute when in editor.
if (Engine.IsEditorHint())
{
// Code to execute when in editor.
}
D'altro parte, se vuoi eseguire del codice solo nel gioco, basta negare la stessa istruzione:
if not Engine.is_editor_hint():
# Code to execute when in game.
if (!Engine.IsEditorHint())
{
// Code to execute when in game.
}
I pezzi di codice che non soddisfano nessuna delle due condizioni sopra indicate saranno eseguiti sia nell'editor sia nel gioco.
Ecco come una funzione _process() potrebbe sembrare per te:
func _process(delta):
if Engine.is_editor_hint():
# Code to execute in editor.
if not Engine.is_editor_hint():
# Code to execute in game.
# Code to execute both in editor and in game.
public override void _Process(double delta)
{
if (Engine.IsEditorHint())
{
// Code to execute in editor.
}
if (!Engine.IsEditorHint())
{
// Code to execute in game.
}
// Code to execute both in editor and in game.
}
Informazioni importanti
La regola generale è che qualsiasi altro GDScript utilizzato dallo script strumento deve *anch'esso* essere uno strumento. L'editor non è in grado di costruire istanze da file GDScript senza @tool, il che significa che non è possibile chiamare metodi o fare riferimento a variabili membro da essi altrimenti. Tuttavia, poiché i metodi statici, le costanti e gli enum si possono usare senza creare un'istanza, è possibile chiamarli o farvi riferimento da uno script @tool ad altri script non-strumento. Un'eccezione a questa regola sono le variabili statiche. Se si tenta di leggere il valore di una variabile statica in uno script che non ha @tool, verrà sempre restituito null ma non verrà stampato alcun avviso o errore. Questa restrizione non si applica ai metodi statici, che si possono chiamare a prescindere dal fatto che lo script interessato sia in modalità strumento.
Estendere uno script @tool non rende automaticamente lo script estendente uno @tool. Omettere @tool dallo script estendente disabiliterà il comportamento strumento dalla superclasse. Pertanto, anche lo script estendente dovrebbe specificare l'annotazione @tool.
Le modifiche nell'editor sono permanenti, senza possibilità di annullare/ripristinare. Ad esempio, nella prossima sezione, quando rimuoveremo lo script, il nodo manterrà la sua rotazione. Fai attenzione a non apportare modifiche indesiderate. Valuta di configurare un controllo versione per evitare di perdere lavoro in caso di sbagli.
Debugging
Sebbene il debugger e i punti d'interruzione non si possano utilizzare direttamente con gli script strumento, è possibile avviare una nuova istanza dell'editor e usare il debugger da lì. Per fare ciò, vai su Debug > Personalizza istanze di esecuzione... e specifica --editor in Parametri di esecuzione principale.
Consulta Panoramica degli strumenti di debug per maggiori informazioni.
Inoltre, è possibile servirsi di istruzioni print per visualizzare il contenuto delle variabili.
Provare @tool
Aggiungi un nodo Sprite2D alla tua scena e imposta la texture sull'icona di Godot. Allega e apri uno script e cambialo così:
@tool
extends Sprite2D
func _process(delta):
rotation += PI * delta
using Godot;
[Tool]
public partial class MySprite : Sprite2D
{
public override void _Process(double delta)
{
Rotation += Mathf.Pi * (float)delta;
}
}
Salva lo script e torna all'editor. Ora dovresti vedere il tuo oggetto ruotare. Se esegui il gioco, ruoterà egualmente.
Avvertimento
Potrebbe essere necessario riavviare l'editor. Questo è un bug noto presente in tutte le versioni di Godot 4: GH-66381.
Nota
Se non vedi le modifiche, ricarica la scena (chiudila e riaprila).
Ora scegliamo quale codice eseguire e quando. Modifica la funzione _process() per essere così:
func _process(delta):
if Engine.is_editor_hint():
rotation += PI * delta
else:
rotation -= PI * delta
public override void _Process(double delta)
{
if (Engine.IsEditorHint())
{
Rotation += Mathf.Pi * (float)delta;
}
else
{
Rotation -= Mathf.Pi * (float)delta;
}
}
Salva lo script. Ora l'oggetto ruoterà in senso orario nell'editor, ma se esegui il gioco, ruoterà in senso antiorario.
Modificare le variabili
Aggiungi ed esporta una variabile di velocità nello script. Per aggiornare la velocità e reimpostare anche l'angolo di rotazione, aggiungi un setter set(new_speed) che è eseguito con l'input dall'ispettore. Modifica _process() per includere la velocità di rotazione.
@tool
extends Sprite2D
@export var speed = 1:
# Update speed and reset the rotation.
set(new_speed):
speed = new_speed
rotation = 0
func _process(delta):
rotation += PI * delta * speed
using Godot;
[Tool]
public partial class MySprite : Sprite2D
{
private float _speed = 1;
[Export]
public float Speed
{
get => _speed;
set
{
// Update speed and reset the rotation.
_speed = value;
Rotation = 0;
}
}
public override void _Process(double delta)
{
Rotation += Mathf.Pi * (float)delta * _speed;
}
}
Nota
Il codice proveniente da altri nodi non è eseguito nell'editor. L'accesso agli altri nodi è limitato. È possibile accedere all'albero, ai nodi e alle loro proprietà predefinite, ma non alle variabili utente. Se si desidera farlo, anche gli altri nodi si devono eseguire nell'editor.
Essere notificati quando gli array o i dizionari cambiano
È possibile utilizzare un Array o un Dictionary come variabile @export. In uno script @tool, è possibile reagire a qualsiasi modifica apportata a tale collezione attraverso un setter. Normalmente, in fase di esecuzione, tale setter viene chiamato solo quando si assegna un valore alla variabile, ma quando si modifica un Array o un Dictionary nell'ispettore, il setter verrà chiamato anche in questo caso.
@tool
class_name MyTool
extends Node
@export var my_array = []:
set(new_array):
my_array = new_array
print("My array just changed!")
@export var my_dictionary = {}:
set(new_dictionary):
my_dictionary = new_dictionary
print("My dictionary just changed!")
using Godot;
[Tool]
public partial class MyTool : Node
{
private Array _myArray = new();
private Dictionary _myDictionary = new();
[Export]
public Array MyArray
{
get => _myArray;
set
{
_myArray = value;
GD.Print("My array just changed!");
}
}
[Export]
public Dictionary MyDictionary
{
get => _myDictionary;
set
{
_myDictionary = value;
GD.Print("My dictionary just changed!");
}
}
}
Essere notificati quando una risorsa cambia
A volte si desidera che il proprio strumento utilizzi una risorsa. Tuttavia, quando si modifica una proprietà di quella risorsa nell'editor, il metodo set() dello strumento non verrà chiamato.
@tool
class_name MyTool
extends Node
@export var resource: MyResource:
set(new_resource):
resource = new_resource
_on_resource_set()
# This will only be called when you create, delete, or paste a resource.
# You will not get an update when tweaking properties of it.
func _on_resource_set():
print("My resource was set!")
using Godot;
[Tool]
public partial class MyTool : Node
{
private MyResource _resource;
[Export]
public MyResource Resource
{
get => _resource;
set
{
_resource = value;
OnResourceSet();
}
}
// This will only be called when you create, delete, or paste a resource.
// You will not get an update when tweaking properties of it.
private void OnResourceSet()
{
GD.Print("My resource was set!");
}
}
Per aggirare questo problema, devi prima trasformare la tua risorsa in uno strumento e farle emettere il segnale changed ogni volta che viene impostata una proprietà:
# Make Your Resource a tool.
@tool
class_name MyResource
extends Resource
@export var property = 1:
set(new_setting):
property = new_setting
# Emit a signal when the property is changed.
changed.emit()
using Godot;
[Tool]
public partial class MyResource : Resource
{
private float _property = 1;
[Export]
public float Property
{
get => _property;
set
{
_property = value;
// Emit a signal when the property is changed.
EmitChanged();
}
}
}
Dopodiché devi connettere il segnale quando una nuova risorsa viene impostata:
@tool
class_name MyTool
extends Node
@export var resource: MyResource:
set(new_resource):
resource = new_resource
# Connect the changed signal as soon as a new resource is being added.
if resource != null:
resource.changed.connect(_on_resource_changed)
func _on_resource_changed():
print("My resource just changed!")
using Godot;
[Tool]
public partial class MyTool : Node
{
private MyResource _resource;
[Export]
public MyResource Resource
{
get => _resource;
set
{
_resource = value;
// Connect the changed signal as soon as a new resource is being added.
if (_resource != null)
{
_resource.Changed += OnResourceChanged;
}
}
}
private void OnResourceChanged()
{
GD.Print("My resource just changed!");
}
}
Infine, ricorda di disconnettere il segnale, poiché utilizzare la vecchia risorsa e modificarla altrove causerebbe aggiornamenti non necessari.
@export var resource: MyResource:
set(new_resource):
# Disconnect the signal if the previous resource was not null.
if resource != null:
resource.changed.disconnect(_on_resource_changed)
resource = new_resource
if resource != null:
resource.changed.connect(_on_resource_changed)
[Export]
public MyResource Resource
{
get => _resource;
set
{
// Disconnect the signal if the previous resource was not null.
if (_resource != null)
{
_resource.Changed -= OnResourceChanged;
}
_resource = value;
if (_resource != null)
{
_resource.Changed += OnResourceChanged;
}
}
}
Segnalare avvisi di configurazione dei nodi
Godot utilizza un sistema di avvisi di configurazione dei nodi per avvisare gli utenti della presenza di nodi configurati male. Quando un nodo non è configurato correttamente, un segnale di avviso giallo appare accanto al nome del nodo nel pannello Scena. Passando il mouse o cliccando sull'icona, appare un messaggio di avviso. Puoi utilizzare questa funzionalità nei tuoi script per aiutare te e il tuo team a evitare errori durante la configurazione delle scene.
Quando si utilizzano gli avvisi di configurazione dei nodi, quando cambia un valore che dovrebbe influire o rimuovere l'avviso, è necessario chiamare update_configuration_warnings. Normalmente, l'avviso si aggiorna solo alla chiusura e alla riapertura della scena.
# Use setters to update the configuration warning automatically.
@export var title = "":
set(p_title):
if p_title != title:
title = p_title
update_configuration_warnings()
@export var description = "":
set(p_description):
if p_description != description:
description = p_description
update_configuration_warnings()
func _get_configuration_warnings():
var warnings = []
if title == "":
warnings.append("Please set `title` to a non-empty value.")
if description.length() >= 100:
warnings.append("`description` should be less than 100 characters long.")
# Returning an empty array means "no warning".
return warnings
Running one-off scripts using EditorScript
A volte, è necessario eseguire codice una sola volta per automatizzare un'attività non disponibile nell'editor predefinito. Alcuni esempi includono:
Utilizzare come ambiente di prova per script GDScript o C# senza dover eseguire un progetto. L'output
print()è visualizzato nel pannello Output dell'editor.Ridimensionare tutti i nodi di luce nella scena attualmente modificata, dopo aver notato che il livello risulta troppo scuro o troppo luminoso dopo aver posizionato le luci dove desiderato.
Sostituire i nodi copiati e incollati con istanze di scena per renderli più facili da modificare in seguito.
Questa abilità è disponibile in Godot estendendo EditorScript in uno script. Questo consente di eseguire i singoli script nell'editor senza dover creare un'estensione.
Per creare un EditorScript, fai clic destro su una cartella o su uno spazio vuoto nel pannello FileSystem, quindi scegli Nuovo > Script.... Nella finestra di creazione di uno script, clicca sull'icona ad albero per scegliere un oggetto da cui estendere (oppure inserisci EditorScript direttamente nel campo a sinistra, tenendo presente che fa distinzione tra maiuscole e minuscole):
Creazione di uno script di editor nella finestra di creazione nell'editor di script
Sarà automaticamente selezionato un modello di script adatto agli EditorScript, con un metodo _run() già inserito:
@tool
extends EditorScript
# Called when the script is executed (using File -> Run in Script Editor).
func _run():
pass
using Godot;
[Tool]
public partial class MyEditorScript : EditorScript
{
// Called when the script is executed (right-click on Script -> Run in FileSystem dock).
public override void _Run()
{
// ...
}
}
Questo metodo _run() viene eseguito quando si utilizza uno qualsiasi dei 4 approcci possibili per eseguire un EditorScript:
Vai su in alto l'editor di script, con l'EditorScript come scheda attuale.
Premi la scorciatoia da tastiera Ctrl + Maiusc + X mentre l'EditorScript è la scheda attuale. Questa scorciatoia da tastiera funziona solo quando l'editor di script è focalizzato.
Fai clic destro sullo script nel pannello FileSystem e scegli .
Aggiungi
class_name <nome>all'inizio dello script, apri la tavolozza dei comandi premendo Ctrl + Shift + P e inserisci il nome della classe per eseguirlo. La voce avrà un nome basato sul nome della classe, con capitalizzazione automatica.
Gli script che estendono EditorScript devono essere script @tool per poter funzionare.
Nota
È possibile eseguire gli EditorScript solo dall'editor di script Godot. Se stai usando un editor esterno, usa uno degli ultimi due approcci per eseguire lo script.
Nota
Non è possibile eseguire gli script Cdall'editor di script poiché supporta solo GDScript. Fai riferimento ai metodi alternativi descritti in precedenza per eseguire script C# personalizzati.
Tieni presente che gli script degli strumenti C# appariranno nella tavolozza dei comandi solo se denotati dall'attributo GlobalClass.
Pericolo
Gli EditorScript non hanno funzionalità di annullamento/ripristino, quindi assicurati di salvare la scena prima di eseguirne uno se lo script è progettato per modificare dei dati.
Per accedere ai nodi nella scena attualmente modificata, usa il metodo EditorScript.get_edited_scene_root, che restituisce il nodo radice della scena attualmente modificata. Ecco un esempio che ottiene ricorsivamente tutti i nodi nella scena attualmente modificata e raddoppia la portata di tutti i nodi OmniLight3D:
@tool
# Thanks to the class name, we can run this script by bringing up
# the command palette and searching "Scale Omni Lights".
class_name ScaleOmniLights
extends EditorScript
func _run():
for node in EditorInterface.get_edited_scene_root().find_children("", "OmniLight3D"):
# Don't operate on instanced subscene children, as changes are lost
# when reloading the scene.
# See the "Instancing scenes" section below for a description of `owner`.
var is_instanced_subscene_child = node != get_scene() and node.owner != get_scene()
if not is_instanced_subscene_child:
node.omni_range *= 2.0
EditorInterface.mark_scene_as_unsaved()
using Godot;
[GlobalClass, Tool]
// Thanks to the GlobalClass attribute, we can run this script by bringing up
// the command palette and searching "Scale Omni Lights".
public partial class ScaleOmniLights : EditorScript
{
public override void _Run()
{
var sceneNode = EditorInterface.Singleton.GetEditedSceneRoot();
foreach (OmniLight3D node in sceneNode.FindChildren("", "OmniLight3D"))
{
// Don't operate on instanced subscene children, as changes are lost
// when reloading the scene.
// See the "Instancing scenes" section below for a description of `owner`.
var isInstancedSubsceneChild = node != sceneNode && node.Owner != sceneNode;
if (!isInstancedSubsceneChild)
{
node.OmniRange *= 2.0f;
EditorInterface.Singleton.MarkSceneAsUnsaved();
}
}
}
}
Nell'esempio precedente, chiamiamo anche EditorScript.mark_scene_as_unsaved() dopo ogni modifica che influisce sullo stato della scena. Questo permette all'editor di visualizzare la scena come "non salvata" (ovvero con un asterisco accanto al nome). In questo modo, è richiesta una conferma qualvolta si tenta di chiudere la scena con modifiche non salvate.
Suggerimento
È possibile cambiare la scena attualmente modificata nella parte superiore dell'editor, anche mentre la vista Script è aperta. Questo influirà sul valore restituito da EditorInterface.get_edited_scene_root, quindi assicurati di aver selezionato la scena su cui intendi iterare prima di eseguire lo script.
Istanziare scene
È possibile istanziare le scene impacchettate normalmente e aggiungerle alla scena attualmente aperta nell'editor. Come predefinito, i nodi o le scene aggiunti con Node.add_child(node) non sono visibili nel pannello Scena e non vengono salvati su disco. Se si desidera che il nodo o la scena siano visibili nel pannello Scena e salvati su disco al momento del salvataggio, è necessario impostare la proprietà owner del nodo figlio sulla radice della scena attualmente modificata.
Se stai utilizzando @tool:
func _ready():
var node = Node3D.new()
add_child(node) # Parent could be any node in the scene
# The line below is required to make the node visible in the Scene tree dock
# and persist changes made by the tool script to the saved scene file.
node.owner = get_tree().edited_scene_root
public override void _Ready()
{
var node = new Node3D();
AddChild(node); // Parent could be any node in the scene
// The line below is required to make the node visible in the Scene tree dock
// and persist changes made by the tool script to the saved scene file.
node.Owner = GetTree().EditedSceneRoot;
}
Se stai utilizzando EditorScript:
func _run():
# `parent` could be any node in the scene.
var parent = get_scene().get_node("Parent")
var node = Node3D.new()
parent.add_child(node)
# The line below is required to make the node visible in the Scene tree dock
# and persist changes made by the tool script to the saved scene file.
node.owner = get_scene()
public override void _Run()
{
// `parent` could be any node in the scene.
var parent = GetScene().GetNode("Parent");
var node = new Node3D();
parent.AddChild(node);
// The line below is required to make the node visible in the Scene tree dock
// and persist changes made by the tool script to the saved scene file.
node.Owner = GetScene();
}
Nota
Le modifiche apportate tramite script strumento e EditorScript (come l'aggiunta nodi o la modifica di proprietà) non contrassegnano automaticamente la scena come non salvata. Per visualizzare l'asterisco (*) e prevenire la perdita accidentale di dati, chiamare EditorInterface.mark_scene_as_unsaved() dopo le modifiche, oppure utilizzare EditorUndoRedoManager per supportare l'annullamento.
Avvertimento
L'uso improprio di @tool può generare molti errori. Si consiglia di scrivere prima il codice come desiderato e solo successivamente aggiungere l'annotazione @tool in alto. Inoltre, assicurati di separare il codice eseguito nell'editor da quello eseguito nel gioco. In questo modo, potrai individuare più facilmente i bug.