diff --git a/crates/bevy_animation/Cargo.toml b/crates/bevy_animation/Cargo.toml index a1d019ad1abae..4f493ceb34366 100644 --- a/crates/bevy_animation/Cargo.toml +++ b/crates/bevy_animation/Cargo.toml @@ -33,6 +33,7 @@ petgraph = { version = "0.6", features = ["serde-1"] } ron = "0.8" serde = "1" blake3 = { version = "1.0" } +downcast-rs = "1.2.0" derive_more = { version = "1", default-features = false, features = [ "error", "from", diff --git a/crates/bevy_animation/src/animation_curves.rs b/crates/bevy_animation/src/animation_curves.rs index 6ca1ec34c4821..f9b031850128a 100644 --- a/crates/bevy_animation/src/animation_curves.rs +++ b/crates/bevy_animation/src/animation_curves.rs @@ -91,7 +91,7 @@ use bevy_math::{ }, Quat, Vec3, }; -use bevy_reflect::{FromReflect, Reflect, Reflectable, TypePath}; +use bevy_reflect::{FromReflect, Reflect, Reflectable}; use bevy_render::mesh::morph::MorphWeights; use bevy_transform::prelude::Transform; @@ -100,6 +100,7 @@ use crate::{ prelude::{Animatable, BlendInput}, AnimationEntityMut, AnimationEvaluationError, }; +use downcast_rs::{impl_downcast, Downcast}; /// A value on a component that Bevy can animate. /// @@ -118,7 +119,7 @@ use crate::{ /// impl AnimatableProperty for FieldOfViewProperty { /// type Component = PerspectiveProjection; /// type Property = f32; -/// fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> { +/// fn get_mut<'a>(&self, component: &'a mut Self::Component) -> Option<&'a mut Self::Property> { /// Some(&mut component.fov) /// } /// } @@ -131,46 +132,111 @@ use crate::{ /// # use bevy_reflect::Reflect; /// # use bevy_render::camera::PerspectiveProjection; /// # let animation_target_id = AnimationTargetId::from(&Name::new("Test")); -/// # #[derive(Reflect)] +/// # #[derive(Reflect, Clone)] /// # struct FieldOfViewProperty; /// # impl AnimatableProperty for FieldOfViewProperty { /// # type Component = PerspectiveProjection; /// # type Property = f32; -/// # fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> { +/// # fn get_mut<'a>(&self, component: &'a mut Self::Component) -> Option<&'a mut Self::Property> { /// # Some(&mut component.fov) /// # } /// # } /// let mut animation_clip = AnimationClip::default(); /// animation_clip.add_curve_to_target( /// animation_target_id, -/// AnimatableKeyframeCurve::new( -/// [ +/// AnimatableCurve::new( +/// FieldOfViewProperty, +/// AnimatableKeyframeCurve::new([ /// (0.0, core::f32::consts::PI / 4.0), /// (1.0, core::f32::consts::PI / 3.0), -/// ] +/// ]).expect("Failed to create font size curve") /// ) -/// .map(AnimatableCurve::::from_curve) -/// .expect("Failed to create font size curve") /// ); /// /// Here, the use of [`AnimatableKeyframeCurve`] creates a curve out of the given keyframe time-value /// pairs, using the [`Animatable`] implementation of `f32` to interpolate between them. The -/// invocation of [`AnimatableCurve::from_curve`] with `FieldOfViewProperty` indicates that the `f32` +/// invocation of [`AnimatableCurve::new`] with `FieldOfViewProperty` indicates that the `f32` /// output from that curve is to be used to animate the font size of a `PerspectiveProjection` component (as /// configured above). /// /// [`AnimationClip`]: crate::AnimationClip -pub trait AnimatableProperty: Reflect + TypePath { +pub trait AnimatableProperty { /// The type of the component that the property lives on. type Component: Component; /// The type of the property to be animated. - type Property: Animatable + FromReflect + Reflectable + Clone + Sync + Debug; + type Property: Animatable + Clone + Sync + Debug; /// Given a reference to the component, returns a reference to the property. /// /// If the property couldn't be found, returns `None`. - fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property>; + fn get_mut<'a>(&self, component: &'a mut Self::Component) -> Option<&'a mut Self::Property>; +} + +/// A [`Component`] property that can be animated, defined by a function that reads the component and returns +/// the accessed field / property. +#[derive(Clone)] +pub struct AnimatedProperty &mut P> { + func: F, + marker: PhantomData<(C, P)>, +} + +impl AnimatableProperty for AnimatedProperty +where + C: Component, + P: Animatable + Clone + Sync + Debug, + F: Fn(&mut C) -> &mut P, +{ + type Component = C; + + type Property = P; + + fn get_mut<'a>(&self, component: &'a mut Self::Component) -> Option<&'a mut Self::Property> { + Some((self.func)(component)) + } +} + +impl &mut P + 'static> AnimatedProperty { + /// Creates a new instance of [`AnimatedProperty`] + pub fn new(func: F) -> Self { + Self { + func, + marker: PhantomData, + } + } +} + +/// A [`Component`] property that can be animated, defined by a function that reads the component and returns +/// either the accessed field / property, or [`None`] if it does not exist +#[derive(Clone)] +pub struct AnimatedPropertyOptional Option<&mut P>> { + func: F, + marker: PhantomData<(C, P)>, +} + +impl AnimatableProperty for AnimatedPropertyOptional +where + C: Component, + P: Animatable + Clone + Sync + Debug, + F: Fn(&mut C) -> Option<&mut P>, +{ + type Component = C; + + type Property = P; + + fn get_mut<'a>(&self, component: &'a mut Self::Component) -> Option<&'a mut Self::Property> { + (self.func)(component) + } +} + +impl Option<&mut P> + 'static> AnimatedPropertyOptional { + /// Creates a new instance of [`AnimatedPropertyOptional`] + pub fn new(func: F) -> Self { + Self { + func, + marker: PhantomData, + } + } } /// This trait collects the additional requirements on top of [`Curve`] needed for a @@ -187,12 +253,14 @@ impl AnimationCompatibleCurve for C where C: Curve + Debug + Clone + #[derive(Reflect, FromReflect)] #[reflect(from_reflect = false)] pub struct AnimatableCurve { + /// The property selector, which defines what component to access and how to access + /// a property on that component. + pub property: P, + /// The inner [curve] whose values are used to animate the property. /// /// [curve]: Curve pub curve: C, - #[reflect(ignore)] - _phantom: PhantomData

, } /// An [`AnimatableCurveEvaluator`] for [`AnimatableProperty`] instances. @@ -205,8 +273,7 @@ where P: AnimatableProperty, { evaluator: BasicAnimationCurveEvaluator, - #[reflect(ignore)] - phantom: PhantomData

