running_code_in_the_editor.rst 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. .. _doc_running_code_in_the_editor:
  2. Running code in the editor
  3. ==========================
  4. What is ``@tool``?
  5. ------------------
  6. ``@tool`` is a powerful line of code that, when added at the top of your script,
  7. makes it execute in the editor. You can also decide which parts of the script
  8. execute in the editor, which in game, and which in both.
  9. You can use it for doing many things, but it is mostly useful in level design
  10. for visually presenting things that are hard to predict ourselves. Here are some
  11. use cases:
  12. - If you have a cannon that shoots cannonballs affected by physics (gravity),
  13. you can draw the cannonball's trajectory in the editor, making level design a
  14. lot easier.
  15. - If you have jumppads with varying jump heights, you can draw the maximum jump
  16. height a player would reach if it jumped on one, also making level design
  17. easier.
  18. - If your player doesn't use a sprite, but draws itself using code, you can make
  19. that drawing code execute in the editor to see your player.
  20. .. danger::
  21. ``@tool`` scripts run inside the editor, and let you access the scene tree
  22. of the currently edited scene. This is a powerful feature which also comes
  23. with caveats, as the editor does not include protections for potential
  24. misuse of ``@tool`` scripts.
  25. Be **extremely** cautious when manipulating the scene tree, especially via
  26. :ref:`Node.queue_free<class_Node_method_queue_free>`, as it can cause
  27. crashes if you free a node while the editor runs logic involving it.
  28. How to use ``@tool``
  29. --------------------
  30. To turn a script into a tool, add the ``@tool`` annotation at the top of your code.
  31. To check if you are currently in the editor, use: ``Engine.is_editor_hint()``.
  32. For example, if you want to execute some code only in the editor, use:
  33. .. tabs::
  34. .. code-tab:: gdscript GDScript
  35. if Engine.is_editor_hint():
  36. # Code to execute when in editor.
  37. .. code-tab:: csharp
  38. if (Engine.IsEditorHint())
  39. {
  40. // Code to execute when in editor.
  41. }
  42. On the other hand, if you want to execute code only in game, simply negate the
  43. same statement:
  44. .. tabs::
  45. .. code-tab:: gdscript GDScript
  46. if not Engine.is_editor_hint():
  47. # Code to execute when in game.
  48. .. code-tab:: csharp
  49. if (!Engine.IsEditorHint())
  50. {
  51. // Code to execute when in game.
  52. }
  53. Pieces of code that do not have either of the 2 conditions above will run both
  54. in-editor and in-game.
  55. Here is how a ``_process()`` function might look for you:
  56. .. tabs::
  57. .. code-tab:: gdscript GDScript
  58. func _process(delta):
  59. if Engine.is_editor_hint():
  60. # Code to execute in editor.
  61. if not Engine.is_editor_hint():
  62. # Code to execute in game.
  63. # Code to execute both in editor and in game.
  64. .. code-tab:: csharp
  65. public override void _Process(double delta)
  66. {
  67. if (Engine.IsEditorHint())
  68. {
  69. // Code to execute in editor.
  70. }
  71. if (!Engine.IsEditorHint())
  72. {
  73. // Code to execute in game.
  74. }
  75. // Code to execute both in editor and in game.
  76. }
  77. .. _doc_running_code_in_the_editor_important_information:
  78. Important information
  79. ---------------------
  80. The general rule is that **any other GDScript that your tool script uses must
  81. *also* be a tool**. The editor is not able to construct instances from GDScript
  82. files without ``@tool``, which means you cannot call methods or reference member
  83. variables from them otherwise. However, since static methods, constants and
  84. enums can be used without creating an instance, it is possible to call them or
  85. reference them from a ``@tool`` script onto other non-tool scripts. One exception to
  86. this are :ref:`static variables <doc_gdscript_basics_static_variables>`.
  87. If you try to read a static variable's value in a script that does not have
  88. ``@tool``, it will always return ``null`` but won't print a warning or error
  89. when doing so. This restriction does not apply to static methods, which can be
  90. called regardless of whether the target script is in tool mode.
  91. Extending a ``@tool`` script does not automatically make the extending script
  92. a ``@tool``. Omitting ``@tool`` from the extending script will disable tool
  93. behavior from the super class. Therefore, the extending script should also
  94. specify the ``@tool`` annotation.
  95. Modifications in the editor are permanent, with no undo/redo possible. For
  96. example, in the next section when we remove the script, the node will keep its
  97. rotation. Be careful to avoid making unwanted modifications. Consider setting up
  98. :ref:`version control <doc_version_control_systems>` to avoid losing work in
  99. case you make a mistake.
  100. Debugging
  101. ---------
  102. While the debugger and breakpoints cannot be used directly with tool scripts, it is possible
  103. to launch a new instance of the editor and debug from there. To do this, navigate to
  104. **Debug > Customize Run Instances...** and specify `--editor` in **Main Run Args**.
  105. See :ref:`doc_overview_of_debugging_tools` for more information.
  106. Additionally, you can use print statements to display the contents of variables instead.
  107. Try ``@tool`` out
  108. -----------------
  109. Add a ``Sprite2D`` node to your scene and set the texture to Godot icon. Attach
  110. and open a script, and change it to this:
  111. .. tabs::
  112. .. code-tab:: gdscript GDScript
  113. @tool
  114. extends Sprite2D
  115. func _process(delta):
  116. rotation += PI * delta
  117. .. code-tab:: csharp
  118. using Godot;
  119. [Tool]
  120. public partial class MySprite : Sprite2D
  121. {
  122. public override void _Process(double delta)
  123. {
  124. Rotation += Mathf.Pi * (float)delta;
  125. }
  126. }
  127. Save the script and return to the editor. You should now see your object rotate.
  128. If you run the game, it will also rotate.
  129. .. warning::
  130. You may need to restart the editor. This is a known bug found in all Godot 4 versions:
  131. `GH-66381 <https://github.com/godotengine/godot/issues/66381>`_.
  132. .. image:: img/rotating_in_editor.gif
  133. .. note::
  134. If you don't see the changes, reload the scene (close it and open it again).
  135. Now let's choose which code runs when. Modify your ``_process()`` function to
  136. look like this:
  137. .. tabs::
  138. .. code-tab:: gdscript GDScript
  139. func _process(delta):
  140. if Engine.is_editor_hint():
  141. rotation += PI * delta
  142. else:
  143. rotation -= PI * delta
  144. .. code-tab:: csharp
  145. public override void _Process(double delta)
  146. {
  147. if (Engine.IsEditorHint())
  148. {
  149. Rotation += Mathf.Pi * (float)delta;
  150. }
  151. else
  152. {
  153. Rotation -= Mathf.Pi * (float)delta;
  154. }
  155. }
  156. Save the script. Now the object will spin clockwise in the editor, but if you
  157. run the game, it will spin counter-clockwise.
  158. Editing variables
  159. -----------------
  160. Add and export a variable speed to the script. To update the speed and also reset the rotation
  161. angle add a setter ``set(new_speed)`` which is executed with the input from the inspector. Modify
  162. ``_process()`` to include the rotation speed.
  163. .. tabs::
  164. .. code-tab:: gdscript GDScript
  165. @tool
  166. extends Sprite2D
  167. @export var speed = 1:
  168. # Update speed and reset the rotation.
  169. set(new_speed):
  170. speed = new_speed
  171. rotation = 0
  172. func _process(delta):
  173. rotation += PI * delta * speed
  174. .. code-tab:: csharp
  175. using Godot;
  176. [Tool]
  177. public partial class MySprite : Sprite2D
  178. {
  179. private float _speed = 1;
  180. [Export]
  181. public float Speed
  182. {
  183. get => _speed;
  184. set
  185. {
  186. // Update speed and reset the rotation.
  187. _speed = value;
  188. Rotation = 0;
  189. }
  190. }
  191. public override void _Process(double delta)
  192. {
  193. Rotation += Mathf.Pi * (float)delta * _speed;
  194. }
  195. }
  196. .. note::
  197. Code from other nodes doesn't run in the editor. Your access to other nodes
  198. is limited. You can access the tree and nodes, and their default properties,
  199. but you can't access user variables. If you want to do so, other nodes have
  200. to run in the editor too.
  201. Getting notified when resources change
  202. --------------------------------------
  203. Sometimes you want your tool to use a resource. However, when you change a
  204. property of that resource in the editor, the ``set()`` method of your tool will
  205. not be called.
  206. .. tabs::
  207. .. code-tab:: gdscript GDScript
  208. @tool
  209. class_name MyTool
  210. extends Node
  211. @export var resource: MyResource:
  212. set(new_resource):
  213. resource = new_resource
  214. _on_resource_set()
  215. # This will only be called when you create, delete, or paste a resource.
  216. # You will not get an update when tweaking properties of it.
  217. func _on_resource_set():
  218. print("My resource was set!")
  219. .. code-tab:: csharp
  220. using Godot;
  221. [Tool]
  222. public partial class MyTool : Node
  223. {
  224. private MyResource _resource;
  225. [Export]
  226. public MyResource Resource
  227. {
  228. get => _resource;
  229. set
  230. {
  231. _resource = value;
  232. OnResourceSet();
  233. }
  234. }
  235. // This will only be called when you create, delete, or paste a resource.
  236. // You will not get an update when tweaking properties of it.
  237. private void OnResourceSet()
  238. {
  239. GD.Print("My resource was set!");
  240. }
  241. }
  242. To get around this problem you first have to make your resource a tool and make it
  243. emit the ``changed`` signal whenever a property is set:
  244. .. tabs::
  245. .. code-tab:: gdscript GDScript
  246. # Make Your Resource a tool.
  247. @tool
  248. class_name MyResource
  249. extends Resource
  250. @export var property = 1:
  251. set(new_setting):
  252. property = new_setting
  253. # Emit a signal when the property is changed.
  254. changed.emit()
  255. .. code-tab:: csharp
  256. using Godot;
  257. [Tool]
  258. public partial class MyResource : Resource
  259. {
  260. private float _property = 1;
  261. [Export]
  262. public float Property
  263. {
  264. get => _property;
  265. set
  266. {
  267. _property = value;
  268. // Emit a signal when the property is changed.
  269. EmitChanged();
  270. }
  271. }
  272. }
  273. You then want to connect the signal when a new resource is set:
  274. .. tabs::
  275. .. code-tab:: gdscript GDScript
  276. @tool
  277. class_name MyTool
  278. extends Node
  279. @export var resource: MyResource:
  280. set(new_resource):
  281. resource = new_resource
  282. # Connect the changed signal as soon as a new resource is being added.
  283. if resource != null:
  284. resource.changed.connect(_on_resource_changed)
  285. func _on_resource_changed():
  286. print("My resource just changed!")
  287. .. code-tab:: csharp
  288. using Godot;
  289. [Tool]
  290. public partial class MyTool : Node
  291. {
  292. private MyResource _resource;
  293. [Export]
  294. public MyResource Resource
  295. {
  296. get => _resource;
  297. set
  298. {
  299. _resource = value;
  300. // Connect the changed signal as soon as a new resource is being added.
  301. if (_resource != null)
  302. {
  303. _resource.Changed += OnResourceChanged;
  304. }
  305. }
  306. }
  307. private void OnResourceChanged()
  308. {
  309. GD.Print("My resource just changed!");
  310. }
  311. }
  312. Lastly, remember to disconnect the signal as the old resource being used and changed somewhere else
  313. would cause unneeded updates.
  314. .. tabs::
  315. .. code-tab:: gdscript GDScript
  316. @export var resource: MyResource:
  317. set(new_resource):
  318. # Disconnect the signal if the previous resource was not null.
  319. if resource != null:
  320. resource.changed.disconnect(_on_resource_changed)
  321. resource = new_resource
  322. if resource != null:
  323. resource.changed.connect(_on_resource_changed)
  324. .. code-tab:: csharp
  325. [Export]
  326. public MyResource Resource
  327. {
  328. get => _resource;
  329. set
  330. {
  331. // Disconnect the signal if the previous resource was not null.
  332. if (_resource != null)
  333. {
  334. _resource.Changed -= OnResourceChanged;
  335. }
  336. _resource = value;
  337. if (_resource != null)
  338. {
  339. _resource.Changed += OnResourceChanged;
  340. }
  341. }
  342. }
  343. Reporting node configuration warnings
  344. -------------------------------------
  345. Godot uses a *node configuration warning* system to warn users about incorrectly
  346. configured nodes. When a node isn't configured correctly, a yellow warning sign
  347. appears next to the node's name in the Scene dock. When you hover or click on
  348. the icon, a warning message pops up. You can use this feature in your scripts to
  349. help you and your team avoid mistakes when setting up scenes.
  350. When using node configuration warnings, when any value that should affect or
  351. remove the warning changes, you need to call
  352. :ref:`update_configuration_warnings<class_Node_method_update_configuration_warnings>` .
  353. By default, the warning only updates when closing and reopening the scene.
  354. .. tabs::
  355. .. code-tab:: gdscript GDScript
  356. # Use setters to update the configuration warning automatically.
  357. @export var title = "":
  358. set(p_title):
  359. if p_title != title:
  360. title = p_title
  361. update_configuration_warnings()
  362. @export var description = "":
  363. set(p_description):
  364. if p_description != description:
  365. description = p_description
  366. update_configuration_warnings()
  367. func _get_configuration_warnings():
  368. var warnings = []
  369. if title == "":
  370. warnings.append("Please set `title` to a non-empty value.")
  371. if description.length() >= 100:
  372. warnings.append("`description` should be less than 100 characters long.")
  373. # Returning an empty array means "no warning".
  374. return warnings
  375. .. _doc_running_code_in_the_editor_editorscript:
  376. Running one-off scripts using EditorScript
  377. ------------------------------------------
  378. Sometimes, you need to run code just one time to automate a certain task that is
  379. not available in the editor out of the box. Some examples might be:
  380. - Use as a playground for GDScript or C# scripting without having to run a project.
  381. ``print()`` output is displayed in the editor Output panel.
  382. - Scale all light nodes in the currently edited scene, as you noticed your level
  383. ends up looking too dark or too bright after placing lights where desired.
  384. - Replace nodes that were copy-pasted with scene instances to make them easier
  385. to modify later.
  386. This is available in Godot by extending :ref:`class_EditorScript` in a script.
  387. This provides a way to run individual scripts in the editor without having to
  388. create an editor plugin.
  389. To create an EditorScript, right-click a folder or empty space in the FileSystem
  390. dock then choose **New > Script...**. In the script creation dialog, click the
  391. tree icon to choose an object to extend from (or enter ``EditorScript`` directly
  392. in the field on the left, though note this is case-sensitive):
  393. .. figure:: img/running_code_in_the_editor_creating_editor_script.webp
  394. :align: center
  395. :alt: Creating an editor script in the script editor creation dialog
  396. Creating an editor script in the script editor creation dialog
  397. This will automatically select a script template that is suited for
  398. EditorScripts, with a ``_run()`` method already inserted:
  399. ::
  400. @tool
  401. extends EditorScript
  402. # Called when the script is executed (using File -> Run in Script Editor).
  403. func _run():
  404. pass
  405. This ``_run()`` method is executed when you use **File > Run** or the keyboard
  406. shortcut :kbd:`Ctrl + Shift + X` while the EditorScript is the currently open
  407. script in the script editor. This keyboard shortcut is only effective when
  408. currently focused on the script editor.
  409. Scripts that extend EditorScript must be ``@tool`` scripts to function.
  410. .. note::
  411. EditorScripts can only be run from the Godot script editor. If you are using
  412. an external editor, open the script inside the Godot script editor to run it.
  413. .. danger::
  414. EditorScripts have no undo/redo functionality, so **make sure to save your
  415. scene before running one** if the script is designed to modify any data.
  416. To access nodes in the currently edited scene, use the
  417. :ref:`EditorScript.get_scene <class_EditorScript_method_get_scene>` method which
  418. returns the root Node of the currently edited scene. Here's an example that
  419. recursively gets all nodes in the currently edited scene and doubles the range
  420. of all OmniLight3D nodes:
  421. ::
  422. @tool
  423. extends EditorScript
  424. func _run():
  425. for node in get_all_children(get_scene()):
  426. if node is OmniLight3D:
  427. # Don't operate on instanced subscene children, as changes are lost
  428. # when reloading the scene.
  429. # See the "Instancing scenes" section below for a description of `owner`.
  430. var is_instanced_subscene_child = node != get_scene() and node.owner != get_scene()
  431. if not is_instanced_subscene_child:
  432. node.omni_range *= 2.0
  433. # This function is recursive: it calls itself to get lower levels of child nodes as needed.
  434. # `children_acc` is the accumulator parameter that allows this function to work.
  435. # It should be left to its default value when you call this function directly.
  436. func get_all_children(in_node, children_acc = []):
  437. children_acc.push_back(in_node)
  438. for child in in_node.get_children():
  439. children_acc = get_all_children(child, children_acc)
  440. return children_acc
  441. .. tip::
  442. You can change the currently edited scene at the top of the editor even
  443. while the Script view is open. This will affect the return value of
  444. :ref:`EditorScript.get_scene <class_EditorScript_method_get_scene>`, so make
  445. sure you've selected the scene you intend to iterate upon before running
  446. the script.
  447. Instancing scenes
  448. -----------------
  449. You can instantiate packed scenes normally and add them to the scene currently
  450. opened in the editor. By default, nodes or scenes added with
  451. :ref:`Node.add_child(node) <class_Node_method_add_child>` are **not** visible
  452. in the Scene tree dock and are **not** persisted to disk. If you wish the node
  453. or scene to be visible in the scene tree dock and persisted to disk when saving
  454. the scene, you need to set the child node's :ref:`owner <class_Node_property_owner>`
  455. property to the currently edited scene root.
  456. If you are using ``@tool``:
  457. .. tabs::
  458. .. code-tab:: gdscript GDScript
  459. func _ready():
  460. var node = Node3D.new()
  461. add_child(node) # Parent could be any node in the scene
  462. # The line below is required to make the node visible in the Scene tree dock
  463. # and persist changes made by the tool script to the saved scene file.
  464. node.owner = get_tree().edited_scene_root
  465. .. code-tab:: csharp
  466. public override void _Ready()
  467. {
  468. var node = new Node3D();
  469. AddChild(node); // Parent could be any node in the scene
  470. // The line below is required to make the node visible in the Scene tree dock
  471. // and persist changes made by the tool script to the saved scene file.
  472. node.Owner = GetTree().EditedSceneRoot;
  473. }
  474. If you are using :ref:`EditorScript<class_EditorScript>`:
  475. .. tabs::
  476. .. code-tab:: gdscript GDScript
  477. func _run():
  478. # `parent` could be any node in the scene.
  479. var parent = get_scene().get_node("Parent")
  480. var node = Node3D.new()
  481. parent.add_child(node)
  482. # The line below is required to make the node visible in the Scene tree dock
  483. # and persist changes made by the tool script to the saved scene file.
  484. node.owner = get_scene()
  485. .. code-tab:: csharp
  486. public override void _Run()
  487. {
  488. // `parent` could be any node in the scene.
  489. var parent = GetScene().GetNode("Parent");
  490. var node = new Node3D();
  491. parent.AddChild(node);
  492. // The line below is required to make the node visible in the Scene tree dock
  493. // and persist changes made by the tool script to the saved scene file.
  494. node.Owner = GetScene();
  495. }
  496. .. warning::
  497. Using ``@tool`` improperly can yield many errors. It is advised to first
  498. write the code how you want it, and only then add the ``@tool`` annotation to
  499. the top. Also, make sure to separate code that runs in-editor from code that
  500. runs in-game. This way, you can find bugs more easily.