Use this skill when working with Sequencer, LevelSequence, cutscene, cinematic, camera, movie scene, sequencer event, or Movie Render Queue in Unreal Engine. See references/sequencer-patterns.md for dialogue cutscene, in-game camera, and scripted event patterns. For animation playback, see ue-animation-system.
---
name: ue-sequencer-cinematics
description: "Use this skill when working with Sequencer, LevelSequence, cutscene, cinematic, camera, movie scene, sequencer event, or Movie Render Queue in Unreal Engine. See references/sequencer-patterns.md for dialogue cutscene, in-game camera, and scripted event patterns. For animation playback, see ue-animation-system."
metadata:
version: 1.0.0
---
# Skill: ue-sequencer-cinematics
You are an expert in Unreal Engine's Sequencer and cinematic systems.
## Context Check
Read `.agents/ue-project-context.md` before writing any cinematic code for:
- Target UE version (API availability varies — SequencePlayer property deprecated in 5.4+)
- Cinematic requirements (cutscenes, in-game cameras, offline rendering)
- Multiplayer context (sequence replication behavior differs)
- Build.cs modules already declared
## Information Gathering
Ask for:
1. Cinematic type: full-screen cutscene, in-game camera, or background ambient sequence
2. Whether actors bind at runtime or are pre-placed in the level
3. Whether camera control must return to the player after playback
4. One-shot or looping sequence
5. Real-time gameplay or offline Movie Render Queue output
---
## Build.cs Modules
```csharp
PublicDependencyModuleNames.AddRange(new string[]
{
"LevelSequence", "MovieScene", "CinematicCamera",
});
// Offline rendering only:
PrivateDependencyModuleNames.Add("MovieRenderPipelineCore");
```
---
## Playing Level Sequences
### ALevelSequenceActor — World-Placed
`ALevelSequenceActor` owns the player. Use `GetSequencePlayer()` — the direct `SequencePlayer`
property is deprecated since UE 5.4.
```cpp
// LevelSequenceActor.h: ULevelSequencePlayer* GetSequencePlayer() const;
ULevelSequencePlayer* Player = SeqActor->GetSequencePlayer();
if (Player) { Player->Play(); }
```
### ULevelSequencePlayer::CreateLevelSequencePlayer — Runtime Spawn
```cpp
// LevelSequencePlayer.h:
// static ULevelSequencePlayer* CreateLevelSequencePlayer(
// UObject* WorldContextObject, ULevelSequence*, FMovieSceneSequencePlaybackSettings,
// ALevelSequenceActor*& OutActor);
FMovieSceneSequencePlaybackSettings Settings;
Settings.bAutoPlay = false;
Settings.PlayRate = 1.0f;
Settings.LoopCount.Value = 0; // 0=once, -1=infinite
Settings.bDisableMovementInput = true;
Settings.bDisableLookAtInput = true;
Settings.bHidePlayer = false;
Settings.bHideHud = false;
Settings.bDisableCameraCuts = false;
Settings.bPauseAtEnd = false;
Settings.FinishCompletionStateOverride =
EMovieSceneCompletionModeOverride::ForceRestoreState; // safe for skippable cutscenes
ALevelSequenceActor* OutActor = nullptr;
ULevelSequencePlayer* Player = ULevelSequencePlayer::CreateLevelSequencePlayer(
this, Sequence, Settings, OutActor);
// Store OutActor in a UPROPERTY to prevent GC
ActiveSequenceActor = OutActor;
if (Player)
{
Player->OnFinished.AddDynamic(this, &AMyClass::OnCutsceneFinished);
Player->Play();
}
```
### Play Control (UMovieSceneSequencePlayer base)
```cpp
// MovieSceneSequencePlayer.h
Player->Play();
Player->PlayReverse();
Player->PlayLooping(-1); // -1 = infinite loops
Player->Pause();
Player->Stop(); // moves cursor to end, fires OnStop
Player->StopAtCurrentTime();
Player->GoToEndAndStop();
Player->SetPlayRate(0.5f); // negative = reverse
// Jump to frame (skips events between current and target)
// Blueprint exposes this as GoToFrame. In C++, use SetPlaybackPosition with Jump.
Player->SetPlaybackPosition(
FMovieSceneSequencePlaybackParams(FFrameTime(120), EUpdatePositionMethod::Jump));
// Play through to frame (triggers all events along the way)
Player->PlayTo(
FMovieSceneSequencePlaybackParams(FFrameTime(240), EUpdatePositionMethod::Play),
FMovieSceneSequencePlayToParams());
// Restrict play range
Player->SetFrameRange(0, 60); // frames 0–60
Player->SetTimeRange(0.0f, 2.5f); // seconds
// State query
bool bPlaying = Player->IsPlaying();
FQualifiedFrameTime Now = Player->GetCurrentTime();
FQualifiedFrameTime Dur = Player->GetDuration();
// Delegates (bind before Play)
Player->OnFinished.AddDynamic(this, &UMyClass::OnSeqFinished);
Player->OnPlay.AddDynamic(this, &UMyClass::OnSeqPlay);
Player->OnStop.AddDynamic(this, &UMyClass::OnSeqStop);
Player->OnNativeFinished.BindUObject(this, &UMyClass::OnNativeFinished); // non-dynamic
// Camera cut event (LevelSequencePlayer.h)
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLevelSequencePlayerCameraCutEvent, UCameraComponent*, CameraComponent)
LsPlayer->OnCameraCut.AddDynamic(this, &UMyClass::OnCameraCut);
// Restore state on early stop (skippable cutscene)
Player->SetCompletionModeOverride(EMovieSceneCompletionModeOverride::ForceRestoreState);
Player->Stop();
// Sub-sequence snapshot
FLevelSequencePlayerSnapshot Snap;
Player->TakeFrameSnapshot(Snap);
// Snap.CurrentShotName, Snap.RootTime, Snap.CameraComponent, Snap.ActiveShot
```
---
## Actor Binding
Tag bindings in Sequencer (right-click object binding -> Tags...) then override at runtime.
**Always bind before calling `Play()`.**
### Spawnables vs Possessables
**Possessables** bind to pre-existing world actors (placed in level or spawned by gameplay). **Spawnables** are actors the sequence itself creates and destroys.
```cpp
// Spawnable: sequence owns the actor lifecycle
// Set in Sequencer editor: right-click actor track → "Change to Spawnable"
// At runtime, actor spawns when sequence reaches its range, despawns when exiting
// Dynamic possessable: bind a gameplay-spawned actor to a sequence track
// Create the binding ID in the editor, then override at runtime:
FMovieSceneObjectBindingID BindingID = /* from sequence editor */;
ALevelSequenceActor* SeqActor = /* your sequence actor */;
SeqActor->SetBinding(BindingID, {SpawnedActor});
```
**Why this matters**: Use possessables for persistent world actors (doors, elevators) and spawnables for transient cutscene-only actors (cinematic-only characters, props). Possessables survive sequence end; spawnables are cleaned up automatically.
```cpp
// LevelSequenceActor.h — all binding API lives here
// Replace bound actors (preferred: tag-based, resilient to GUID changes)
SeqActor->SetBindingByTag(FName("Hero"), TArray<AActor*>{ HeroActor },
/*bAllowBindingsFromAsset=*/ false);
// GUID-based (expose FMovieSceneObjectBindingID via UPROPERTY for editor assignment)
// UPROPERTY(EditAnywhere) FMovieSceneObjectBindingID HeroBindingID;
SeqActor->SetBinding(HeroBindingID, TArray<AActor*>{ HeroActor }, false);
// Append without removing existing bindings
SeqActor->AddBindingByTag(FName("NPC"), NpcActor, /*bAllowBindingsFromAsset=*/ true);
// Remove / reset
SeqActor->RemoveBindingByTag(FName("NPC"), NpcActor);
SeqActor->ResetBinding(HeroBindingID); // revert to asset binding
SeqActor->ResetBindings(); // revert all overrides
// Lookup by tag
FMovieSceneObjectBindingID ID = SeqActor->FindNamedBinding(FName("Hero"));
const TArray<FMovieSceneObjectBindingID>& All = SeqActor->FindNamedBindings(FName("NPC"));
// Inspect what is currently bound
TArray<UObject*> Bound = Player->GetBoundObjects(HeroBindingID);
// FMovieSceneObjectBindingID (MovieSceneObjectBindingID.h):
// Access via public accessors — internal fields are private:
// GetGuid() — FGuid identifying the binding track
// GetRelativeSequenceID() — which sub-sequence holds it (0 = local)
```
---
## Camera System
### ACineCameraActor / UCineCameraComponent
```cpp
// CineCameraActor.h: UCineCameraComponent* GetCineCameraComponent() const;
// CineCameraComponent.h / CineCameraSettings.h:
// FCameraFilmbackSettings Filmback (SensorWidth, SensorHeight in mm, read-only SensorAspectRatio)
// FCameraLensSettings LensSettings (MinFocalLength, MaxFocalLength, MinFStop, MaxFStop mm)
// FCameraFocusSettings FocusSettings (FocusMethod, ManualFocusDistance cm, bSmoothFocusChanges)
// float CurrentFocalLength — Interp, animatable in Sequencer
// float CurrentAperture — f-stop, Interp-animatable
ACineCameraActor* Cam = GetWorld()->SpawnActor<ACineCameraActor>(SpawnTransform);
UCineCameraComponent* CC = Cam->GetCineCameraComponent();
FCameraFilmbackSettings FB;
FB.SensorWidth = 36.0f; FB.SensorHeight = 24.0f; // full-frame 35mm
CC->SetFilmback(FB);
CC->SetCurrentFocalLength(50.0f); // mm
CC->SetCurrentAperture(2.0f); // f-stop
FCameraFocusSettings FS;
FS.FocusMethod = ECameraFocusMethod::Manual; // Manual | Tracking | Disable | DoNotOverride
FS.ManualFocusDistance = 500.0f; // cm
FS.bSmoothFocusChanges = true;
CC->SetFocusSettings(FS);
// Lookat tracking (CineCameraActor.h: FCameraLookatTrackingSettings LookatTrackingSettings)
Cam->LookatTrackingSettings.bEnableLookAtTracking = true;
Cam->LookatTrackingSettings.ActorToTrack = TargetActor;
Cam->LookatTrackingSettings.LookAtTrackingInterpSpeed = 5.0f;
Cam->LookatTrackingSettings.RelativeOffset = FVector(0, 0, 90.f);
// Prevent sequence from overriding gameplay camera (set before Play)
SeqActor->PlaybackSettings.bDisableCameraCuts = true;
// Restore player camera after cutscene (call from OnFinished delegate)
APlayerController* PC = GetWorld()->GetFirstPlayerController();
PC->SetViewTargetWithBlend(PC->GetPawn(), 0.5f, VTBlend_Cubic);
PC->SetIgnoreMoveInput(false);
PC->SetIgnoreLookInput(false);
```
### APlayerCameraManager
`APlayerCameraManager` (accessed via `PlayerController->PlayerCameraManager`) manages camera
blending and view target selection for the local player. After a cutscene ends, restore the
gameplay camera explicitly — Sequencer does not do this automatically:
```cpp
APlayerController* PC = GetWorld()->GetFirstPlayerController();
// PlayerCameraManager handles blend state; SetViewTargetWithBlend triggers it.
PC->SetViewTargetWithBlend(PC->GetPawn(), 0.5f, VTBlend_Cubic);
// The blend is processed each tick inside APlayerCameraManager::UpdateCamera.
```
---
## Sequencer Events
Event tracks call `UFUNCTION`s on objects in the event context.
Default event context: `UWorld` + `ALevelScriptActor`.
```cpp
// Function must be UFUNCTION(BlueprintCallable) on a context object
UFUNCTION(BlueprintCallable, Category = "Cinematics")
void TriggerExplosion()
{
ExplosionSystem->SpawnExplosion(ExplosionLocation);
}
// Trigger C++ code at a specific frame by playing to it (fires all events in range)
Player->PlayTo(
FMovieSceneSequencePlaybackParams(FFrameTime(60), EUpdatePositionMethod::Play),
FMovieSceneSequencePlayToParams());
```
### FMovieSceneEvent
`FMovieSceneEvent` holds the event endpoint data — the bound function to call when the event
fires. Event tracks are structured as:
- `UMovieSceneEventTrack` — the master track added to a binding or as a master track
- `UMovieSceneEventTriggerSection` — a section containing one or more trigger entries
- `FMovieSceneEvent` — each entry inside the trigger section, pointing to the endpoint function
The endpoint function is resolved at runtime against the event context objects (by default
`UWorld` and `ALevelScriptActor`). Director-based events resolve against a
`ULevelSequenceDirector` subclass instance.
### ULevelSequenceDirector — Custom Event Context
`ULevelSequenceDirector` provides a per-sequence scripting context that receives event track
calls. Subclass it (Blueprint or C++) and assign to the LevelSequence asset's Director Class
property. The Director instance is created when the sequence begins playing and destroyed
when it stops — its lifetime matches the sequence player.
```cpp
// MySequenceDirector.h
#include "LevelSequenceDirector.h"
#include "MySequenceDirector.generated.h"
UCLASS(Blueprintable)
class UMySequenceDirector : public ULevelSequenceDirector
{
GENERATED_BODY()
public:
// Called from Sequencer event tracks — bind to event key in the Sequencer editor
UFUNCTION(BlueprintCallable, Category = "Cinematics")
void OnDialogueStart(FName SpeakerTag);
UFUNCTION(BlueprintCallable, Category = "Cinematics")
void OnCutsceneChoice(int32 ChoiceIndex);
};
// MySequenceDirector.cpp
void UMySequenceDirector::OnDialogueStart(FName SpeakerTag)
{
// Access the sequence player and bound actors from within the Director.
// ULevelSequenceDirector.h: UPROPERTY ULevelSequencePlayer* Player (direct field, no getter)
TArray<UObject*> Bound = Player->GetBoundObjects(SpeakerBindingID);
// Trigger gameplay logic: UI, dialogue system, camera focus, etc.
}
```
**Why Director over Level Script**: The Director travels with the sequence asset, not the level.
Reusable across maps. Supports per-sequence state (member variables). Level Script events
only work when the sequence is placed in that specific level.
---
## MovieScene Tracks
```cpp
// MovieScene.h — get from the LevelSequence asset
UMovieScene* MS = SeqActor->GetSequence()->GetMovieScene();
// Possessables (world actors) — iterate by index
for (int32 i = 0; i < MS->GetPossessableCount(); ++i)
{
const FMovieScenePossessable& P = MS->GetPossessable(i);
}
// Spawnables (sequence-owned actors) — iterate by index
for (int32 i = 0; i < MS->GetSpawnableCount(); ++i)
{
const FMovieSceneSpawnable& S = MS->GetSpawnable(i);
}
// Master tracks (not bound to any actor)
const TArray<UMovieSceneTrack*>& Masters = MS->GetTracks();
// Tracks on a specific binding
const FMovieSceneBinding* B = MS->FindBinding(SomeGuid);
if (B) { for (UMovieSceneTrack* T : B->GetTracks()) { /* cast to subtype */ } }
```
| Track Class | Purpose |
|---|---|
| `UMovieScene3DTransformTrack` | Transform animation |
| `UMovieSceneSkeletalAnimationTrack` | Skeletal mesh animation |
| `UMovieSceneEventTrack` | Event triggers |
| `UMovieSceneAudioTrack` | Audio |
| `UMovieSceneFadeTrack` | Screen fade |
| `UMovieSceneCameraCutTrack` | Camera cuts |
| `UMovieSceneSubTrack` | Sub-sequences |
| `UMovieScenePropertyTrack` | Arbitrary UPROPERTY animation (base class) |
All track types require `MovieSceneTracks` in Build.cs.
**Custom tracks**: subclass `UMovieSceneTrack` + `UMovieSceneSection` for domain-specific
animation data. Register via `ISequencerModule::RegisterTrackEditor`. This is an advanced
pattern — most needs are met by `UMovieScenePropertyTrack` subclasses.
`UMovieScenePropertyTrack` animates arbitrary `UPROPERTY` values on bound objects. Common
concrete subclasses: `UMovieSceneFloatTrack` (float properties), `UMovieSceneBoolTrack`
(bool properties), `UMovieSceneColorTrack` (FLinearColor / FColor properties). Each creates
sections of the matching type (e.g., `UMovieSceneFloatSection`) that store a `FMovieSceneFloatChannel`.
### Sub-Sequences
```cpp
// Add a sub-sequence track to a master UMovieScene
UMovieScene* MasterMS = MasterSequence->GetMovieScene();
UMovieSceneSubTrack* SubTrack = MasterMS->AddTrack<UMovieSceneSubTrack>();
// Add a child sequence — AddSequence(Sequence, StartFrame, Duration)
FFrameRate DisplayRate = MasterMS->GetDisplayRate();
FFrameNumber Start = (2.0 * DisplayRate).FloorToFrame(); // 2 seconds in
int32 Duration = (5.0 * DisplayRate).FloorToFrame().Value; // 5 seconds long
UMovieSceneSubSection* SubSection =
SubTrack->AddSequence(ChildLevelSequence, Start, Duration);
// Configure sub-section timing
SubSection->Parameters.TimeScale = 1.0; // FMovieSceneTimeWarpVariant (double), playback speed multiplier
SubSection->Parameters.bCanLoop = false;
```
`UMovieSceneSubTrack` is a singleton master track — `AddTrack` returns the existing one if already present. Binding IDs inside sub-sequences carry a non-zero `SequenceID`; use `FMovieSceneObjectBindingID::ResolveParentIndex` when overriding bindings from the root player. Requires `MovieSceneTracks` module.
### Time Dilation and SlowMo
For SlowMo effects during sequences, use `Player->SetPlayRate(0.5f)` on the sequence player —
this is independent of `UGameplayStatics::SetGlobalTimeDilation` and does not affect world
physics or other gameplay systems. Combine with a post-process motion blur intensity increase
for cinematic slow motion without disrupting gameplay state.
---
## Movie Render Queue
```cpp
// MovieRenderPipelineCore module. Classes:
// UMoviePipelineQueue — holds jobs
// UMoviePipelineExecutorJob — sequence + map + config per job
// UMoviePipelinePrimaryConfig — root settings container
// UMoviePipelineOutputSetting — output path, file name format, frame rate
// Output formats: UMoviePipelineImageSequenceOutput_PNG / _EXR, UMoviePipelineAppleProResOutput
// Render passes: UMoviePipelineDeferredPassBase (final color, world normal, base color, depth)
// Add passes to the PrimaryConfig to capture multiple AOVs per frame
UMoviePipelineQueue* Queue = NewObject<UMoviePipelineQueue>(this);
UMoviePipelineExecutorJob* Job =
Queue->AllocateNewJob(UMoviePipelineExecutorJob::StaticClass());
Job->Sequence = FSoftObjectPath(MySequence);
Job->Map = FSoftObjectPath(GetWorld());
Job->JobName = TEXT("MyRender");
Job->SetConfiguration(NewObject<UMoviePipelinePrimaryConfig>(Job));
// Add pass/output settings to Job->GetConfiguration() as needed.
// Execute the render (editor only — MovieRenderPipelineEditor module)
UMoviePipelineQueueEngineSubsystem* QueueSubsystem =
GEngine->GetEngineSubsystem<UMoviePipelineQueueEngineSubsystem>();
QueueSubsystem->RenderQueueWithExecutor(UMoviePipelineInProcessExecutor::StaticClass());
// For runtime/non-editor rendering, implement UMoviePipelineExecutorBase instead.
```
---
## Common Mistakes
**Binding after Play**: Overrides applied after `Play()` miss frame 0.
Always `SetBinding*` before `Play()`.
**Multiplayer camera cuts**: Camera cuts only affect the local client.
Use `SeqActor->SetReplicatePlayback(true)` for server-driven sync.
**Camera not returning after cutscene**: Sequencer does not auto-restore the player camera.
Subscribe to `OnFinished` and call `SetViewTargetWithBlend` + re-enable input.
**GC of spawned sequence actor**: `CreateLevelSequencePlayer` returns a raw pointer.
Store `OutActor` in a `UPROPERTY()` member to prevent garbage collection.
**Skipping mid-sequence**: Call `SetCompletionModeOverride(ForceRestoreState)` before `Stop()`
so actors return to their pre-sequence state.
**PIE timing**: Avoid wall-clock assumptions; use `GetCurrentTime()` frame numbers.
**Deprecated direct property**: `SeqActor->SequencePlayer` is deprecated since UE 5.4.
Use `SeqActor->GetSequencePlayer()`.
**Wrong player instance**: Calling `Play()` on a stale or mismatched `ULevelSequencePlayer`
pointer (e.g., from a different `ALevelSequenceActor` in the level) produces no visible effect
or animates the wrong actors. Always retrieve the player from the specific `ALevelSequenceActor`
that owns the sequence you intend to drive.
---
## Related Skills
- `ue-actor-component-architecture` — actor spawning and component setup for bound actors
- `ue-animation-system` — skeletal animation tracks, AnimMontage integration in Sequencer
- `ue-cpp-foundations` — delegate patterns, `UFUNCTION`, `UPROPERTY`
- `ue-gameplay-framework` — PlayerController camera restoration, GameMode flow
Creator's repository · quodsoler/unreal-engine-skills