, + property: P, } impl AnimatableCurve @@ -218,22 +285,20 @@ where /// valued in an [animatable property]. /// /// [animatable property]: AnimatableProperty::Property - pub fn from_curve(curve: C) -> Self { - Self { - curve, - _phantom: PhantomData, - } + pub fn new(property: P, curve: C) -> Self { + Self { property, curve } } } impl Clone for AnimatableCurve where C: Clone, + P: Clone, { fn clone(&self) -> Self { Self { curve: self.curve.clone(), - _phantom: PhantomData, + property: self.property.clone(), } } } @@ -249,10 +314,10 @@ where } } -impl AnimationCurve for AnimatableCurve +impl AnimationCurve for AnimatableCurve where - P: AnimatableProperty, - C: AnimationCompatibleCurve, + P: AnimatableProperty + Clone, + C: AnimationCompatibleCurve + Clone, { fn clone_value(&self) -> Box { Box::new(self.clone()) @@ -269,7 +334,7 @@ where fn create_evaluator(&self) -> Box { Box::new(AnimatableCurveEvaluator { evaluator: BasicAnimationCurveEvaluator::default(), - phantom: PhantomData::

, + property: self.property.clone(), }) } @@ -280,7 +345,7 @@ where weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { - let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) + let curve_evaluator = curve_evaluator .downcast_mut::>() .unwrap(); let value = self.curve.sample_clamped(t); @@ -298,7 +363,7 @@ where impl

AnimationCurveEvaluator for AnimatableCurveEvaluator

where - P: AnimatableProperty, + P: AnimatableProperty + Send + Sync + 'static, { fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ false) @@ -324,7 +389,9 @@ where let mut component = entity.get_mut::().ok_or_else(|| { AnimationEvaluationError::ComponentNotPresent(TypeId::of::()) })?; - let property = P::get_mut(&mut component) + let property = self + .property + .get_mut(&mut component) .ok_or_else(|| AnimationEvaluationError::PropertyNotPresent(TypeId::of::

