Skip to content

Commit

Permalink
AnimatedField and Rework Evaluators (#16484)
Browse files Browse the repository at this point in the history
# Objective

Animating component fields requires too much boilerplate at the moment:

```rust
#[derive(Reflect)]
struct FontSizeProperty;

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)
    }
}

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::<FontSizeProperty, _>::from_curve)
    .expect("should be able to build translation curve because we pass in valid samples"),
);
```

## Solution

This adds `AnimatedField` and an `animated_field!` macro, enabling the
following:

```rust
animation_clip.add_curve_to_target(
    animation_target_id,
    AnimatableCurve::new(
        animated_field!(TextFont::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",
        ),
    ),
);
```

This required reworking the internals a bit, namely stripping out a lot
of the `Reflect` usage, as that implementation was fundamentally
incompatible with the `AnimatedField` pattern. `Reflect` was being used
in this context just to downcast traits. But we can get downcasting
behavior without the `Reflect` requirement by implementing `Downcast`
for `AnimationCurveEvaluator`.

This also reworks "evaluator identity" to support either a (Component /
Field) pair, or a TypeId. This allows properties to reuse evaluators,
even if they have different accessor methods. The "contract" here is
that for a given (Component / Field) pair, the accessor will return the
same value. Fields are identified by their Reflect-ed field index. The
(TypeId, usize) is prehashed and cached to optimize for lookup speed.

This removes the built-in hard-coded TranslationCurve / RotationCurve /
ScaleCurve in favor of AnimatableField.

---------

Co-authored-by: Alice Cecile <[email protected]>
  • Loading branch information
cart and alice-i-cecile authored Nov 27, 2024
1 parent a6e1397 commit af10aa3
Show file tree
Hide file tree
Showing 8 changed files with 539 additions and 746 deletions.
1 change: 1 addition & 0 deletions crates/bevy_animation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
601 changes: 216 additions & 385 deletions crates/bevy_animation/src/animation_curves.rs

Large diffs are not rendered by default.

Loading

0 comments on commit af10aa3

Please sign in to comment.