Commands and menus
A command is an action with a label, an optional shortcut, menu path, toolbar button and enabled state. Wrap scene edits in a transaction to make the whole command one undo step.
Register a command
Host.AddCommand(options, execute, isEnabled, isChecked) adds a command. Its editor id is <plugin id>.<Id>, so other plugins and automation can run it with Host.Execute.
| Option | Effect |
|---|---|
Menu | A path under Tools › Plugins ("Arrange", "Level/Lights") |
Shortcut | A key chord such as "Ctrl+Shift+R"; users can rebind it in Help › Keyboard Shortcuts |
Toolbar | A button on the viewport's plugin toolbar (bottom left) |
Icon | A Lucide icon name for the menu, palette and toolbar |
Description, Keywords | Shown and searched in the command palette |
isEnabled greys the command out (for example until two entities are selected); isChecked shows a tick for toggles.
ArrangePlugin.cs
public sealed class ArrangePlugin : EditorPlugin { public override void OnLoad() { // The editor command id is "<plugin id>.ArrangeInRow". Host.AddCommand( new CommandOptions("ArrangeInRow", "Arrange in Row") { Menu = "Arrange", // Tools > Plugins > Arrange > Arrange in Row Icon = "align-horizontal-distribute-center", Shortcut = "Ctrl+Shift+R", Toolbar = true, // the viewport's plugin toolbar Keywords = "align distribute row", }, ArrangeInRow, isEnabled: () => !Host.IsPlaying && Host.Document is not null && Host.Selection.Count >= 2); } private void ArrangeInRow() { World scene = Host.Document!; IReadOnlyList<Entity> selection = Host.Selection; Vector3 start = scene.GetTransform(selection[0]).Position; using (Host.Transaction("Arrange in Row")) // Ctrl+Z undoes the whole row { for (int index = 0; index < selection.Count; ++index) { Transform transform = scene.GetTransform(selection[index]); transform.Position = start + new Vector3(index * 2.0f, 0.0f, 0.0f); scene.SetTransform(selection[index], transform); } } } }
Edit the scene with undo
Host.Document is the scene being edited (null while Play runs or without a scene) and Host.Selection the selected entities. Reading needs permission scene.read, writing scene.write. Each write is undoable on its own; inside using (Host.Transaction("Label")) all writes become one undo step named Label.
Run long work in the background
Host.RunTask(name, run, complete) runs run on a worker thread with progress and cancel in the Tasks panel, then calls complete on the main thread. Never touch the scene or the UI from the worker; hand results back with the completion or Host.Post.
SceneStats.cs
public sealed class SceneStats : EditorPlugin { public override void OnLoad() { Host.AddCommand(new CommandOptions("Count", "Count Entities") { Menu = "Stats" }, () => { int count = Host.Document?.Entities.Count ?? 0; Host.Notify(Severity.Info, "Scene", $"{count} entities, {Host.Selection.Count} selected"); }); // Slow work on a worker thread, with progress in the Tasks panel; the completion runs on the main thread. Host.AddCommand(new CommandOptions("Scan", "Scan Content") { Menu = "Stats" }, () => Host.RunTask("Scan content", progress => { string[] files = Directory.GetFiles(Host.ContentFolder, "*", SearchOption.AllDirectories); progress.Report(1.0f, $"{files.Length} files"); }, (succeeded, message) => Host.Log($"scan finished: {message}"))); } public override void OnUnload() => Host.Log("Stats unloading (registrations are removed for you)"); }
KroysEngine 0.1.0 documentation, built from the engine repository by Tools/BuildDocsSite.py.