Ghost Workflow
TimeMachine Ghost Workflow
This guide explains how to record an existing player character with the GameObject Time Machine, then spawn a reusable ghost using the modern GhostPlaybackComponent. It also documents the Crystal Save configuration required in the Crystal Save settings window, how to capture runs, and how to replay them. The legacy GhostPlayer demo component is covered briefly to clarify its upcoming deprecation and migration path.
1. Configure TimeMachine in Crystal Save Settings
Open Window → Crystal Save → Settings to launch the Remember Me Settings window. Use the TimeMachine Configuration section to pick a preset that enables ghost recording. The Ghost Racing and Speedrunning presets enable ghost mode, accumulative branch copies, and other defaults needed to race against prior attempts. Selecting a preset automatically applies the recommended resume mode, branch behaviour, copy mode, and ghost-mode toggle, while also surfacing warnings about continuous-time requirements for lap recordings.
Under the hood, the selected preset updates the SaveSettings asset. The preset controls default resume behaviour, automatic branching, branch-copy semantics, and whether recording during playback (ghost mode) is allowed. These options live alongside the maximum snapshot budget and ghost animation defaults that every ghost instance will inherit.
Checklist before you enter Play Mode

Choose the Ghost Racing preset (or another ghost-enabled preset) so that simultaneous record + playback is allowed and branches copy the data needed for ghosts.
Set an appropriate Max Save Duration so the recorded lap is preserved. Unlimited (
-1) is possible but increases save sizes; a finite window is usually enough.Keep an eye on continuous-time behaviour if you need clean lap timestamps—
Ghost Racingdefaults to continuous time, and the settings window reminds you to disable it in code if every run must start at zero.
2. Prepare the Player Character for Recording
2.1 Ensure a Stable Unique ID
Ghost playback looks up timeline snapshots by the source object’s unique identifier. Attach a Remember Component, or Remember Prefab component to the player so GameObjectUtilities.GetUniqueID resolves a stable string. If nothing is present the system falls back to the GameObject name, which is fragile for ghosts.
2.2 Add TimeMachineRecorder
Attach TimeMachineRecorder to the player character (Add Component → Crystal Save → Time Machine → Time Machine Recorder). Or add a Remember Component and a Remember Transform Component and enable Time Machine in the Remember Transform Settings.
The component exposes recording cadence, snapshot limits, and optional event tagging. It automatically starts recording on enable when the Time Machine singleton is ready, and you can also call StartRecording, StopRecording, RecordCurrentState, and RewindToTime from code or visual scripting.
Recommended options for ghost racing:
autoStartRecording: enabled so the lap starts capturing immediately.
useInterval / snapshotInterval: tune the sampling cadence (for racing ghosts 0.05–0.1 s is typical).
Ghost Playback Mode Override: leave on Use Global Setting unless this character needs to force NavMesh or transform interpolation behaviour.
2.3 Persist Timeline Data (Optional but Recommended)
If ghosts must survive saves/loads, add RememberTimeMachine to the GameObject that hosts the global GameObjectTimeMachine. It serializes every branch, snapshot, and recording flag so ghosts can be reconstructed after loading. The component intentionally disables direct recording; the individual TimeMachineRecorder components remain in charge of recording.
3. Capture a Recording
During gameplay, the TimeMachineRecorder tracks the player and writes snapshots into the Original branch. You can rely on auto-start behaviour or call StartRecording() manually after resetting the scene. When the lap ends, invoke StopRecording() if you want to freeze the history before branching.
If you are chaining multiple laps, use branch-friendly presets so the Time Machine keeps previous branches while continuing to record the active run. Tags such as OnEnable are recorded automatically if enabled, helping you mark split points in the timeline.
4. Spawn and Initialize a Ghost with GhostPlaybackComponent
4.1 Why GhostPlaybackComponent?

GhostPlaybackComponent is the generic, controller-agnostic playback system. It supports NavMesh agents, direct interpolation, runtime path rendering, loop styles, animation modes, and optional custom controllers—making it suitable for any character type. The older GhostPlayer script is retained only for demo compatibility.
4.2 Instantiate the Ghost GameObject
Create or load a ghost prefab that visually matches the player but omits gameplay-only components. Add GhostPlaybackComponent to it (manually or through code). If the prefab also contains a GhostNavMeshController and a NavMeshAgent, the component automatically switches to NavMesh playback; otherwise it defaults to direct transform interpolation.
Optional Inspector tweaks:
Loop Playback / Close Loop for continuous racing.
Animation Mode to switch between full recorded animator data and a simple velocity-driven parameter (e.g., feeding a
Speedfloat).Runtime Path Rendering toggles to visualize the route during playback.
4.3 Initialize from the Recorded Player
Call Initialize(sourcePlayer, recordingDuration) to clone the player’s Original branch into a uniquely named ghost branch. The method verifies the source has snapshots, clamps the requested duration, copies the timeline into a new branch, positions the ghost at the first snapshot (plus any positional offset), and auto-starts playback if autoPlay is enabled.
When loading an older run, call InitializeFromExistingBranch(branchName) instead. This reuses a pre-made branch (for example one restored by RememberTimeMachine) and immediately starts playback once valid snapshots are found.【Assets/Plugins/CrystalSave/Modules/TimeMachine/Core/GhostPlaybackComponent.cs†L531-L589】
4.4 Start and Manage Playback
StartPlayback() fetches the copied snapshots and configures either the NavMesh or direct interpolation pipeline. In NavMesh mode it derives waypoint, timestamp, and velocity lists, hands them to GhostNavMeshController, and applies runtime path rendering and loop settings. In direct mode it builds a path array for interpolation, enabling motion smoothing and loop mechanics such as ping-pong or shortest-path returns. Subsequent API calls (e.g., to reverse playback, loop, or stop) operate on the same branch.【Assets/Plugins/CrystalSave/Modules/TimeMachine/Core/GhostPlaybackComponent.cs†L595-L700】
5. Recording Multiple Ghosts
Each invocation of Initialize creates a new branch (e.g., Ghost_a1b2c3d4). You can keep these branches around to spawn multiple ghosts simultaneously, adjusting positionOffset or colour via your custom controller to keep them visually distinct. Because branch creation uses the Original branch as a source, make sure the active preset copies the ancestor data you need (accumulative copy is required when ghosts should show the entire lap history).【Assets/Plugins/CrystalSave/Modules/TimeMachine/Core/GhostPlaybackComponent.cs†L428-L511】
6. Legacy GhostPlayer (Demo Only)
GhostPlayer lives in the demo scripts and is tightly coupled to the racing vehicle example (it even removes demo-specific components like VehicleController). It manually strips saveable components, disables colliders, and depends on vehicle-specific logic, which makes it unsuitable for generic projects. Plan to migrate to GhostPlaybackComponent; the guide above covers the new workflow while GhostPlayer remains for backwards compatibility during the transition.【Assets/Plugins/CrystalSave/Modules/TimeMachine/Demo/Scripts/GhostPlayer.cs†L10-L186】
By combining the Configuration preset, a properly configured TimeMachineRecorder, and the flexible GhostPlaybackComponent, you can reliably record any player character, spawn ghost replays, and persist them across sessions.
Last updated