voxell-tech / bevy_motiongfx

Motion graphics creation tool in Bevy. (Highly inspired by Motion Canvas and Manim)
Apache License 2.0
169 stars 3 forks source link

Overhaul repo, Removing `bevy_vello_graphics` & `bevy_typst` #51

Closed nixon-voxell closed 5 months ago

nixon-voxell commented 5 months ago

Changelog

TL;DR

A new foundational innovation stated below allow us to break away from the previous rigid build struct structure provided by the command extensions: build_pbr, build_fsvector, build_fvector, build_svector.

It is now encouraged to spawn entities using the "Bevy style":

// I am just showing the type of components I am adding,
// instantiate them properly in your code base.
let comps = (Transform, VelloLine, Fill, Stroke);
let id = commands.spawn(VelloSceneBundle::default()).insert(comps.clone()).id();

Which can then be coupled with the Entity (id) to form a flexible actionable "motion" tuple (note that you can put up to 20 items inside the comps tuple as long as none of them conflict with each other in terms of Type).

let mut graphics = (id, comps);
graphics.transform().to_translation_x(10.0);
graphics.fill().to_color(Color::RED);
// Or mutably referencing the value if the specific
// motion trait is not applied to that type
act!(
    (graphics.id(), VelloLine),
    start = { *graphics.get_mut::<VelloLine>() },
    end = graphics.get_mut::<VelloLine>().extend(100.0),
);

GetMutValue trait implemented to all kinds of tuple combinations

A new macro called tuple_combinations!(impl_macro, count) was added to create implementation of traits for all tuple combinations.

// Define the macro for implementing the trait
macro_rules! impl_trait_macro { ... }

tuple_combinations!(impl_trait_macro, 3);
// This will implement
impl<T0, T1> Trait<T0, 0> for (T0, T1) { }
impl<T0, T1> Trait<T1, 1> for (T0, T1) { }
impl<T0, T1, T2> Trait<T0, 0> for (T0, T1, T2) { }
impl<T0, T1, T2> Trait<T1, 1> for (T0, T1, T2) { }
impl<T0, T1, T2> Trait<T2, 2> for (T0, T1, T2) { }

This macro is used to implement GetMutValue trait so that it implements for all combinations up until 20.

Which means this is now valid code:

let comps = (Transform::default(), Sprite::default(), Visibility::default());
let transform: &mut Transform = comps.get_mut_value();

Rust will now automatically find the correct location of the item in the tuple based on Type!

Easy Type reference using GetMut trait

Sometimes, writing

let transform: &mut Transform = comps.get_mut_value();

is quite troublesome. For one because you need to perform type annotation and assign it to a variable before using it. The GetMut trait implemented on top of GetMutValue trait intends to remove this limitation by allowing you to specify the Type at the function level:

// It's now a lot easier to use and shorter as well!
let transform = comps.get_mut::<Transform>();

Summary

A full example:

fn create_line_animation(mut commands: Commands) {
    // Color palette
    let palette = ColorPalette::default();

    // Create line vello graphics
    let line = (
        VelloLine::new(DVec2::new(-300.0, 0.0), DVec2::new(300.0, 0.0)),
        Stroke::default().with_brush(Brush::from_color(palette.get(ColorKey::Base8))),
        Transform::from_xyz(0.0, -100.0, 0.0),
    );
    let id = commands
        .spawn(VelloSceneBundle::default())
        .insert(line.clone())
        .id();
    let mut line = (id, line);

    // Create sequence
    let sequence = [
        commands
            .add_motion({
                let y = line.transform().transform.translation.y;
                line.transform().to_translation_y(y - 100.0).animate(1.5)
            })
            .add_motion(
                act!(
                    (line.id(), VelloLine),
                    start = { *line.get_mut::<VelloLine>() },
                    end = line.get_mut::<VelloLine>().extend(100.0),
                )
                .animate(1.0),
            )
            .add_motion(line.stroke().to_width(10.0).animate(1.0))
            .all(),
        commands
            .add_motion({
                let y = line.transform().transform.translation.y;
                line.transform().to_translation_y(y + 100.0).animate(1.5)
            })
            .add_motion(
                act!(
                    (line.id(), VelloLine),
                    start = { *line.get_mut::<VelloLine>() },
                    end = line.get_mut::<VelloLine>().extend(-100.0),
                )
                .animate(1.0),
            )
            .add_motion(line.stroke().to_width(1.0).animate(1.0))
            .all(),
    ]
    .chain();

    commands.spawn(SequencePlayerBundle {
        sequence,
        ..default()
    });
}