//! Animation for the game engine Bevy #![warn(missing_docs)] use std::ops::Deref; use bevy_app::{App, CoreStage, Plugin}; use bevy_asset::{AddAsset, Assets, Handle}; use bevy_core::Name; use bevy_ecs::{ change_detection::DetectChanges, entity::Entity, prelude::Component, reflect::ReflectComponent, schedule::IntoSystemDescriptor, system::{Query, Res}, }; use bevy_hierarchy::Children; use bevy_math::{Quat, Vec3}; use bevy_reflect::{Reflect, TypeUuid}; use bevy_time::Time; use bevy_transform::{prelude::Transform, TransformSystem}; use bevy_utils::{tracing::warn, HashMap}; #[allow(missing_docs)] pub mod prelude { #[doc(hidden)] pub use crate::{ AnimationClip, AnimationPlayer, AnimationPlugin, EntityPath, Keyframes, VariableCurve, }; } /// List of keyframes for one of the attribute of a [`Transform`]. #[derive(Clone, Debug)] pub enum Keyframes { /// Keyframes for rotation. Rotation(Vec), /// Keyframes for translation. Translation(Vec), /// Keyframes for scale. Scale(Vec), } /// Describes how an attribute of a [`Transform`] should be animated. /// /// `keyframe_timestamps` and `keyframes` should have the same length. #[derive(Clone, Debug)] pub struct VariableCurve { /// Timestamp for each of the keyframes. pub keyframe_timestamps: Vec, /// List of the keyframes. pub keyframes: Keyframes, } /// Path to an entity, with [`Name`]s. Each entity in a path must have a name. #[derive(Clone, Debug, Hash, PartialEq, Eq, Default)] pub struct EntityPath { /// Parts of the path pub parts: Vec, } /// A list of [`VariableCurve`], and the [`EntityPath`] to which they apply. #[derive(Clone, TypeUuid, Debug, Default)] #[uuid = "d81b7179-0448-4eb0-89fe-c067222725bf"] pub struct AnimationClip { curves: HashMap>, duration: f32, } impl AnimationClip { #[inline] /// Hashmap of the [`VariableCurve`]s per [`EntityPath`]. pub fn curves(&self) -> &HashMap> { &self.curves } /// Duration of the clip, represented in seconds #[inline] pub fn duration(&self) -> f32 { self.duration } /// Add a [`VariableCurve`] to an [`EntityPath`]. pub fn add_curve_to_path(&mut self, path: EntityPath, curve: VariableCurve) { // Update the duration of the animation by this curve duration if it's longer self.duration = self .duration .max(*curve.keyframe_timestamps.last().unwrap_or(&0.0)); self.curves.entry(path).or_default().push(curve); } } /// Animation controls #[derive(Component, Reflect)] #[reflect(Component)] pub struct AnimationPlayer { paused: bool, repeat: bool, speed: f32, elapsed: f32, animation_clip: Handle, } impl Default for AnimationPlayer { fn default() -> Self { Self { paused: false, repeat: false, speed: 1.0, elapsed: 0.0, animation_clip: Default::default(), } } } impl AnimationPlayer { /// Start playing an animation, resetting state of the player pub fn start(&mut self, handle: Handle) -> &mut Self { *self = Self { animation_clip: handle, ..Default::default() }; self } /// Start playing an animation, resetting state of the player, unless the requested animation is already playing. pub fn play(&mut self, handle: Handle) -> &mut Self { if self.animation_clip != handle || self.is_paused() { self.start(handle); } self } /// Set the animation to repeat pub fn repeat(&mut self) -> &mut Self { self.repeat = true; self } /// Stop the animation from repeating pub fn stop_repeating(&mut self) -> &mut Self { self.repeat = false; self } /// Pause the animation pub fn pause(&mut self) { self.paused = true; } /// Unpause the animation pub fn resume(&mut self) { self.paused = false; } /// Is the animation paused pub fn is_paused(&self) -> bool { self.paused } /// Speed of the animation playback pub fn speed(&self) -> f32 { self.speed } /// Set the speed of the animation playback pub fn set_speed(&mut self, speed: f32) -> &mut Self { self.speed = speed; self } /// Time elapsed playing the animation pub fn elapsed(&self) -> f32 { self.elapsed } /// Seek to a specific time in the animation pub fn set_elapsed(&mut self, elapsed: f32) -> &mut Self { self.elapsed = elapsed; self } } /// System that will play all animations, using any entity with a [`AnimationPlayer`] /// and a [`Handle`] as an animation root pub fn animation_player( time: Res