Update AnimatableProperty documentation, reduce crate dependencies (#18543)

## Objective

- Remove the second to last `bevy_animation` dependency on
`bevy_render`.
- Update some older documentation to reflect later changes to the crate.

## Narrative

I'm trying to make `bevy_animation` independent of `bevy_render`. The
documentation for `bevy_animation::AnimatableProperty` is one of the
last few dependencies. It uses `bevy_render::Projection` to demonstrate
animating an arbitrary value, but I thought that could be easily swapped
for something else.

I then realised that the rest of the documentation was a bit out of
date. Originally `AnimatableProperty` was the only way to animate a
property and so the documentation was quite detailed. But over time the
crate has gained more documentation and other ways to hook up
properties, leaving parts of the docs stale or covered elsewhere. So
I've slimmed down the `AnimatableProperty` docs and added a link to the
main alternative (`animated_field`).

I've probably swung too far towards brevity, so I can build them back up
if preferred. Also the example is kinda contrived and doesn't show the
range of `AnimatableProperty`, like being able to choose different
components. And finally the memes might be a bit stale?

## Showcase


![image](https://github.com/user-attachments/assets/23f1c0bf-10ea-4602-a566-673abe5dace7)

## Testing

```
cargo doc -p bevy_animation --no-deps --all-features
cargo test -p bevy_animation --doc --all-features
```
This commit is contained in:
Greeble 2025-03-26 13:43:32 +00:00 committed by GitHub
parent 5d1fe16bfd
commit a02cdaa017
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -105,38 +105,43 @@ use bevy_platform_support::hash::Hashed;
use bevy_reflect::{FromReflect, Reflect, Reflectable, TypeInfo, Typed}; use bevy_reflect::{FromReflect, Reflect, Reflectable, TypeInfo, Typed};
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
/// A value on a component that Bevy can animate. /// A trait for exposing a value in an entity so that it can be animated.
/// ///
/// You can implement this trait on a unit struct in order to support animating /// `AnimatableProperty` allows any value contained in an entity to be animated
/// custom components other than transforms and morph weights. Use that type in /// as long as it can be obtained by mutable reference. This makes it more
/// conjunction with [`AnimatableCurve`] (and perhaps [`AnimatableKeyframeCurve`] /// flexible than [`animated_field`].
/// to define the animation itself). ///
/// For example, in order to animate field of view, you might use: /// [`animated_field`]: crate::animated_field
///
/// Here, `AnimatableProperty` is used to animate a value inside an `Option`,
/// returning an error if the option is `None`.
/// ///
/// # use bevy_animation::{prelude::AnimatableProperty, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId}; /// # use bevy_animation::{prelude::AnimatableProperty, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId};
/// # use bevy_reflect::Reflect; /// # use bevy_ecs::component::Component;
/// # use std::any::TypeId; /// # use std::any::TypeId;
/// # use bevy_render::camera::{Projection, PerspectiveProjection}; /// #[derive(Component)]
/// #[derive(Reflect)] /// struct ExampleComponent {
/// struct FieldOfViewProperty; /// power_level: Option<f32>
/// }
/// ///
/// impl AnimatableProperty for FieldOfViewProperty { /// #[derive(Clone)]
/// struct PowerLevelProperty;
///
/// impl AnimatableProperty for PowerLevelProperty {
/// type Property = f32; /// type Property = f32;
/// fn get_mut<'a>(&self, entity: &'a mut AnimationEntityMut) -> Result<&'a mut Self::Property, AnimationEvaluationError> { /// fn get_mut<'a>(
/// &self,
/// entity: &'a mut AnimationEntityMut
/// ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// let component = entity /// let component = entity
/// .get_mut::<Projection>() /// .get_mut::<ExampleComponent>()
/// .ok_or(AnimationEvaluationError::ComponentNotPresent(TypeId::of::< /// .ok_or(AnimationEvaluationError::ComponentNotPresent(
/// Projection, /// TypeId::of::<ExampleComponent>()
/// >( /// ))?
/// )))?
/// .into_inner(); /// .into_inner();
/// match component { /// component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(
/// Projection::Perspective(perspective) => Ok(&mut perspective.fov), /// TypeId::of::<Option<f32>>()
/// _ => Err(AnimationEvaluationError::PropertyNotPresent(TypeId::of::< /// ))
/// PerspectiveProjection,
/// >(
/// ))),
/// }
/// } /// }
/// ///
/// fn evaluator_id(&self) -> EvaluatorId { /// fn evaluator_id(&self) -> EvaluatorId {
@ -144,58 +149,44 @@ use downcast_rs::{impl_downcast, Downcast};
/// } /// }
/// } /// }
/// ///
/// You can then create an [`AnimationClip`] to animate this property like so:
/// ///
/// # use bevy_animation::{AnimationClip, AnimationTargetId, VariableCurve, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId}; /// You can then create an [`AnimatableCurve`] to animate this property like so:
///
/// # use bevy_animation::{VariableCurve, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId};
/// # use bevy_animation::prelude::{AnimatableProperty, AnimatableKeyframeCurve, AnimatableCurve}; /// # use bevy_animation::prelude::{AnimatableProperty, AnimatableKeyframeCurve, AnimatableCurve};
/// # use bevy_ecs::name::Name; /// # use bevy_ecs::{name::Name, component::Component};
/// # use bevy_reflect::Reflect;
/// # use bevy_render::camera::{Projection, PerspectiveProjection};
/// # use std::any::TypeId; /// # use std::any::TypeId;
/// # let animation_target_id = AnimationTargetId::from(&Name::new("Test")); /// # #[derive(Component)]
/// # #[derive(Reflect, Clone)] /// # struct ExampleComponent { power_level: Option<f32> }
/// # struct FieldOfViewProperty; /// # #[derive(Clone)]
/// # impl AnimatableProperty for FieldOfViewProperty { /// # struct PowerLevelProperty;
/// # type Property = f32; /// # impl AnimatableProperty for PowerLevelProperty {
/// # fn get_mut<'a>(&self, entity: &'a mut AnimationEntityMut) -> Result<&'a mut Self::Property, AnimationEvaluationError> { /// # type Property = f32;
/// # let component = entity /// # fn get_mut<'a>(
/// # .get_mut::<Projection>() /// # &self,
/// # .ok_or(AnimationEvaluationError::ComponentNotPresent(TypeId::of::< /// # entity: &'a mut AnimationEntityMut
/// # Projection, /// # ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// # >( /// # let component = entity
/// # )))? /// # .get_mut::<ExampleComponent>()
/// # .into_inner(); /// # .ok_or(AnimationEvaluationError::ComponentNotPresent(
/// # match component { /// # TypeId::of::<ExampleComponent>()
/// # Projection::Perspective(perspective) => Ok(&mut perspective.fov), /// # ))?
/// # _ => Err(AnimationEvaluationError::PropertyNotPresent(TypeId::of::< /// # .into_inner();
/// # PerspectiveProjection, /// # component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(
/// # >( /// # TypeId::of::<Option<f32>>()
/// # ))), /// # ))
/// # } /// # }
/// # } /// # fn evaluator_id(&self) -> EvaluatorId {
/// # fn evaluator_id(&self) -> EvaluatorId { /// # EvaluatorId::Type(TypeId::of::<Self>())
/// # EvaluatorId::Type(TypeId::of::<Self>()) /// # }
/// # }
/// # } /// # }
/// let mut animation_clip = AnimationClip::default(); /// AnimatableCurve::new(
/// animation_clip.add_curve_to_target( /// PowerLevelProperty,
/// animation_target_id, /// AnimatableKeyframeCurve::new([
/// AnimatableCurve::new( /// (0.0, 0.0),
/// FieldOfViewProperty, /// (1.0, 9001.0),
/// AnimatableKeyframeCurve::new([ /// ]).expect("Failed to create power level curve")
/// (0.0, core::f32::consts::PI / 4.0),
/// (1.0, core::f32::consts::PI / 3.0),
/// ]).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::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: Send + Sync + 'static { pub trait AnimatableProperty: Send + Sync + 'static {
/// The animated property type. /// The animated property type.
type Property: Animatable; type Property: Animatable;