Skip to content

Update AnimatableProperty documentation, reduce crate dependencies #18543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 26, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 62 additions & 71 deletions crates/bevy_animation/src/animation_curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,97 +105,88 @@ 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 {
/// EvaluatorId::Type(TypeId::of::<Self>())
/// }
/// }
///
/// 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;
Expand Down