bevy_director 0.7.0-dev

Unreal-Sequencer-inspired cinematic camera and sequence system for Bevy
Documentation
//! bevy_director: shots, sequences, and a camera you can direct.
//!
//! An Unreal-Sequencer-shaped cinematic layer for Bevy: sequences are RON
//! assets made of shots; shots are camera rigs with eased keys and a
//! lens; playback hands off from your gameplay camera and glides back to
//! it when the take ends. The viewfinder feature adds an in-game capture
//! mode: fly, frame, press K.
//!
//! Quickstart:
//! ```ignore
//! app.add_plugins(DirectorPlugin);
//! // gate your own camera-driving system:
//! //   my_camera_system.run_if(bevy_director::gameplay_camera_free)
//! commands.play_sequence(asset_server.load("sequences/intro.dir.ron"));
//! ```
//!
//! While the director owns the frame, the game should also yield its
//! keyboard, mouse, and cursor grabbing: gate them with [`director_idle`],
//! or bridge [`DirectorState::phase`](player::DirectorState) into your
//! game's state machine. The README's "Integrating with a live game"
//! section walks through the full checklist.

use bevy::{prelude::*, transform::TransformSystems};

pub mod curve;
pub mod eval;
pub mod letterbox;
pub mod loader;
pub mod player;
pub mod sequence;
mod shake;

#[cfg(feature = "editor")]
pub mod editor;
#[cfg(feature = "gizmos")]
pub mod gizmos;
#[cfg(feature = "titles")]
pub mod titles;
#[cfg(feature = "viewfinder")]
pub mod viewfinder;

pub use curve::ArcLengthLut;
#[cfg(feature = "editor")]
pub use editor::{
    DirectorsCutConfig, DirectorsCutGrid, DirectorsCutPlugin, DirectorsCutState, EditorSelection,
    KeyTrack,
};
pub use eval::{
    ActiveActorCue, ActiveText, BakeError, CameraPose, CameraSnapshot, CompiledSequence, DofParams,
    EvalCtx, bake,
};
pub use letterbox::LetterboxSettings;
pub use loader::{SequenceError, SequenceLoader, parse_sequence, to_ron_string};
pub use player::{
    ActiveActorCues, ActiveTexts, CineCamera, ClockSource, DirectorCommands, DirectorPhase,
    DirectorState, FinishReason, HandoffCamera, LoopMode, MarkerReached, PlayOptions, Playback,
    SequenceCut, SequenceFinished, SequencePlayer, SequenceStarted, director_active, director_idle,
    gameplay_camera_free,
};
pub use sequence::{
    ActorCue, ActorTrack, Blend, DofMode, Filmback, FocusTrack, FovSpec, GradeTrack, Key,
    KeyInterp, Lens, Look, Marker, MotionBlurSpec, RailKind, Rig, ScalarKey, ScalarTrack,
    SequenceAsset, Shake, Shot, TargetRef, TextAnchor, TextBlock, TextBlockStyle,
};
#[cfg(feature = "titles")]
pub use titles::{TitleAnchorZone, TitleOverlayRoot, TitlesConfig, TitlesPlugin};
#[cfg(feature = "viewfinder")]
pub use viewfinder::{ViewfinderConfig, ViewfinderPlugin, ViewfinderSession};

pub mod prelude {
    pub use crate::{
        ActiveActorCue, ActiveActorCues, ActiveText, ActiveTexts, ActorCue, ActorTrack, Blend,
        CineCamera, DirectorCommands, DirectorPhase, DirectorPlugin, DirectorState, Filmback,
        FinishReason, FocusTrack, FovSpec, GradeTrack, HandoffCamera, Key, Lens, LetterboxSettings,
        Look, Marker, MarkerReached, MotionBlurSpec, PlayOptions, RailKind, Rig, ScalarKey,
        ScalarTrack, SequenceAsset, SequenceCut, SequenceFinished, SequencePlayer, SequenceStarted,
        Shake, Shot, TargetRef, TextAnchor, TextBlock, TextBlockStyle, gameplay_camera_free,
    };
    #[cfg(feature = "editor")]
    pub use crate::{
        DirectorsCutConfig, DirectorsCutGrid, DirectorsCutPlugin, DirectorsCutState,
        EditorSelection, KeyTrack,
    };
    #[cfg(feature = "titles")]
    pub use crate::{TitlesConfig, TitlesPlugin};
    #[cfg(feature = "viewfinder")]
    pub use crate::{ViewfinderConfig, ViewfinderPlugin, ViewfinderSession};
}

/// The director's PostUpdate passes, chained, before transform
/// propagation: a pose written this frame renders this frame.
#[derive(SystemSet, Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum DirectorSet {
    /// Requests, orphan guard, hot reload.
    Control,
    /// Playhead advance, markers, end-of-sequence routing.
    Tick,
    /// Writing the pose onto the camera.
    Apply,
}

/// Core plugin: asset type and loader, messages, the playback state
/// machine, and — with the "titles" feature, on by default — the text
/// overlay renderer. The viewfinder is its own plugin (feature
/// "viewfinder"); a game that never authors sequences never needs it.
pub struct DirectorPlugin;

impl Plugin for DirectorPlugin {
    fn build(&self, app: &mut App) {
        // The overlay is part of the runtime, not the editor: a sequence
        // that carries text draws it in a plain game build.
        #[cfg(feature = "titles")]
        if !app.is_plugin_added::<titles::TitlesPlugin>() {
            app.add_plugins(titles::TitlesPlugin);
        }
        app.init_asset::<SequenceAsset>()
            .register_asset_loader(SequenceLoader)
            .register_type::<SequenceAsset>()
            .init_resource::<DirectorState>()
            .init_resource::<ActiveTexts>()
            .init_resource::<ActiveActorCues>()
            .add_message::<player::DirectorRequest>()
            .add_message::<SequenceStarted>()
            .add_message::<MarkerReached>()
            .add_message::<SequenceCut>()
            .add_message::<SequenceFinished>()
            .configure_sets(
                PostUpdate,
                (DirectorSet::Control, DirectorSet::Tick, DirectorSet::Apply)
                    .chain()
                    .before(TransformSystems::Propagate),
            )
            .add_systems(
                PostUpdate,
                (
                    player::handle_requests,
                    player::rebake_on_asset_change,
                    player::guard_orphans,
                )
                    .chain()
                    .in_set(DirectorSet::Control),
            )
            .add_systems(PostUpdate, player::tick_players.in_set(DirectorSet::Tick))
            .add_systems(
                PostUpdate,
                (
                    player::execute_cuts,
                    player::apply_pose,
                    letterbox::sync_letterbox,
                    player::update_active_texts,
                    player::update_active_actor_cues,
                )
                    .chain()
                    .in_set(DirectorSet::Apply),
            );
    }
}