Turn Based Replay
The Turn Based Replay component enables recording and replaying gameplay in turn-based lenses. It captures player actions, object movements, animations, and any time-based data with precise synchronization and smooth interpolation, allowing players to see exactly what their opponent did during their turn.
Overview
The Turn Based Replay component provides:
- Complete recording system: Capture any type of data over time during gameplay
- Smooth playback: Replay recorded actions with interpolation between data points
- Flexible configuration: Record transforms, text, enabled states, or custom variables
- Independent tracks: Configure multiple replayable items with different update frequencies
- Event-driven: Subscribe to recording and playback lifecycle events
- Persistent storage: Automatically save and load replay data between turns
Turn Based Replay is perfect for:
- Showing opponent's previous turn in strategy games
- Replaying puzzle solutions
- Creating highlight reels of gameplay
- Ghost racing in competitive games
Adding the Component
- In the Assets panel, click Asset Library
- Search for "Turn Based Replay"
- Click Import to add it to your project (this also installs Turn Based component)
- In the Objects panel, select a scene object (or create a new one)
- In the Inspector panel, click Add Component > Custom Components > Turn Based Replay
- Set the Turn Based input to your Turn Based component in the scene
The Turn Based Replay component requires the Turn Based component to be present in your scene. This is automatically added when you import Turn Based Replay.
Configuration
Replayable Items
Configure what data to record and replay in the Replayable Items section. Each item represents a different type of data captured over time:
Transform Data
Records position, rotation, and/or scale of scene objects.
- Position: Capture object movement (vec3)
- Rotation: Capture object rotation (quat)
- Scale: Capture object scaling (vec3)
- Update Frequency: Minimum time between recordings (seconds)
Text Content
Records text displayed in Text or Text3D components.
- Text Component: Reference to Text/Text3D component
- Update Frequency: How often to check for text changes
Object Enabled State
Records when SceneObjects or Components are enabled/disabled.
- Target: SceneObject or Component to track
- Update Frequency: How often to check state
Custom Variables
Records custom values using getter/setter functions.
- Getter: Function returning current value
- Setter: Function applying value during playback
- Type: number, string, vec2, vec3, vec4, quat, boolean
- Interpolation: Optional smooth transitions between values
Use the Turn Based Replay Object helper component (included in the package) for streamlined setup of individual objects. It simplifies configuration for common use cases.
Event Triggers
Configure when to start, pause, resume, and stop recording/playback:
Recording Triggers:
- Turn Based Component events (onTurnStart, onTurnEnd)
- Behavior system triggers
- Interaction Component events (tap, swipe)
- Custom script calls via API
Playback Triggers:
- Turn Based Component events
- Automatic playback at turn start
- Manual control via scripting API
Event Responses
Add responses to recording and playback events:
Recording Events:
onRecordingStarted- When recording beginsonRecordingPaused- When recording pausesonRecordingResumed- When recording resumesonRecordingCancelled- When recording is cancelledonDataSaved- When data is saved to storage
Playback Events:
onPlaybackReady- When playback data is loadedonPlaybackStarted- When playback beginsonPlaybackFinished- When playback completesonPlaybackPaused- When playback pauses
Response Types:
- Trigger Behavior scripts
- Enable/disable scene objects
- Call methods on other components
Advanced Settings
Data Storage:
- Storage Key Prefix: Custom prefix for Turn Based storage keys
- Data Source: Where to load replay data from (default: Turn Based storage)
- Auto-save: Automatically save recorded data (recommended: enabled)
Leave advanced settings at default values unless you have specific requirements for custom storage or data sources.
Scripting API
Properties
| Property | Type | Description |
|---|---|---|
isPlaying | boolean | True when playback is running (not paused, not stopped). |
playbackTime | number | Current playback time position in seconds. |
playbackSpeed | number | Playback speed multiplier. 1.0 = normal speed, 2.0 = double speed, 0.5 = half speed. |
hasPlaybackStarted | boolean | True if playback was started, even if currently paused/stopped. False if never started or was reset. |
isRecording | boolean | True when recording is in progress. |
Recording Methods
| Method | Description |
|---|---|
startRecording(): void | Start recording new data. Clears any existing unsaved recording. Cannot start while playback is active. |
pauseRecording(): void | Pause recording but keep recorded data. Can be resumed later to continue the same session. |
resumeRecording(): void | Resume recording from paused state. Continues in the same session. Cannot resume while playback is active. |
pauseRecordingAndSave(): void | Pause recording and save all pending data. Convenient combination of pause + save. |
getCurrentRecordingTime(): number | Get the current recording time in seconds. |
setRecordingTime(targetTime: number): void | Set the current recording time for all tracks. Used to synchronize with external time systems. |
saveAllPendingData(): void | Force immediate save of any unsaved recorded data. |
cancelRecording(): void | Cancel current recording session. Discards unsaved data but keeps previously saved data. |
clearAllSavedData(): void | Clear all saved data from persistent storage. Does not affect current recording. |
clearCurrentRecording(): void | Clear current recording session data only. Does not affect saved data. |
clearAllData(): void | Clear both current recording and all saved data. Complete reset. |
cancelRecordingAndClearSaved(): void | Cancel recording and clear all saved data. Combination method. |
Playback Methods
| Method | Description |
|---|---|
isPlaybackFinished(): boolean | Check if playback reached the end of all recorded data. |
isPlaybackReady(): boolean | Check if playback data is loaded and ready to start. |
waitForPlaybackReady(): Promise<void> | Wait for playback to be ready. Returns a promise that resolves when ready. |
startPlayback(): void | Start playback from the beginning. Resets if already playing. Throws error if not ready. Cannot start while recording. |
startPlaybackWhenReady(): void | Start playback when ready, waiting if needed. Safer alternative to startPlayback(). |
pausePlayback(): void | Pause playback at current position. Can be resumed from same position. |
resumePlayback(): void | Resume playback from current position. Cannot resume while recording. |
resetPlayback(): void | Reset playback to initial state. Stops and resets time to 0, keeps loaded data. |
jumpToTime(targetTime: number): void | Jump to specific time in playback. Updates all tracks to interpolated values at that time. |
jumpToEnd(): void | Jump to the end of playback and pause. Sets all tracks to final recorded values. |
setPlaybackSpeed(speed: number): void | Set playback speed multiplier. Values > 1 speed up, < 1 slow down. |
getCurrentPlaybackTime(): number | Get the current playback time in seconds. |
Setup Methods
| Method | Description |
|---|---|
waitForSetupComplete(): Promise<void> | Wait for all configurations to be ready. Returns promise that resolves when setup is complete. |
isSetupReady(): boolean | Check if setup is ready for recording and playback. |
addConfig(config: ReplayableItemConfig<any, any>): void | Add a single replayable item configuration programmatically. |
addConfigs(configs: ReplayableItemConfig<any, any>[]): void | Add multiple replayable item configurations programmatically. |
ReplayableItemConfig Interface
The ReplayableItemConfig interface is used to configure what data should be recorded and how it should be played back.
| Property | Type | Description |
|---|---|---|
trackId | string | Unique identifier for this track. |
getter | () => TEngine | Function to get the current state value during recording. |
setter | (value: TEngine) => void | Function to set the state value during playback. |
updateFrequency | number | Minimum time to wait before recording new data (in seconds). |
diffCheck | (a: TEngine, b: TEngine) => boolean | Function to check if two values are different. |
converterToStorage | (value: TEngine) => TStorage | Optional function to convert values for storage. |
converterFromStorage | (value: TStorage) => TEngine | Optional function to convert values from storage. |
interpolation | (start: TEngine, end: TEngine, t: number) => TEngine | Optional function for smooth interpolation between values. |
TEngine is the type of the value represented in the engine (e.g., number, vec3, string).
TStorage is the type of the value when stored (e.g., number, string, number[]). It should be a primitive value that can be automatically serialized in JSON format. It can be the same as TEngine if no conversion is needed.
Events
Playback Events
| Event | Type | Description |
|---|---|---|
onPlaybackReady | Event<void> | Triggered when playback is ready to start after data loads. |
onPlaybackStarted | Event<void> | Triggered when playback begins, including after resets. |
onPlaybackFinished | Event<void> | Triggered when playback reaches the end and stops. |
onPlaybackPaused | Event<void> | Triggered when playback is paused or interrupted. |
Recording Events
| Event | Type | Description |
|---|---|---|
onRecordingStarted | Event<void> | Triggered when recording begins successfully. |
onRecordingPaused | Event<void> | Triggered when recording is paused (data retained). |
onRecordingResumed | Event<void> | Triggered when recording resumes from paused state. |
onRecordingCancelled | Event<void> | Triggered when recording is cancelled (unsaved data discarded). |
onDataSaved | Event<string> | Triggered when data is saved, with track name as parameter. |
Usage Example
This example replays the previous turn's recording at the start of each new turn, then starts recording the new turn.
- TypeScript
- JavaScript
import { TurnBasedReplay } from 'Turn Based Replay.lsc/Turn Based Replay';
import { TurnBased } from 'Turn Based.lsc/Turn Based/Turn Based';
@component
export class ReplayExample extends BaseScriptComponent {
@input turnBasedReplay: TurnBasedReplay;
@input turnBased: TurnBased;
onAwake() {
this.createEvent('OnStartEvent').bind(() => {
this.turnBased.onTurnStart.add((eventData) => {
// If first turn, start turn gameplay right away
if (eventData.turnCount == 0) {
this.startTurnGameplay();
} else {
// Otherwise, start playback of the last turn's recording
this.startPlayback(() => this.startTurnGameplay());
}
});
});
}
private startPlayback(onComplete: () => void) {
// First, subscribe to playback finished event to be notified when playback is done
this.turnBasedReplay.onPlaybackFinished.addOnce(onComplete);
// Start playback
this.turnBasedReplay.startPlaybackWhenReady();
}
private startTurnGameplay() {
// Start recording the turn
this.turnBasedReplay.startRecording();
// Do your game stuff here, and call onTurnOver() when the turn is over :)
}
private onTurnOver() {
// When turn ends, pause the recording
this.turnBasedReplay.pauseRecording();
// And if autosave is disabled, save the recording manually:
// this.turnBasedReplay.saveAllPendingData();
}
}
//@input Component.ScriptComponent turnBasedReplay
//@input Component.ScriptComponent turnBased
script.createEvent('OnStartEvent').bind(() => {
script.turnBased.onTurnStart.add((eventData) => {
// If first turn, start turn gameplay right away
if (eventData.turnCount == 0) {
startTurnGameplay();
} else {
// Otherwise, start playback of the last turn's recording
startPlayback(() => startTurnGameplay());
}
});
});
function startPlayback(onComplete) {
// First, subscribe to playback finished event to be notified when playback is done
script.turnBasedReplay.onPlaybackFinished.addOnce(onComplete);
// Start playback
script.turnBasedReplay.startPlaybackWhenReady();
}
function startTurnGameplay() {
// Start recording the turn
script.turnBasedReplay.startRecording();
// Do your game stuff here, and call onTurnOver() when the turn is over :)
}
function onTurnOver() {
// When game ends, pause the recording
script.turnBasedReplay.pauseRecording();
// And if autosave is disabled, save the recording:
// script.turnBasedReplay.saveAllPendingData();
}
Restrictions
- Mutual Exclusivity: A Turn Based Replay component cannot record while playback is active, or start playback while recording is active. However, multiple components can work independently.
- No Object Creation/Deletion: Recording does not support creating or deleting objects dynamically. All objects must exist before recording starts. Use the "Object Enabled State" replayable item to show/hide objects instead.
- Scene Persistence: Objects shouldn't be deleted during recording or playback, or unexpected behavior may occur.
Best Practices
-
First Turn Handling: Always check if it's the first turn before attempting playback, as there's no previous recording to replay.
-
Auto-save Enabled: Keep auto-save enabled (default) to ensure data is saved as it's recorded. This prevents data loss if recording is interrupted.
-
Update Frequency: Set appropriate update frequencies for each replayable item:
- Transforms: 0.016-0.033s (60-30 FPS) for smooth movement
- Text: 0.1s or higher (text doesn't change frequently)
- Enabled state: 0.05s for responsive on/off changes
-
Interpolation: Use interpolation for smooth playback of transforms (position, rotation, scale) and numeric values.
-
Skip Option: Always provide a skip button for players who want to start their turn immediately.
-
Loading States: Show loading UI while waiting for playback to be ready (
waitForPlaybackReady()). -
Clear Feedback: Use visual indicators to show when recording is active vs. when playback is running.
Related Components
- Turn Based - Required for turn-based gameplay and storage
- Turn Based Player Info - Display player information during replay
- Character Controller - Record character movement for replay