()))?; *property = self .evaluator @@ -382,7 +449,7 @@ where weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { - let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) + let curve_evaluator = curve_evaluator .downcast_mut::() .unwrap(); let value = self.0.sample_clamped(t); @@ -479,7 +546,7 @@ where weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { - let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) + let curve_evaluator = curve_evaluator .downcast_mut::() .unwrap(); let value = self.0.sample_clamped(t); @@ -576,7 +643,7 @@ where weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { - let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) + let curve_evaluator = curve_evaluator .downcast_mut::() .unwrap(); let value = self.0.sample_clamped(t); @@ -704,7 +771,7 @@ where weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { - let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) + let curve_evaluator = curve_evaluator .downcast_mut::() .unwrap(); @@ -968,7 +1035,7 @@ where /// mutated in the implementation of [`apply`]. /// /// [`apply`]: AnimationCurve::apply -pub trait AnimationCurve: Reflect + Debug + Send + Sync { +pub trait AnimationCurve: Debug + Send + Sync + 'static { /// Returns a boxed clone of this value. fn clone_value(&self) -> Box; @@ -1031,7 +1098,7 @@ pub trait AnimationCurve: Reflect + Debug + Send + Sync { /// translation keyframes. The stack stores intermediate values generated while /// evaluating the [`crate::graph::AnimationGraph`], while the blend register /// stores the result of a blend operation. -pub trait AnimationCurveEvaluator: Reflect { +pub trait AnimationCurveEvaluator: Downcast + Send + Sync + 'static { /// Blends the top element of the stack with the blend register. /// /// The semantics of this method are as follows: @@ -1099,6 +1166,8 @@ pub trait AnimationCurveEvaluator: Reflect { ) -> Result<(), AnimationEvaluationError>; } +impl_downcast!(AnimationCurveEvaluator); + /// A [curve] defined by keyframes with values in an [animatable] type. /// /// The keyframes are interpolated using the type's [`Animatable::interpolate`] implementation. diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 9fe1d89f2596b..809b534108c49 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -17,7 +17,7 @@ pub mod transition; mod util; use core::{ - any::{Any, TypeId}, + any::TypeId, cell::RefCell, fmt::Debug, hash::{Hash, Hasher}, @@ -38,12 +38,7 @@ use bevy_ecs::{ world::EntityMutExcept, }; use bevy_math::FloatOrd; -use bevy_reflect::{ - prelude::ReflectDefault, utility::NonGenericTypeInfoCell, ApplyError, DynamicTupleStruct, - FromReflect, FromType, GetTypeRegistration, PartialReflect, Reflect, ReflectFromPtr, - ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TupleStruct, TupleStructFieldIter, - TupleStructInfo, TypeInfo, TypePath, TypeRegistration, Typed, UnnamedField, -}; +use bevy_reflect::{prelude::ReflectDefault, Reflect, TypePath}; use bevy_time::Time; use bevy_transform::{prelude::Transform, TransformSystem}; use bevy_utils::{ @@ -100,175 +95,6 @@ impl VariableCurve { } } -// We have to implement `PartialReflect` manually because of the embedded -// `Box`, which can't be automatically derived yet. -impl PartialReflect for VariableCurve { - #[inline] - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { - Some(::type_info()) - } - - #[inline] - fn into_partial_reflect(self: Box) -> Box { - self - } - - #[inline] - fn as_partial_reflect(&self) -> &dyn PartialReflect { - self - } - - #[inline] - fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect { - self - } - - fn try_into_reflect(self: Box) -> Result, Box> { - Ok(self) - } - - #[inline] - fn try_as_reflect(&self) -> Option<&dyn Reflect> { - Some(self) - } - - #[inline] - fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> { - Some(self) - } - - fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { - if let ReflectRef::TupleStruct(tuple_value) = value.reflect_ref() { - for (i, value) in tuple_value.iter_fields().enumerate() { - if let Some(v) = self.field_mut(i) { - v.try_apply(value)?; - } - } - } else { - return Err(ApplyError::MismatchedKinds { - from_kind: value.reflect_kind(), - to_kind: ReflectKind::TupleStruct, - }); - } - Ok(()) - } - - fn reflect_ref(&self) -> ReflectRef { - ReflectRef::TupleStruct(self) - } - - fn reflect_mut(&mut self) -> ReflectMut { - ReflectMut::TupleStruct(self) - } - - fn reflect_owned(self: Box) -> ReflectOwned { - ReflectOwned::TupleStruct(self) - } - - fn clone_value(&self) -> Box { - Box::new((*self).clone()) - } -} - -// We have to implement `Reflect` manually because of the embedded `Box`, which can't be automatically derived yet. -impl Reflect for VariableCurve { - #[inline] - fn into_any(self: Box) -> Box { - self - } - - #[inline] - fn as_any(&self) -> &dyn Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - #[inline] - fn into_reflect(self: Box) -> Box { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn Reflect { - self - } - - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } -} - -// We have to implement `TupleStruct` manually because of the embedded `Box`, which can't be automatically derived yet. -impl TupleStruct for VariableCurve { - fn field(&self, index: usize) -> Option<&dyn PartialReflect> { - match index { - 0 => Some(self.0.as_partial_reflect()), - _ => None, - } - } - - fn field_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> { - match index { - 0 => Some(self.0.as_partial_reflect_mut()), - _ => None, - } - } - - fn field_len(&self) -> usize { - 1 - } - - fn iter_fields(&self) -> TupleStructFieldIter { - TupleStructFieldIter::new(self) - } - - fn clone_dynamic(&self) -> DynamicTupleStruct { - DynamicTupleStruct::from_iter([PartialReflect::clone_value(&*self.0)]) - } -} - -// We have to implement `FromReflect` manually because of the embedded `Box`, which can't be automatically derived yet. -impl FromReflect for VariableCurve { - fn from_reflect(reflect: &dyn PartialReflect) -> Option { - Some(reflect.try_downcast_ref::()?.clone()) - } -} - -// We have to implement `GetTypeRegistration` manually because of the embedded -// `Box`, which can't be automatically derived yet. -impl GetTypeRegistration for VariableCurve { - fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration - } -} - -// We have to implement `Typed` manually because of the embedded `Box`, which can't be automatically derived yet. -impl Typed for VariableCurve { - fn type_info() -> &'static TypeInfo { - static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); - CELL.get_or_set(|| { - TypeInfo::TupleStruct(TupleStructInfo::new::(&[UnnamedField::new::<()>(0)])) - }) - } -} - /// A list of [`VariableCurve`]s and the [`AnimationTargetId`]s to which they /// apply. /// @@ -276,6 +102,7 @@ impl Typed for VariableCurve { /// [`AnimationTarget`] with that ID. #[derive(Asset, Reflect, Clone, Debug, Default)] pub struct AnimationClip { + #[reflect(ignore)] curves: AnimationCurves, events: AnimationEvents, duration: f32, diff --git a/examples/animation/animated_ui.rs b/examples/animation/animated_ui.rs index e40afed616cdf..53f96029be31c 100644 --- a/examples/animation/animated_ui.rs +++ b/examples/animation/animated_ui.rs @@ -5,18 +5,6 @@ use bevy::{ prelude::*, }; -// A type that represents the font size of the first text section. -// -// We implement `AnimatableProperty` on this. -#[derive(Reflect)] -struct FontSizeProperty; - -// A type that represents the color of the first text section. -// -// We implement `AnimatableProperty` on this. -#[derive(Reflect)] -struct TextColorProperty; - // Holds information about the animation we programmatically create. struct AnimationInfo { // The name of the animation target (in this case, the text). @@ -39,29 +27,6 @@ fn main() { .run(); } -impl AnimatableProperty for FontSizeProperty { - type Component = TextFont; - - type Property = f32; - - fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> { - Some(&mut component.font_size) - } -} - -impl AnimatableProperty for TextColorProperty { - type Component = TextColor; - - type Property = Srgba; - - fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> { - match component.0 { - Color::Srgba(ref mut color) => Some(color), - _ => None, - } - } -} - impl AnimationInfo { // Programmatically creates the UI animation. fn create( @@ -76,35 +41,40 @@ impl AnimationInfo { let mut animation_clip = AnimationClip::default(); // Create a curve that animates font size. - // - // The curve itself is a `Curve`, and `f32` is `FontSizeProperty::Property`, - // which is required by `AnimatableCurve::from_curve`. animation_clip.add_curve_to_target( animation_target_id, - AnimatableKeyframeCurve::new( - [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0] - .into_iter() - .zip([24.0, 80.0, 24.0, 80.0, 24.0, 80.0, 24.0]), - ) - .map(AnimatableCurve::::from_curve) - .expect("should be able to build translation curve because we pass in valid samples"), + AnimatableCurve::new( + AnimatedProperty::new(|font: &mut TextFont| &mut font.font_size), + AnimatableKeyframeCurve::new( + [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0] + .into_iter() + .zip([24.0, 80.0, 24.0, 80.0, 24.0, 80.0, 24.0]), + ) + .expect( + "should be able to build translation curve because we pass in valid samples", + ), + ), ); // Create a curve that animates font color. Note that this should have // the same time duration as the previous curve. - // - // Similar to the above, the curve itself is a `Curve`, and `Srgba` is - // `TextColorProperty::Property`, which is required by the `from_curve` method. animation_clip.add_curve_to_target( animation_target_id, - AnimatableKeyframeCurve::new([0.0, 1.0, 2.0, 3.0].into_iter().zip([ - Srgba::RED, - Srgba::GREEN, - Srgba::BLUE, - Srgba::RED, - ])) - .map(AnimatableCurve::::from_curve) - .expect("should be able to build translation curve because we pass in valid samples"), + AnimatableCurve::new( + AnimatedPropertyOptional::new(|color: &mut TextColor| match &mut color.0 { + Color::Srgba(srgba) => Some(srgba), + _ => None, + }), + AnimatableKeyframeCurve::new([0.0, 1.0, 2.0, 3.0].into_iter().zip([ + Srgba::RED, + Srgba::GREEN, + Srgba::BLUE, + Srgba::RED, + ])) + .expect( + "should be able to build translation curve because we pass in valid samples", + ), + ), ); // Save our animation clip as an asset.