在编辑器中运行代码

tool 是什么?

tool 是一个强大的代码行,当添加到脚本的顶部时,它会在编辑器中执行。 您还可以决定脚本的哪些部分在编辑器中执行,哪部分在游戏中执行,以及哪部分在两者中均执行。

您可以使用它来做很多事情,它在层次设计中非常有用,可以直观地呈现难以预测的事物。 以下是一些用例:

  • 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.

  • 如果您有不同跳跃高度的跳线,您可以绘制游戏角色能跳过的最大跳跃高度,也可以让关卡设计变得更容易。

  • 如果您的游戏角色不使用精灵,却使用代码来绘制,您可以在编辑器中执行该绘图代码以查看您的游戏角色。

危险

tool 脚本在编辑器内运行,让你访问当前编辑的场景树。这是一个强大的功能,但也有一些注意事项,因为编辑器不包括对 tool 脚本潜在滥用的保护。在操作场景树时,尤其是通过 Node.queue_free 时,要 极其 谨慎,因为如果你在编辑器运行涉及到节点的逻辑时释放该节点,可能会导致崩溃。

如何使用它

To turn a script into a tool, add the keyword tool at the top of your code.

要检查您当前是否在编辑器中,请使用 : Engine.editor_hint

For example, if you want to execute some code only in the editor, use:

if Engine.editor_hint:
    # Code to execute when in editor.
if (Engine.EditorHint)
{
    // Code to execute when in editor.
}

On the other hand, if you want to execute code only in game, simply negate the same statement:

if not Engine.editor_hint:
    # Code to execute when in game.
if (!Engine.EditorHint)
{
    // 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:

func _process(delta):
    if Engine.editor_hint:
        # Code to execute in editor.

    if not Engine.editor_hint:
        # Code to execute in game.

    # Code to execute both in editor and in game.
public override void _Process(float delta)
{
    if (Engine.EditorHint)
    {
        // Code to execute in editor.
    }

    if (!Engine.EditorHint)
    {
        // Code to execute in game.
    }

    // Code to execute both in editor and in game.
}

注解

Modifications in 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.

试试看

在场景中添加一个 Sprite 节点,并将纹理设置为Godot图标。 添加并打开脚本,并将其更改为:

tool
extends Sprite

func _process(delta):
    rotation_degrees += 180 * delta
using Godot;
using System;

[Tool]
public class MySprite : Sprite
{
    public override void _Process(float delta)
    {
        RotationDegrees += 180 * delta;
    }
}

保存脚本并返回编辑器。 现在您应该看到您的对象在旋转。 如果您运行游戏,它也会旋转。

../../_images/rotating_in_editor.gif

注解

如果您没有看到变化,请重新加载场景(关闭它并再次打开)。

现在让我们选择何时运行代码。 将 _process() 函数修改为:

func _process(delta):
    if Engine.editor_hint:
        rotation_degrees += 180 * delta
    else:
        rotation_degrees -= 180 * delta
public override void _Process(float delta)
{
    if (Engine.EditorHint)
    {
        RotationDegrees += 180 * delta;
    }
    else
    {
        RotationDegrees -= 180 * delta;
    }
}

保存脚本。 现在,对象将在编辑器中顺时针旋转,但如果您运行游戏,它将逆时针旋转。

Editing variables

在脚本中添加并导出一个变量速度。在 "setget" 之后的函数set_speed与你的输入一起执行,以改变这个变量。修改 _process() 以包含旋转速度。

tool
extends Sprite


export var speed = 1 setget set_speed


# Update speed and reset the rotation.
func set_speed(new_speed):
    speed = new_speed
    rotation_degrees = 0


func _process(delta):
    rotation_degrees += 180 * delta * speed
using Godot;
using System;

[Tool]
public class MySprite : Sprite
{
    private float speed = 1;

    [Export]
    public float Speed {
        get => speed;
        set => SetSpeed(value);
    }

    // Update speed and reset the rotation.
    private void SetSpeed(float newSpeed)
    {
        speed = newSpeed;
        RotationDegrees = 0;
    }

    public override void _Process(float delta)
    {
        RotationDegrees += 180 * delta * speed;
    }
}

注解

其他节点的代码不会在编辑器中运行。 您对其他节点的访问权限被限制了。 您可以访问树和节点及其默认属性,但无法访问用户变量。 如果要这样做,其他节点也必须在编辑器中运行。 AutoLoad节点时无法在编辑器中访问的。

实例化场景

你可以正常实例化打包的场景,并将它们添加到编辑器中当前打开的场景中。一定要将场景根节点设置为所有以这种方式创建的节点的所有者,否则这些节点在编辑器中就不可见。

If you are using tool:

func _ready():
    var node = Spatial.new()
    add_child(node) # Parent could be any node in the scene
    node.set_owner(get_tree().edited_scene_root)
public override void _Ready()
{
    var node = new Spatial();
    AddChild(node); // Parent could be any node in the scene
    node.Owner = GetTree().EditedSceneRoot;
}

If you are using EditorScript:

func _run():
    var parent = get_scene().find_node("Parent") # Parent could be any node in the scene
    var node = Spatial.new()
    parent.add_child(node)
    node.set_owner(get_scene())
public override void _Run()
{
    var parent = GetScene().FindNode("Parent"); // Parent could be any node in the scene
    var node = new Spatial();
    parent.AddChild(node);
    node.Owner = GetScene();
}

警告

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 keyword 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.