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 François Mockers
parent 4bc4f93f5b
commit 9a3baa7dc0

View File

@ -105,38 +105,43 @@ use bevy_platform_support::hash::Hashed;
use bevy_reflect::{FromReflect, Reflect, Reflectable, TypeInfo, Typed};
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
/// custom components other than transforms and morph weights. Use that type in
/// conjunction with [`AnimatableCurve`] (and perhaps [`AnimatableKeyframeCurve`]
/// to define the animation itself).
/// For example, in order to animate field of view, you might use:
/// `AnimatableProperty` allows any value contained in an entity to be animated
/// as long as it can be obtained by mutable reference. This makes it more
/// flexible than [`animated_field`].
///
/// [`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_reflect::Reflect;
/// # use bevy_ecs::component::Component;
/// # use std::any::TypeId;
/// # use bevy_render::camera::{Projection, PerspectiveProjection};
/// #[derive(Reflect)]
/// struct FieldOfViewProperty;
/// #[derive(Component)]
/// struct ExampleComponent {
/// power_level: Option<f32>
/// }
///
/// impl AnimatableProperty for FieldOfViewProperty {
/// #[derive(Clone)]
/// struct PowerLevelProperty;
///
/// impl AnimatableProperty for PowerLevelProperty {
/// 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
/// .get_mut::<Projection>()
/// .ok_or(AnimationEvaluationError::ComponentNotPresent(TypeId::of::<
/// Projection,
/// >(
/// )))?
/// .get_mut::<ExampleComponent>()
/// .ok_or(AnimationEvaluationError::ComponentNotPresent(
/// TypeId::of::<ExampleComponent>()
/// ))?
/// .into_inner();
/// match component {
/// Projection::Perspective(perspective) => Ok(&mut perspective.fov),
/// _ => Err(AnimationEvaluationError::PropertyNotPresent(TypeId::of::<
/// PerspectiveProjection,
/// >(
/// ))),
/// }
/// component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(
/// TypeId::of::<Option<f32>>()
/// ))
/// }
///
/// 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_ecs::name::Name;
/// # use bevy_reflect::Reflect;
/// # use bevy_render::camera::{Projection, PerspectiveProjection};
/// # use bevy_ecs::{name::Name, component::Component};
/// # use std::any::TypeId;
/// # let animation_target_id = AnimationTargetId::from(&Name::new("Test"));
/// # #[derive(Reflect, Clone)]
/// # struct FieldOfViewProperty;
/// # impl AnimatableProperty for FieldOfViewProperty {
/// # type Property = f32;
/// # fn get_mut<'a>(&self, entity: &'a mut AnimationEntityMut) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// # let component = entity
/// # .get_mut::<Projection>()
/// # .ok_or(AnimationEvaluationError::ComponentNotPresent(TypeId::of::<
/// # Projection,
/// # >(
/// # )))?
/// # .into_inner();
/// # match component {
/// # Projection::Perspective(perspective) => Ok(&mut perspective.fov),
/// # _ => Err(AnimationEvaluationError::PropertyNotPresent(TypeId::of::<
/// # PerspectiveProjection,
/// # >(
/// # ))),
/// # }
/// # }
/// # fn evaluator_id(&self) -> EvaluatorId {
/// # EvaluatorId::Type(TypeId::of::<Self>())
/// # }
/// # #[derive(Component)]
/// # struct ExampleComponent { power_level: Option<f32> }
/// # #[derive(Clone)]
/// # struct PowerLevelProperty;
/// # impl AnimatableProperty for PowerLevelProperty {
/// # type Property = f32;
/// # fn get_mut<'a>(
/// # &self,
/// # entity: &'a mut AnimationEntityMut
/// # ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// # let component = entity
/// # .get_mut::<ExampleComponent>()
/// # .ok_or(AnimationEvaluationError::ComponentNotPresent(
/// # TypeId::of::<ExampleComponent>()
/// # ))?
/// # .into_inner();
/// # component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(
/// # TypeId::of::<Option<f32>>()
/// # ))
/// # }
/// # fn evaluator_id(&self) -> EvaluatorId {
/// # EvaluatorId::Type(TypeId::of::<Self>())
/// # }
/// # }
/// let mut animation_clip = AnimationClip::default();
/// animation_clip.add_curve_to_target(
/// animation_target_id,
/// 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")
/// )
/// AnimatableCurve::new(
/// PowerLevelProperty,
/// AnimatableKeyframeCurve::new([
/// (0.0, 0.0),
/// (1.0, 9001.0),
/// ]).expect("Failed to create power level 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 {
/// The animated property type.
type Property: Animatable;