bevy/examples/animation
Carter Anderson af10aa38aa
AnimatedField and Rework Evaluators (#16484)
# 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 <alice.i.cecile@gmail.com>
2024-11-27 22:19:55 +00:00
..
animated_fox.rs Make some examples deterministic (#16488) 2024-11-23 18:28:47 +00:00
animated_transform.rs AnimatedField and Rework Evaluators (#16484) 2024-11-27 22:19:55 +00:00
animated_ui.rs AnimatedField and Rework Evaluators (#16484) 2024-11-27 22:19:55 +00:00
animation_events.rs AnimationEvent -> Event and other improvements (#16440) 2024-11-22 00:16:04 +00:00
animation_graph.rs Remove the invalid system ordering in the animation example. (#16173) 2024-10-30 23:44:19 +00:00
animation_masks.rs Incorporate all node weights in additive blending (#16279) 2024-11-07 19:12:08 +00:00
color_animation.rs Use en-us locale for typos (#16037) 2024-10-20 18:55:17 +00:00
custom_skinned_mesh.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
eased_motion.rs AnimatedField and Rework Evaluators (#16484) 2024-11-27 22:19:55 +00:00
easing_functions.rs Use normal constructors for EasingCurve, FunctionCurve, ConstantCurve (#16367) 2024-11-13 15:30:05 +00:00
gltf_skinned_mesh.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
morph_targets.rs Fix println in morph_targets example (#15851) 2024-10-11 15:35:22 +00:00