123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- .. _doc_running_code_in_the_editor:
- Running code in the editor
- ==========================
- What is ``@tool``?
- ------------------
- ``@tool`` is a powerful line of code that, when added at the top of your script,
- makes it execute in the editor. You can also decide which parts of the script
- execute in the editor, which in game, and which in both.
- You can use it for doing many things, but it is mostly useful in level design
- for visually presenting things that are hard to predict ourselves. Here are some
- use cases:
- - If you have a cannon that shoots cannonballs affected by physics (gravity),
- you can draw the cannonball's trajectory in the editor, making level design a
- lot easier.
- - If you have jumppads with varying jump heights, you can draw the maximum jump
- height a player would reach if it jumped on one, also making level design
- easier.
- - If your player doesn't use a sprite, but draws itself using code, you can make
- that drawing code execute in the editor to see your player.
- .. DANGER::
- ``@tool`` scripts run inside the editor, and let you access the scene tree
- of the currently edited scene. This is a powerful feature which also comes
- with caveats, as the editor does not include protections for potential
- misuse of ``@tool`` scripts.
- Be **extremely** cautious when manipulating the scene tree, especially via
- :ref:`Node.queue_free<class_Node_method_queue_free>`, as it can cause
- crashes if you free a node while the editor runs logic involving it.
- How to use it
- -------------
- To turn a script into a tool, add the ``@tool`` annotation at the top of your code.
- To check if you are currently in the editor, use: ``Engine.is_editor_hint()``.
- For example, if you want to execute some code only in the editor, use:
- .. tabs::
- .. code-tab:: gdscript GDScript
- if Engine.is_editor_hint():
- # Code to execute when in editor.
- .. code-tab:: csharp
- if (Engine.IsEditorHint())
- {
- // Code to execute when in editor.
- }
- On the other hand, if you want to execute code only in game, simply negate the
- same statement:
- .. tabs::
- .. code-tab:: gdscript GDScript
- if not Engine.is_editor_hint():
- # Code to execute when in game.
- .. code-tab:: csharp
- if (!Engine.IsEditorHint())
- {
- // Code to execute when in game.
- }
- Pieces of code do not have either of the 2 conditions above will run both
- in-editor and in-game.
- Here is how a ``_process()`` function might look for you:
- .. tabs::
- .. code-tab:: gdscript GDScript
- 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.
- .. code-tab:: csharp
- 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.
- }
- .. note::
- Modifications in the editor are permanent. For example, in the following
- case, when we remove the script, the node will keep its rotation. Be careful
- to avoid making unwanted modifications.
- Try it out
- -----------
- Add a ``Sprite2D`` node to your scene and set the texture to Godot icon. Attach
- and open a script, and change it to this:
- .. tabs::
- .. code-tab:: gdscript GDScript
- @tool
- extends Sprite2D
- func _process(delta):
- rotation += PI * delta
- .. code-tab:: csharp
- using Godot;
- [Tool]
- public partial class MySprite : Sprite2D
- {
- public override void _Process(double delta)
- {
- Rotation += Mathf.Pi * (float)delta;
- }
- }
- Save the script and return to the editor. You should now see your object rotate.
- If you run the game, it will also rotate.
- .. image:: img/rotating_in_editor.gif
- .. note::
- If you don't see the changes, reload the scene (close it and open it again).
- Now let's choose which code runs when. Modify your ``_process()`` function to
- look like this:
- .. tabs::
- .. code-tab:: gdscript GDScript
- func _process(delta):
- if Engine.is_editor_hint():
- rotation += PI * delta
- else:
- rotation -= PI * delta
- .. code-tab:: csharp
- public override void _Process(double delta)
- {
- if (Engine.IsEditorHint())
- {
- Rotation += Mathf.Pi * (float)delta;
- }
- else
- {
- Rotation -= Mathf.Pi * (float)delta;
- }
- }
- Save the script. Now the object will spin clockwise in the editor, but if you
- run the game, it will spin counter-clockwise.
- Editing variables
- -----------------
- Add and export a variable speed to the script. To update the speed and also reset the rotation
- angle add a setter ``set(new_speed)`` which is executed with the input from the inspector. Modify
- ``_process()`` to include the rotation speed.
- .. tabs::
- .. code-tab:: gdscript GDScript
- @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
- .. code-tab:: csharp
- 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;
- }
- }
- .. note::
- Code from other nodes doesn't run in the editor. Your access to other nodes
- is limited. You can access the tree and nodes, and their default properties,
- but you can't access user variables. If you want to do so, other nodes have
- to run in the editor too. Autoload nodes cannot be accessed in the editor at
- all.
- Reporting node configuration warnings
- -------------------------------------
- Godot uses a *node configuration warning* system to warn users about incorrectly
- configured nodes. When a node isn't configured correctly, a yellow warning sign
- appears next to the node's name in the Scene dock. When you hover or click on
- the icon, a warning message pops up. You can use this feature in your scripts to
- help you and your team avoid mistakes when setting up scenes.
- When using node configuration warnings, when any value that should affect or
- remove the warning changes, you need to call
- :ref:`update_configuration_warnings<class_Node_method_update_configuration_warnings>` .
- By default, the warning only updates when closing and reopening the scene.
- .. tabs::
- .. code-tab:: gdscript GDScript
- # 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
- Instancing scenes
- -----------------
- You can instantiate packed scenes normally and add them to the scene currently
- opened in the editor. By default, nodes or scenes added with
- :ref:`Node.add_child(node) <class_Node_method_add_child>` are **not** visible
- in the Scene tree dock and are **not** persisted to disk. If you wish the node
- or scene to be visible in the scene tree dock and persisted to disk when saving
- the scene, you need to set the child node's :ref:`owner <class_Node_property_owner>`
- property to the currently edited scene root.
- If you are using ``@tool``:
- .. tabs::
- .. code-tab:: gdscript GDScript
- 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.set_owner(get_tree().edited_scene_root)
- .. code-tab:: csharp
- 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;
- }
- If you are using :ref:`EditorScript<class_EditorScript>`:
- .. tabs::
- .. code-tab:: gdscript GDScript
- func _run():
- # `parent` could be any node in the scene.
- var parent = get_scene().find_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.set_owner(get_scene())
- .. code-tab:: csharp
- public override void _Run()
- {
- // `parent` could be any node in the scene.
- var parent = GetScene().FindNode("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();
- }
- .. warning::
- Using ``@tool`` improperly can yield many errors. It is advised to first
- write the code how you want it, and only then add the ``@tool`` annotation to
- the top. Also, make sure to separate code that runs in-editor from code that
- runs in-game. This way, you can find bugs more easily.
|