Skip to content

Editor Tools

Editor tools are Lua scripts that run in Studio while you edit a scene, not during gameplay. Use them for workflow helpers: scattering objects, batch renaming, procedural layout, validating scene setup, or any task you would otherwise do by hand in the editor.

Behaviours are the opposite: they drive runtime game logic when you press Play. They receive init, update, and the other lifecycle hooks described in Lifecycle Methods.

Both script kinds share the same property system (Properties), attach to scene objects the same way, and extend a common ScriptComponent base. The class you return from the script tells the engine which lifecycle to use.

Behaviours vs editor tools

Behaviour EditorTool
When it runs Play mode Edit mode (Studio)
Base class Class.new(Behaviour) Class.new(EditorTool)
Setup hook init() editorInit() (optional)
Per-frame hooks update, render, … None
Inspector buttons No Property.EditorAction

A single scene object can have both a Behaviour and an EditorTool attached. For example, an EditorTool might scatter foliage in the editor, while a Behaviour animates that foliage at runtime.

Creating an editor tool

Create a script that returns Class.new(EditorTool) instead of Class.new(Behaviour):

local MyTool = Class.new(EditorTool)

MyTool:addProperty("runAction", Property.EditorAction, "Run action", {
    info = "Execute the editor action",
})

function MyTool:runAction()
    print("Action ran on", self.node:getDisplayName())
end

return MyTool

Attach the script to a scene object in the Inspector, the same way you attach a Behaviour. Configure any properties, then click the button that appears for each EditorAction field.

editorInit()

If you define editorInit, it is called once when the editor tool is loaded for that script instance in Studio (similar to init() for behaviours in play mode). Use it for one-time setup that depends on self.node or linked assets.

function MyTool:editorInit()
    print("Editor tool ready on", self.node:getDisplayName())
end

Editor actions

Property.EditorAction adds a button to the Inspector. The property name must match a method on your editor tool class. The default value is the button label.

MyTool:addProperty("scatter", Property.EditorAction, "Scatter objects", {
    info = "Place random spheres under this node",
})

function MyTool:scatter()
    -- runs when the user clicks the button
end

Important rules:

  • The property name and method name must be identical (e.g. both scatter).
  • EditorAction properties are UI-only. They do not store a runtime value on the instance.
  • Use normal properties (Property.Number, Property.Link, …) for settings the action reads via self.mySetting.
  • When a button is clicked, the engine refreshes inspector field values on the tool instance, then calls the matching method.

Property.EditorAction is intended for EditorTool scripts. Define action buttons only on editor tools, not on behaviours.

Example: random spawner

This tool spawns simple spheres as children of the node it is attached to. Adjust count and spread in the Inspector, then click Spawn spheres.

local SgSphere = require('engine/sceneobjects/sgsphere')
local Transform = require('engine/math/transform')
local Vector3 = require('engine/math/vector3')

local RandomSpawner = Class.new(EditorTool)

RandomSpawner:addProperty("count", Property.Number, 5, {
    minValue = 1,
    maxValue = 50,
    info = "How many spheres to create",
})

RandomSpawner:addProperty("spread", Property.Number, 5, {
    minValue = 0.5,
    maxValue = 50,
    info = "Random position radius around this node",
})

RandomSpawner:addProperty("spawn", Property.EditorAction, "Spawn spheres", {
    info = "Create random spheres as children of this node",
})

function RandomSpawner:spawn()
    local parent = self.node
    if not parent then
        return
    end

    for i = 1, self.count do
        local x = (math.random() * 2 - 1) * self.spread
        local z = (math.random() * 2 - 1) * self.spread
        local y = math.random() * 0.5

        SgSphere.create({
            name = "Sphere " .. i,
            transform = Transform.new(Vector3.new(x, y, z)),
            radius = 0.2 + math.random() * 0.3,
        }, parent)
    end
end

return RandomSpawner

Scene changes made from an editor tool use the same scene graph APIs as runtime scripts (see Scene Graph). Consider grouping spawned objects under self.node so they stay organized in the hierarchy.

When editor tools run

  • Loaded when a scene is open in Studio and the script instance is created.
  • Suspended when you enter play mode (behaviours take over for runtime).
  • Restored when you stop play mode.
  • Not run in published builds

If a button does nothing, check the browser/console for errors. Common mistakes:

  • Method name does not match the EditorAction property name.
  • Script returns Behaviour instead of EditorTool.

See also

  • Properties — all property types, including Property.EditorAction
  • Basics — Behaviour scripts and play-mode lifecycle
  • Lifecycle Methods — runtime hooks for behaviours
  • Scene Graph — creating and parenting scene objects