Skip to content
Snippets Groups Projects
sequence.rs 6.96 KiB
use std::time::Duration;

use bevy::prelude::*;

use bevy_tweening::{lens::*, *};

fn main() {
    App::default()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            window: WindowDescriptor {
                title: "Sequence".to_string(),
                width: 600.,
                height: 600.,
                present_mode: bevy::window::PresentMode::Fifo, // vsync
                ..default()
            },
            ..default()
        }))
        .add_system(bevy::window::close_on_esc)
        .add_plugin(TweeningPlugin)
        .add_startup_system(setup)
        .add_system(update_text)
        .run();
}

#[derive(Component)]
struct RedProgress;

#[derive(Component)]
struct BlueProgress;

#[derive(Component)]
struct RedSprite;

#[derive(Component)]
struct BlueSprite;

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());

    let font = asset_server.load("fonts/FiraMono-Regular.ttf");
    let text_style_red = TextStyle {
        font: font.clone(),
        font_size: 50.0,
        color: Color::RED,
    };
    let text_style_blue = TextStyle {
        font,
        font_size: 50.0,
        color: Color::BLUE,
    };

    let text_alignment = TextAlignment {
        vertical: VerticalAlign::Center,
        horizontal: HorizontalAlign::Center,
    };

    // Text with the index of the active tween in the sequence
    commands.spawn((
        Text2dBundle {
            text: Text {
                sections: vec![
                    TextSection {
                        value: "progress: ".to_owned(),
                        style: text_style_red.clone(),
                    },
                    TextSection {
                        value: "0%".to_owned(),
                        style: text_style_red,
                    },
                ],
                alignment: text_alignment,
            },
            transform: Transform::from_translation(Vec3::new(0., 40., 0.)),
            ..default()
        },
        RedProgress,
    ));

    // Text with progress of the active tween in the sequence
    commands.spawn((
        Text2dBundle {
            text: Text {
                sections: vec![
                    TextSection {
                        value: "progress: ".to_owned(),
                        style: text_style_blue.clone(),
                    },
                    TextSection {
                        value: "0%".to_owned(),
                        style: text_style_blue,
                    },
                ],
                alignment: text_alignment,
            },
            transform: Transform::from_translation(Vec3::new(0., -40., 0.)),
            ..default()
        },
        BlueProgress,
    ));

    let size = 25.;

    let margin = 40.;
    let screen_x = 600.;
    let screen_y = 600.;
    let center = Vec3::new(screen_x / 2., screen_y / 2., 0.);

    // Run around the window from corner to corner
    let dests = &[
        Vec3::new(margin, margin, 0.),
        Vec3::new(screen_x - margin, margin, 0.),
        Vec3::new(screen_x - margin, screen_y - margin, 0.),
        Vec3::new(margin, screen_y - margin, 0.),
        Vec3::new(margin, margin, 0.),
    ];
    // Build a sequence from an iterator over a Tweenable (here, a
    // Tracks<Transform>)
    let seq = Sequence::new(dests.windows(2).enumerate().map(|(index, pair)| {
        Tracks::new([
            Tween::new(
                EaseFunction::QuadraticInOut,
                Duration::from_millis(250),
                TransformRotateZLens {
                    start: 0.,
                    end: 180_f32.to_radians(),
                },
            )
            .with_repeat_count(RepeatCount::Finite(4))
            .with_repeat_strategy(RepeatStrategy::MirroredRepeat),
            Tween::new(
                EaseFunction::QuadraticInOut,
                Duration::from_secs(1),
                TransformPositionLens {
                    start: pair[0] - center,
                    end: pair[1] - center,
                },
            )
            // Get an event after each segment
            .with_completed_event(index as u64),
        ])
    }));

    commands.spawn((
        SpriteBundle {
            sprite: Sprite {
                color: Color::RED,
                custom_size: Some(Vec2::new(size, size)),
                ..default()
            },
            ..default()
        },
        RedSprite,
        Animator::new(seq),
    ));

    // First move from left to right, then rotate around self 180 degrees while
    // scaling size at the same time.
    let tween_move = Tween::new(
        EaseFunction::QuadraticInOut,
        Duration::from_secs(1),
        TransformPositionLens {
            start: Vec3::new(-200., 100., 0.),
            end: Vec3::new(200., 100., 0.),
        },
    )
    .with_completed_event(99); // Get an event once move completed
    let tween_rotate = Tween::new(
        EaseFunction::QuadraticInOut,
        Duration::from_secs(1),
        TransformRotationLens {
            start: Quat::IDENTITY,
            end: Quat::from_rotation_z(180_f32.to_radians()),
        },
    );
    let tween_scale = Tween::new(
        EaseFunction::QuadraticInOut,
        Duration::from_secs(1),
        TransformScaleLens {
            start: Vec3::ONE,
            end: Vec3::splat(2.0),
        },
    );
    // Build parallel tracks executing two tweens at the same time: rotate and
    // scale.
    let tracks = Tracks::new([tween_rotate, tween_scale]);
    // Build a sequence from an heterogeneous list of tweenables by casting them
    // manually to a BoxedTweenable: first move, then { rotate + scale }.
    let seq2 = Sequence::new([Box::new(tween_move) as BoxedTweenable<_>, tracks.into()]);

    commands.spawn((
        SpriteBundle {
            sprite: Sprite {
                color: Color::BLUE,
                custom_size: Some(Vec2::new(size * 3., size)),
                ..default()
            },
            ..Default::default()
        },
        BlueSprite,
        Animator::new(seq2),
    ));
}

fn update_text(
    mut query_text_red: Query<&mut Text, (With<RedProgress>, Without<BlueProgress>)>,
    mut query_text_blue: Query<&mut Text, (With<BlueProgress>, Without<RedProgress>)>,
    query_anim_red: Query<&Animator<Transform>, With<RedSprite>>,
    query_anim_blue: Query<&Animator<Transform>, With<BlueSprite>>,
    mut query_event: EventReader<TweenCompleted>,
) {
    let anim_red = query_anim_red.single();
    let progress_red = anim_red.tweenable().progress();

    let anim_blue = query_anim_blue.single();
    let progress_blue = anim_blue.tweenable().progress();

    let mut red_text = query_text_red.single_mut();
    red_text.sections[1].value = format!("{:5.1}%", progress_red * 100.);

    let mut blue_text = query_text_blue.single_mut();
    blue_text.sections[1].value = format!("{:5.1}%", progress_blue * 100.);

    for ev in query_event.iter() {
        println!(
            "Event: TweenCompleted entity={:?} user_data={}",
            ev.entity, ev.user_data
        );
    }
}