bevy_director 0.2.0

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"));
//! ```

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 = "viewfinder")]
pub mod viewfinder;

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

pub mod prelude {
    pub use crate::{
        Blend, CineCamera, DirectorCommands, DirectorPhase, DirectorPlugin, DirectorState,
        Filmback, FinishReason, FocusTrack, FovSpec, HandoffCamera, Key, Lens, LetterboxSettings,
        Look, Marker, MarkerReached, PlayOptions, RailKind, Rig, ScalarKey, ScalarTrack,
        SequenceAsset, SequenceFinished, SequencePlayer, SequenceStarted, Shake, Shot, TargetRef,
        gameplay_camera_free,
    };
    #[cfg(feature = "editor")]
    pub use crate::{
        DirectorsCutConfig, DirectorsCutGrid, DirectorsCutPlugin, DirectorsCutState,
        EditorSelection, KeyTrack,
    };
    #[cfg(feature = "viewfinder")]
    pub use crate::{ViewfinderConfig, ViewfinderPlugin};
}

/// 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. The viewfinder is its own plugin (feature "viewfinder").
pub struct DirectorPlugin;

impl Plugin for DirectorPlugin {
    fn build(&self, app: &mut App) {
        app.init_asset::<SequenceAsset>()
            .register_asset_loader(SequenceLoader)
            .register_type::<SequenceAsset>()
            .init_resource::<DirectorState>()
            .add_message::<player::DirectorRequest>()
            .add_message::<SequenceStarted>()
            .add_message::<MarkerReached>()
            .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::apply_pose, letterbox::sync_letterbox)
                    .chain()
                    .in_set(DirectorSet::Apply),
            );
    }
}