#node #sprite #bevy #animation #plugin #attributes #game-engine

bevy_sprite_animation

基于 Aarthificial 的 Reanimator 的基于节点精灵动画插件。

1 个不稳定版本

0.4.0 2023年7月25日

#1998 in 游戏开发

MIT/Apache

120KB
3K SLoC

bevy_sprite_animation

Bevy 游戏引擎的简单 2D 精灵动画插件。

任何人都可以对这个仓库提出建议和修正,包括但不限于排版和内容!

这基本上是 Aarthificial 的 Reanimator 的一个副本,但当然是为了 Bevy。

这里 是一个解释示例如何工作的视频。 这里 是一个介绍 0.4 版本新功能的视频。

根据反馈可能有所更改

版本

0.3 = Bevy 0.7 可在 0.7 分支中获取
0.3.1 = Bevy 0.8 可在 0.8 分支中获取
0.3.1 = Bevy 0.10 可在 0.10 分支中获取
0.3.2 = Bevy 0.11 可在主分支中获取
0.4 = Bevy 0.11 可在 v0.4 分支中获取

使用方法

AnimationPlugin::<usize> 和其他系统添加到应用程序中

usize 是单个路径可以包含的最大节点数,这是为了防止无限锁定帧。

fn main() {
    App::build()
        .add_plugin(AnimationPlugin::<10>)
        .add_startup_system(add_nodes.system())
        .add_startup_system(add_animator.system())
        .add_system(update_animator)
        .add_system(read_animator)
        // registure any custom nodes so thay can be loaded
        // these nodes should reflect LoadNode inorder to work correctly
        .register_type::<MatchNode<ZState>>()
        .run()
}

将节点添加到 Assets<AnimationNode> 资源中

fn add_nodes(
    asset_server : Res<AssetServer>,
    mut nodes : ResMut<Assets<AnimationNode>>,
) {
    // make some image handles
    let mut handles = Vec::new();
    for i in 0..10 {
        handles.push(asset_server.load(format!("SomeSprite_{}", i));
    }

    // add a node created in this system
    // hardcoded like this
    let node = Box::new(IndexNode::new("New Node", &handles));

    // this will return a handle to the node
    let node_handle = nodes.add_node(node);
    // this converts our handle into a NodeId
    let node_id = NodeId::from_handle(node_handle);

    // this can be used to make common nodes easy to refrence
    // or to make it easy to refrece from a node loaded from a file

    //with a given name
    nodes.set(NodeId::from_name("Node Name"), node);
    //with a given id
    nodes.set(NodeId::from_u64("Node Name"), node);
    
    // load a node from a file
    // this will return a handle to a Refrence Node, this is so the node can have a Handle<AnimationNode> diffrent from its FilePath
    let from_file = asset_server.load("example.node");
    
    // load a node_tree from a file
    // this will return a handle to a Refrence Node, this is also the nodes can have diffrent Handle<AnimationNode> diffrent from its FilePath and so they dont unload if the nodes that my point into the tree where to unload
    node_tree.load("example.nodetree");

    // Refrence Nodes will run the first Node in there list if you call them so it it ok the use them as start nodes, as long as you are using .node or the first node in the file is correct
}

创建一个具有 AnimationStateStartNode 的实体

起始节点用于每帧选择入口点

fn add_animator(
    mut commands: Commands,
) {
    // create a default state
    let mut state = AnimationState::default();
    // set starting Attributes
    start.set_attribute(Attribute::FlipX, true);
    // Attributes data can be any time that derives Reflect
    // Attributes can be made from any time that impls  `Into<Cow<'static, str>>`
    // Attributes made this way will dispay this name when they are debugged
    start.set_attribute(Attribute::new_attribute("custom_attribute"), "cat");
    // there is a more relaxed way to get Attributes that only requies it impl Hash
    // Attributes made this way will **Not** dispay a name when they are debugged
    // Atttibute both methodes of getting an attribute are Eq, if T.into::<Cow>().hash() and T.hash()
    // are also Eq
    start.set_attribute(Attribute::new_attribute_id("specil_attribute"), 5);

    // set temporary attribute
    // these will be removed if they are not changed each frame
    // you can also get index attributes, they follow the same rules but can only be used to get and set usize into the AnimationState
    state.set_temporary(Attribute::new_index("Idel"));

    // remove temporary attribute
    // by default all attributes are persistent
    // Index Attributes do not conflict with Custom Attributes
    // Attribute::new_index("Idel") != Attribute::new_attribute("Idel")
    state.set_persistent(Attribute::new_index_id("Idel"));

    // spwan the entity
    commands.spwan((
        // we need this to see it
        SpriteBundle::default(),
        // the state the nodes can use so multiple entitys can use the same nodes and get diffrent results
        state,
        // the first node the entity should run to work out its final sprite
        // this can be from a u64, anything that impls Cow<'_, str>, or a Handel<AnimationNode>
        StartNode::from_u64(0),
    ))
}

更改 AnimationState 的状态以控制下一帧选择的帧

fn update_animation_state(
    mut animatiors : Query<&mut AnimationState>,
    input : Res<Input<KeyCode>>,
) {
    if input.just_pressed(KeyCode::Space){
    for mut animatior in animatiors.iter(){
      start.set_attribute(Attributes::new_attribute("custom_attribute"), "dog");
    }}
}

AnimationState 获取属性以创建仅在特殊帧上发生的逻辑

fn read_animation_state(
    animatiors : Query<(Entity, &AnimationState)>,
) {
    for (entity, animatior) in animatiors.iter(){
      if let Ok(ground_type) = animatior.get_attribute::<GroundType>(Attributes::new_attribute("step")) {
        println!("{} is on a frame where you should play the sound of someone stepping on {}", entity, ground_type);
      }
    }
}

检查 AnimationState 的属性是否在本帧更改

fn read_animation_change(
    animatiors : Query<(Entity, &AnimationState)>,
    dogs: Query<&mut Dogs>,
) {
    for (entity, animatior) in animatiors.iter(){
        // assuming barke is temporary it will only change when set to true.
        // use `changed` for logic where you dont care what the attribute
        if animatior.changed(Attributes::new_attribute("barke")) {
            println!("{} is on a frame where you should play a barke sound effect", entity);
        }
    }

    for (entity, animatior) in animatiors.iter(){
        if animatior.changed(Attributes::new_attribute("dog_breed")) {
            let dog = dogs.get(animatior.get_attribute::<Entity>(Attributes::new_attribute("dog_breed")));
            // do something to the state based on the dog's breed
            println!("{} is on a frame where you should play a barke sound effect", entity);
        }
    }
}

依赖项

~21–53MB
~860K SLoC