keyframe-animate 0.1.1

A keyframe animtaion library for bevyengine
Documentation
//! Create and play an animation defined by code that operates on the
//! `Transform` component.

use std::f32::consts::{FRAC_PI_2, PI};

use bevy::prelude::*;
use keyframe_animate::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugin(KeyframeAnimationPlugin)
        .insert_resource(AmbientLight {
            color: Color::WHITE,
            brightness: 1.0,
        })
        .add_startup_system(setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // Camera
    commands.spawn_bundle(PerspectiveCameraBundle {
        transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });

    // The animation API uses the `Name` component to target entities
    let planet = Name::new("planet");
    let orbit_controller = Name::new("orbit_controller");
    let satellite = Name::new("satellite");

    // Creating the animation
    let mut animation = KeyframeAnimationClip::default();
    // A curve can modify a single part of a transform, here the translation
    animation.add_curve_to_path(
        KeyframeEntityPath {
            parts: vec![planet.clone()],
        },
        KeyframeVariableCurve {
            options: Some(vec!["translation".to_string()]),
            keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
            keyframes: Keyframe::translation(vec![
                Vec3::new(1.0, 0.0, 1.0),
                Vec3::new(-1.0, 0.0, 1.0),
                Vec3::new(-1.0, 0.0, -1.0),
                Vec3::new(1.0, 0.0, -1.0),
                // in case seamless looping is wanted, the last keyframe should
                // be the same as the first one
                Vec3::new(1.0, 0.0, 1.0),
            ]),
        },
    );
    // Or it can modify the rotation of the transform.
    // To find the entity to modify, the hierarchy  will be traversed looking for
    // an entity with the right name at each level
    animation.add_curve_to_path(
        KeyframeEntityPath {
            parts: vec![planet.clone(), orbit_controller.clone()],
        },
        KeyframeVariableCurve {
            options: Some(vec!["rotation".to_string()]),
            keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
            keyframes: Keyframe::rotation(vec![
                Quat::from_axis_angle(Vec3::Y, 0.0),
                Quat::from_axis_angle(Vec3::Y, FRAC_PI_2),
                Quat::from_axis_angle(Vec3::Y, PI),
                Quat::from_axis_angle(Vec3::Y, 3.0 * FRAC_PI_2),
                Quat::from_axis_angle(Vec3::Y, 0.0),
            ]),
        },
    );
    // If a curve in an animation is shorter than the other, it will not repeat
    // until all other curves are finished. In that case, another animation should
    // be created for each part that would have a different duration / period
    animation.add_curve_to_path(
        KeyframeEntityPath {
            parts: vec![planet.clone(), orbit_controller.clone(), satellite.clone()],
        },
        KeyframeVariableCurve {
            options: Some(vec!["scale".to_string()]),
            keyframe_timestamps: vec![0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
            keyframes: Keyframe::scale(vec![
                Vec3::splat(0.8),
                Vec3::splat(1.2),
                Vec3::splat(0.8),
                Vec3::splat(1.2),
                Vec3::splat(0.8),
                Vec3::splat(1.2),
                Vec3::splat(0.8),
                Vec3::splat(1.2),
                Vec3::splat(0.8),
            ]),
        },
    );
    // There can be more than one curve targeting the same entity path
    animation.add_curve_to_path(
        KeyframeEntityPath {
            parts: vec![planet.clone(), orbit_controller.clone(), satellite.clone()],
        },
        KeyframeVariableCurve {
            options: Some(vec!["rotation".to_string()]),
            keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
            keyframes: Keyframe::rotation(vec![
                Quat::from_axis_angle(Vec3::Y, 0.0),
                Quat::from_axis_angle(Vec3::Y, FRAC_PI_2),
                Quat::from_axis_angle(Vec3::Y, PI),
                Quat::from_axis_angle(Vec3::Y, 3.0 * FRAC_PI_2),
                Quat::from_axis_angle(Vec3::Y, 0.0),
            ]),
        },
    );

    // Create the animation player, and set it to repeat
    let mut player = KeyframeAnimationPlayer::new(animation);
    player.repeat();

    // Create the scene that will be animated
    // First entity is the planet
    commands
        .spawn_bundle(PbrBundle {
            mesh: meshes.add(Mesh::from(shape::Icosphere::default())),
            material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
            ..default()
        })
        // Add the Name component, and the animation player
        .insert_bundle((planet, player))
        .with_children(|p| {
            // This entity is just used for animation, but doesn't display anything
            p.spawn_bundle(TransformBundle { ..default() })
                // Add the Name component
                .insert(orbit_controller)
                .with_children(|p| {
                    // The satellite, placed at a distance of the planet
                    p.spawn_bundle(PbrBundle {
                        transform: Transform::from_xyz(1.5, 0.0, 0.0),
                        mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })),
                        material: materials.add(Color::rgb(0.3, 0.9, 0.3).into()),
                        ..default()
                    })
                    // Add the Name component
                    .insert(satellite);
                });
        });
}