Closed kristoff3r closed 6 months ago
You can do this currently- Example here: https://github.com/vectorgameexperts/bevy-vello/blob/main/demo/src/main.rs
The demo which exhibits state machine behavior and playhead tracking: https://vectorgameexperts.github.io/bevy-vello/ (must be on Chrome or FF Nightly with a flag)
Also, I would recommend using lottielab.com to create lottie animations. It is what I am using to support and maintain this crate.
I am going to assume you have a health bar lottie file which is animated and goes from 0 to 100 health linearly, in a 100 frame rate animation.
Hence, if you had a playhead at 60.0, the animation would reflect 60/100 health.
First, spawn your health bar:
commands
.spawn(VelloAssetBundle {
vector: asset_server.load("../assets/health-bar.json"),
..default()
})
.insert(
LottiePlayer::new("forward")
.with_state({
PlayerState::new("forward")
.with_playback_options(PlaybackOptions {
looping: PlaybackLoopBehavior::DoNotLoop,
direction: PlaybackDirection::Normal,
autoplay: false,
..default()
})
.with_theme(Theme::new().add("health", Color::GREEN))
.reset_playhead_on_start(false)
})
.with_state(
PlayerState::new("reverse")
.with_playback_options(PlaybackOptions {
looping: PlaybackLoopBehavior::DoNotLoop,
direction: PlaybackDirection::Reverse,
autoplay: false,
..default()
})
.with_theme(Theme::new().add("health", Color::RED))
)
...
Then, I would use an event system like this:
pub fn control_animation(
mut health_events: EventReader<HealthEvent>,
mut controller_query: Query<(
&mut LottiePlayer,
&mut Playhead,
&mut PlaybackOptions,
&mut Theme,
), With<HealthBarTag>>,
mut player_query: Query<(
&mut MyPlayer
), With<HealthBarTag>>,
assets: Res<Assets<VelloAsset>>,
) {
let Ok((mut controller, mut playhead, mut options, mut theme, handle)) =
controller_query.get_single_mut()
else {
return;
};
let Ok(mut my_player) = player_query.get_single_mut()
else {
return;
};
for health_event in health_events.read() {
match health_event {
HealthEvent::Damaged(amount) => {
my_player.health = (my_player.health - amount).min(0.0);
controller.transition("reverse");
controller.play();
}
HealthEvent::Healed(amount) => {
my_player.health = (my_player.health + amount).max(100.0);
controller.transition("forward");
controller.play();
}
}
}
theme.edit("layer name", Color::YELLOW); // Interpolate between red and green based on my_player.health
if my_player.health == playhead.frame() {
// Reached desired health
controller.pause();
}
}
Side note: I think this would make a great example. If you want to produce one, I'll accept the PR. Otherwise, I may do this myself when I find bandwidth.
Thanks for the comprehensive response, that looks pretty easy to work with!
I'll try to implement it as an example and do a PR.
First, great work on this crate so far!
I'm evaluating using this for a project where it seems lottie might fit nicely, but I would need more fine grained than what this crate currently provides. An example effect would be a health bar that I can set to e.g. 80%, and then after taking damage it would animate towards 60%. Is it possible for this crate to expose an API that makes this possible?
Also, what is the recommended way to generate lottie files without using Adobe? I tried a bunch of stuff yesterday to convert one of my SVGs to no avail.