Vixen
dd8b0a81
csharp
public sealed class PlayModeController

Play, pause, step and stop, with the scene put back exactly as it was.

No guide page documents this yet — the page shows what the code says about itself.

Remarks

In-process, which is the default of doc 11's two topologies. The game runs in the viewport against the world the editor was editing, and a snapshot taken on entry is restored on exit. What makes that affordable is the ECS layout — WorldSnapshot says how — and what makes it correct is that the restore clears first, so nothing a script created survives.

The hazard is state that is not in the world. A static field, a native allocation, a subscription to an event that outlives the session: none of them are in a snapshot and all of them make the second play-through behave differently from the first. Doc 11's answer is that a play-stop which leaks should fail rather than degrade silently, so the tracked-object count is compared across the session and Leaks is what it found. A test asserts it is empty; the editor shows it as a notification.

⚠ The selection is translated, not kept. Every entity gets a new handle on restore. The controller does the translation for whatever it was handed, because a caller that forgot would have a selection naming whatever landed in those slots — which looks like a rendering fault and is not one.

And it steps a real EngineLoop, which until 2026-08-21 it did not. ShouldTick had no caller outside its own tests: Play snapshotted the world, maximised the viewport, said so in a notification, and nothing advanced. Tick is the frame, and it is deliberately the *engine's* loop rather than a schedule written here — see Loop for what that runs and, more importantly, for what it does not.

⚠ What a session runs is stated rather than assumed, because the honest set is small. An EngineLoop's default graph is behaviours, coroutines and transforms, and every other system a game runs — physics, audio, input, navigation, the render extractions — is registered by that game's own OnInitialise against host services an editor does not have. So this runs a *whole* graph of a *named* set, and Unsupported plus the caller's own inventory are what stop the difference being mistaken for a gameplay bug. [11](../../../docs/plan/11-editor.md) § "Play mode runs a system graph" is the reasoning.

Fields and properties (11)

  • public PlayState State

    What the editor is doing.

  • public bool IsPlaying

    Whether the game is running, paused or not.

  • public int PendingSteps

    How many frames Step still owes.

  • public IReadOnlyList<PlayLeak> Leaks

    What was still live after the last session that was not live before it.

  • public EngineLoop? Loop

    The graph this session steps, or when nothing is running.

  • public PlaySession? Session

    This session's contributions and their teardown, or null when nothing is running.

  • public IReadOnlyList<string> Refused

    The contributions that threw while attaching, by type name.

  • public IReadOnlyList<string> Ordering

    Every ordering declaration this session could not honour, one readable line each.

  • public FrameActivation Declared

    What the project's own declared systems did when this session started.

  • public IReadOnlyList<string> Unsupported

    Behaviours on the world that this session could not take over, by type name.

  • public Func<IEnumerable<BehaviorStore>>? Stores

    Every other store whose authored behaviours this session takes over.

Events (2)

Methods (9)

  • public PlayModeController(World world, BehaviorStore? authored = null, IEditorRegistry? extensions = null)

    Drives play mode over a world.

  • public bool Play()

    Enters play mode, taking a snapshot first.

  • public bool Tick(TimeSpan delta)

    Runs one frame of the game, if this is a frame the game should have.

  • public IReadOnlyList<Entity> Stop(IEnumerable<Entity>? selection = null)

    Stops the game and puts the scene back.

  • public bool Pause()

    Stops stepping without leaving play mode.

  • public bool Resume()

    Starts stepping again.

  • public void Step(int frames = 1)

    Runs a number of frames while paused.

  • public bool ShouldTick()

    Whether the game loop should run this frame, and consumes a step if it does.

  • public void Dispose()

    Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

Used by (9)

  • AnimationWiringTestsVixen.Editor.App.Tests
  • EditorApplicationVixen.Editor.App
  • PlayDeclaredSystemsTestsVixen.Editor.SceneView.Tests
  • PlayGraphTestsVixen.Editor.SceneView.Tests
  • PlayOverlayTestsVixen.Editor.App.Tests
  • PlaySystemsTestsVixen.Editor.SceneView.Tests
  • SceneDocumentTestsVixen.Editor.SceneView.Tests
  • SceneTestsVixen.Editor.SceneView.Tests
  • TerrainSessionTestsVixen.Editor.App.Tests