bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.4k stars 3.59k forks source link

`AnimationPlayer` doesn't explain how it is created #14852

Open aecsocket opened 3 months ago

aecsocket commented 3 months ago

How can Bevy's documentation be improved?

bevy_animation::AnimationPlayer doesn't explain:

When I was trying to spawn an entity with animations, I was following the animated fox example, but tried to simplify it. My code looked approximately like:

let mut anim_player = AnimationPlayer::default();
let mut transitions = AnimationTransitions::new();

transitions
    .play(&mut anim_player, idle_animation, Duration::ZERO)
    .repeat();

commands
    .spawn((
        SceneBundle {
            scene: my_entity_gltf_scene.clone(),
            ..default()
        },
        anim_player,
        transitions,
        my_anim_graph.clone(),
    ));

However, this is wrong because here I'm creating my own AnimationPlayer, when I should actually be using the player created by the scene when it's instantiated. What I actually have to do is spawn the SceneBundle in one system, wait for the scene to be instantiated, and use a second system to use the automatically-created AnimationPlayer (I assume because the scene automatically populates it with some important data?). It's not really documented that this is specifically how you're meant to do this.

aevyrie commented 1 week ago

Ran into this as well. Trying to use the animation system to play a GLTF animation is pretty frustrating right now, and requires you to pipe data around without any docs or explanation. There is also no documentation on AnimationPlayer.

mockersf commented 1 week ago

not saying this is friendly or easy to discover, but here's what I do:

#[derive(Component)]
struct Animation {
    animation: AnimationNodeIndex,
    graph: Handle<AnimationGraph>,
}

pub fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut graphs: ResMut<Assets<AnimationGraph>>,
) {
    let (graph, node) = AnimationGraph::from_clip(
        asset_server.load(GltfAssetLabel::Animation(2).from_asset("my_file.gltf")),
    );

    let graph_handle = graphs.add(graph);

    commands
        .spawn((
            SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset("my_file.gltf"))),
            Animation {
                animation: node,
                graph: graph_handle,
            },
        ))
        .observe(play_animation);
}

fn play_animation(
    trigger: Trigger<SceneInstanceReady>,
    children: Query<&Children>,
    animation: Query<&Animation>,
    mut commands: Commands,
    mut players: Query<&mut AnimationPlayer>,
) {
    let entity = children.get(trigger.entity()).unwrap()[0];
    let player_entity = children.get(entity).unwrap()[0];
    let animation = animation.get(trigger.entity()).unwrap();

    let mut player = players.get_mut(entity).unwrap();
    let mut transitions = AnimationTransitions::new();
    transitions
        .play(&mut player, animation.animation, Duration::ZERO)
        .repeat();

    commands
        .entity(player_entity)
        .insert((AnimationGraphHandle(animation.graph.clone()), transitions));
}