Skip to content

Latest commit

 

History

History
179 lines (142 loc) · 7.76 KB

animated_transformation.md

File metadata and controls

179 lines (142 loc) · 7.76 KB

Animated Transformation

We can define a curve and make a shape moves along the curve. This is done by AnimationClip and AnimationPlayer. We define the curve in an AnimationClip and give it an identity of the shape. The AnimationClip is then played by an AnimationPlayer.

The AnimationClip receives an identity and a curve by its add_curve_to_path method.

let mut animation = AnimationClip::default();

animation.add_curve_to_path(
    EntityPath {
        // ...
    },
    VariableCurve {
        // ...
    },
);

We pass the identity into EntityPath and pass a description of the curve into VariableCurve.

The identity is created by Name.

let rectangle_name = Name::new("rectangle");

// ...

animation.add_curve_to_path(
    EntityPath {
        parts: vec![rectangle_name.clone()],
    },
    // ...
);

The VariableCurve defines the curve.

animation.add_curve_to_path(
    // ...
    VariableCurve {
        keyframe_timestamps: vec![0., 1., 2., 3.],
        keyframes: Keyframes::Translation(vec![
            Vec3::new(-100., 50., 0.),
            Vec3::new(0., -50., 0.),
            Vec3::new(100., 50., 0.),
            Vec3::new(-100., 50., 0.),
        ]),
    },
);

The keyframe_timestamps in VariableCurve defines the moment (timestamps in seconds) that a transformation occurs. The keyframes in VariableCurve defines the actual transformation that happens at the corresponding timestamps. Here, we use Keyframes::Translation in keyframes. This describes the positions of the shape at the corresponding timestamps. For example, at the beginning, the shape should be located at Vec3::new(-100., 50., 0.), at the first second, it should be located at Vec3::new(0., -50., 0.), and so on.

We use AnimationPlayer to change the shape according to the AnimationClip.

fn setup(
    mut animations: ResMut<Assets<AnimationClip>>,
    // ...
) {
    // ...

    let mut player = AnimationPlayer::default();
    player.play(animations.add(animation)).repeat();

    // ...
}

We first add the AnimationClip to the resource Assets<AnimationClip>. And then pass the Handle of the AnimationClip to the method play of the AnimationPlayer. This tells the AnimationPlayer to start changing the corresponding shape. We additionally call the method repeat() to allow the AnimationClip to be played repeatedly.

Finally, we make the app to spawn a rectangle.

commands.spawn((
    ColorMesh2dBundle {
        mesh: meshes.add(Quad::new((100., 100.).into()).into()).into(),
        ..default()
    },
    rectangle_name,
    player,
));

Be sure to spawn the ColorMesh2dBundle along with the Name (rectangle_name here) and the AnimationPlayer. So that the engine knows which shape the identity is for and which tool is able to change the shape.

The full code is as follows:

use bevy::{
    animation::{AnimationClip, AnimationPlayer, EntityPath, Keyframes, VariableCurve},
    app::{App, Startup},
    asset::Assets,
    core::Name,
    core_pipeline::core_2d::Camera2dBundle,
    ecs::system::{Commands, ResMut},
    math::Vec3,
    prelude::default,
    render::mesh::{shape::Quad, Mesh},
    sprite::ColorMesh2dBundle,
    DefaultPlugins,
};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut animations: ResMut<Assets<AnimationClip>>,
) {
    commands.spawn(Camera2dBundle::default());

    let rectangle_name = Name::new("rectangle");
    let mut animation = AnimationClip::default();

    animation.add_curve_to_path(
        EntityPath {
            parts: vec![rectangle_name.clone()],
        },
        VariableCurve {
            keyframe_timestamps: vec![0., 1., 2., 3.],
            keyframes: Keyframes::Translation(vec![
                Vec3::new(-100., 50., 0.),
                Vec3::new(0., -50., 0.),
                Vec3::new(100., 50., 0.),
                Vec3::new(-100., 50., 0.),
            ]),
        },
    );

    let mut player = AnimationPlayer::default();
    player.play(animations.add(animation)).repeat();

    commands.spawn((
        ColorMesh2dBundle {
            mesh: meshes.add(Quad::new((100., 100.).into()).into()).into(),
            ..default()
        },
        rectangle_name,
        player,
    ));
}

When the app starts:

Animated Transformation 1

At the 1st second:

Animated Transformation 2

At the 2nd second:

Animated Transformation 3

For keyframes, in addition to Keyframes::Translation, we can also use Keyframes::Rotation and Keyframes::Scale.

➡️ Next: Keyboard Input

📘 Back: Table of contents