{"id":15137989,"url":"https://github.com/phaestusfox/bevy_sprite_animation","last_synced_at":"2025-10-23T13:31:06.473Z","repository":{"id":49308842,"uuid":"369159558","full_name":"PhaestusFox/bevy_sprite_animation","owner":"PhaestusFox","description":"A simple 2d sprite animation plugin for the bevy game engine","archived":false,"fork":false,"pushed_at":"2023-07-25T00:23:03.000Z","size":217,"stargazers_count":85,"open_issues_count":7,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-30T18:48:26.051Z","etag":null,"topics":["bevy","bevy-engine","bevy-plugin"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PhaestusFox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-20T09:52:50.000Z","updated_at":"2025-01-21T06:51:25.000Z","dependencies_parsed_at":"2023-01-30T17:31:23.248Z","dependency_job_id":null,"html_url":"https://github.com/PhaestusFox/bevy_sprite_animation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhaestusFox%2Fbevy_sprite_animation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhaestusFox%2Fbevy_sprite_animation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhaestusFox%2Fbevy_sprite_animation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhaestusFox%2Fbevy_sprite_animation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PhaestusFox","download_url":"https://codeload.github.com/PhaestusFox/bevy_sprite_animation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237834712,"owners_count":19373779,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bevy","bevy-engine","bevy-plugin"],"created_at":"2024-09-26T07:04:08.958Z","updated_at":"2025-10-23T13:31:01.017Z","avatar_url":"https://github.com/PhaestusFox.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bevy_sprite_animation\n\nA simple 2d sprite animation plugin for the Bevy game engine.\n\nAnyone is welcome to make suggestion and corrections to this repository, typographic and otherwise!\n\nThis is more or less a copy of **[Aarthificial's Reanimator](https://github.com/aarthificial/reanimation)** for Unity but for Bevy of course.\n\n**[Here](https://youtu.be/6fuo8jm7wlM)** is a video explaining how the example works.\n**[Here](https://youtu.be/5UIqGe2P7YU)** is a video going over what is new in 0.4\n\n*subject to change with feedback*\n\n## Version\n0.3 = Bevy 0.7 avalable as 0.7 branch\u003cbr\u003e\n0.3.1 = Bevy 0.8 avalable as 0.8 branch\u003cbr\u003e\n0.3.1 = Bevy 0.10 avalable as 0.10 branch\u003cbr\u003e\n0.3.2 = Bevy 0.11 avalable as master branch\u003cbr\u003e\n0.4 = Bevy 0.11 avalable as v0.4 branch\u003cbr\u003e\n## Usage\n\n### Add `AnimationPlugin::\u003cusize\u003e` and other systems to app\n\nthe usize is the max nodes a single path can take\nthis is to stop loops from locking up frames indefinitly\n\n```rust\nfn main() {\n    App::build()\n        .add_plugin(AnimationPlugin::\u003c10\u003e)\n        .add_startup_system(add_nodes.system())\n        .add_startup_system(add_animator.system())\n        .add_system(update_animator)\n        .add_system(read_animator)\n        // registure any custom nodes so thay can be loaded\n        // these nodes should reflect LoadNode inorder to work correctly\n        .register_type::\u003cMatchNode\u003cZState\u003e\u003e()\n        .run()\n}\n```\n\n### Add nodes to `Assets\u003cAnimationNode\u003e` resurce\n\n```rust\nfn add_nodes(\n    asset_server : Res\u003cAssetServer\u003e,\n    mut nodes : ResMut\u003cAssets\u003cAnimationNode\u003e\u003e,\n) {\n    // make some image handles\n    let mut handles = Vec::new();\n    for i in 0..10 {\n        handles.push(asset_server.load(format!(\"SomeSprite_{}\", i));\n    }\n\n    // add a node created in this system\n    // hardcoded like this\n    let node = Box::new(IndexNode::new(\"New Node\", \u0026handles));\n\n    // this will return a handle to the node\n    let node_handle = nodes.add_node(node);\n    // this converts our handle into a NodeId\n    let node_id = NodeId::from_handle(node_handle);\n\n    // this can be used to make common nodes easy to refrence\n    // or to make it easy to refrece from a node loaded from a file\n\n    //with a given name\n    nodes.set(NodeId::from_name(\"Node Name\"), node);\n    //with a given id\n    nodes.set(NodeId::from_u64(\"Node Name\"), node);\n    \n    // load a node from a file\n    // this will return a handle to a Refrence Node, this is so the node can have a Handle\u003cAnimationNode\u003e diffrent from its FilePath\n    let from_file = asset_server.load(\"example.node\");\n    \n    // load a node_tree from a file\n    // this will return a handle to a Refrence Node, this is also the nodes can have diffrent Handle\u003cAnimationNode\u003e diffrent from its FilePath and so they dont unload if the nodes that my point into the tree where to unload\n    node_tree.load(\"example.nodetree\");\n\n    // 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\n}\n```\n\n### Create an entity with an `AnimationState` and `StartNode`\nthe start node it used to pick the entry point each frame\n\n```rust\nfn add_animator(\n    mut commands: Commands,\n) {\n    // create a default state\n    let mut state = AnimationState::default();\n    // set starting Attributes\n    start.set_attribute(Attribute::FlipX, true);\n    // Attributes data can be any time that derives Reflect\n    // Attributes can be made from any time that impls  `Into\u003cCow\u003c'static, str\u003e\u003e`\n    // Attributes made this way will dispay this name when they are debugged\n    start.set_attribute(Attribute::new_attribute(\"custom_attribute\"), \"cat\");\n    // there is a more relaxed way to get Attributes that only requies it impl Hash\n    // Attributes made this way will **Not** dispay a name when they are debugged\n    // Atttibute both methodes of getting an attribute are Eq, if T.into::\u003cCow\u003e().hash() and T.hash()\n    // are also Eq\n    start.set_attribute(Attribute::new_attribute_id(\"specil_attribute\"), 5);\n\n    // set temporary attribute\n    // these will be removed if they are not changed each frame\n    // you can also get index attributes, they follow the same rules but can only be used to get and set usize into the AnimationState\n    state.set_temporary(Attribute::new_index(\"Idel\"));\n\n    // remove temporary attribute\n    // by default all attributes are persistent\n    // Index Attributes do not conflict with Custom Attributes\n    // Attribute::new_index(\"Idel\") != Attribute::new_attribute(\"Idel\")\n    state.set_persistent(Attribute::new_index_id(\"Idel\"));\n\n    // spwan the entity\n    commands.spwan((\n        // we need this to see it\n        SpriteBundle::default(),\n        // the state the nodes can use so multiple entitys can use the same nodes and get diffrent results\n        state,\n        // the first node the entity should run to work out its final sprite\n        // this can be from a u64, anything that impls Cow\u003c'_, str\u003e, or a Handel\u003cAnimationNode\u003e\n        StartNode::from_u64(0),\n    ))\n}\n```\n\n### Change the state of the `AnimationState` to control what frame is picked next update\n\n```rust\nfn update_animation_state(\n    mut animatiors : Query\u003c\u0026mut AnimationState\u003e,\n    input : Res\u003cInput\u003cKeyCode\u003e\u003e,\n) {\n    if input.just_pressed(KeyCode::Space){\n    for mut animatior in animatiors.iter(){\n      start.set_attribute(Attributes::new_attribute(\"custom_attribute\"), \"dog\");\n    }}\n}\n```\n\n### Get an attribute from an `AnimationState` to create logic that happens only on special frames\n\n```rust\nfn read_animation_state(\n    animatiors : Query\u003c(Entity, \u0026AnimationState)\u003e,\n) {\n    for (entity, animatior) in animatiors.iter(){\n      if let Ok(ground_type) = animatior.get_attribute::\u003cGroundType\u003e(Attributes::new_attribute(\"step\")) {\n        println!(\"{} is on a frame where you should play the sound of someone stepping on {}\", entity, ground_type);\n      }\n    }\n}\n```\n\n### Check if an attribute from `AnimationState` changed this frame\n\n```rust\nfn read_animation_change(\n    animatiors : Query\u003c(Entity, \u0026AnimationState)\u003e,\n    dogs: Query\u003c\u0026mut Dogs\u003e,\n) {\n    for (entity, animatior) in animatiors.iter(){\n        // assuming barke is temporary it will only change when set to true.\n        // use `changed` for logic where you dont care what the attribute\n        if animatior.changed(Attributes::new_attribute(\"barke\")) {\n            println!(\"{} is on a frame where you should play a barke sound effect\", entity);\n        }\n    }\n\n    for (entity, animatior) in animatiors.iter(){\n        if animatior.changed(Attributes::new_attribute(\"dog_breed\")) {\n            let dog = dogs.get(animatior.get_attribute::\u003cEntity\u003e(Attributes::new_attribute(\"dog_breed\")));\n            // do something to the state based on the dog's breed\n            println!(\"{} is on a frame where you should play a barke sound effect\", entity);\n        }\n    }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphaestusfox%2Fbevy_sprite_animation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphaestusfox%2Fbevy_sprite_animation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphaestusfox%2Fbevy_sprite_animation/lists"}