{"id":15017985,"url":"https://github.com/seldom-se/seldom_state","last_synced_at":"2025-05-15T11:00:22.172Z","repository":{"id":60259017,"uuid":"541997680","full_name":"Seldom-SE/seldom_state","owner":"Seldom-SE","description":"Component-based state machine plugin for Bevy. Useful for AI, player state, and other entities that occupy different states.","archived":false,"fork":false,"pushed_at":"2025-05-06T23:15:46.000Z","size":220,"stargazers_count":243,"open_issues_count":5,"forks_count":19,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-06T23:16:22.637Z","etag":null,"topics":["ai","bevy","bevy-engine","bevy-plugin","rust","state-machine"],"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/Seldom-SE.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-09-27T09:12:58.000Z","updated_at":"2025-05-06T23:15:49.000Z","dependencies_parsed_at":"2023-01-22T21:46:01.555Z","dependency_job_id":"f0dcb755-845b-4e74-8c70-537430c9a8ed","html_url":"https://github.com/Seldom-SE/seldom_state","commit_stats":{"total_commits":91,"total_committers":8,"mean_commits":11.375,"dds":0.3626373626373627,"last_synced_commit":"4b2dfb21bfa779eca5e4db1eb37fafa09efc9ca1"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seldom-SE%2Fseldom_state","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seldom-SE%2Fseldom_state/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seldom-SE%2Fseldom_state/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seldom-SE%2Fseldom_state/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Seldom-SE","download_url":"https://codeload.github.com/Seldom-SE/seldom_state/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254328384,"owners_count":22052632,"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":["ai","bevy","bevy-engine","bevy-plugin","rust","state-machine"],"created_at":"2024-09-24T19:51:17.321Z","updated_at":"2025-05-15T11:00:22.160Z","avatar_url":"https://github.com/Seldom-SE.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `seldom_state`\n\n[![Crates.io](https://img.shields.io/crates/v/seldom_state.svg)](https://crates.io/crates/seldom_state)\n[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/Seldom-SE/seldom_state#license)\n[![Crates.io](https://img.shields.io/crates/d/seldom_state.svg)](https://crates.io/crates/seldom_state)\n\n`seldom_state` is a component-based state machine plugin for Bevy. It's useful for AI, player state,\nand other entities that occupy various states. It allows for greater reusability of state logic\nbetween entities, compared to managing mutually-exclusive components directly in your systems.\n\nA *state* is a component attached to an entity that defines its current behavior, such as `Jumping`\nor `Stunned`. A *trigger* is a system that checks information about entities in the world, such as\n`near_position` or `health_below_threshold`. A *transition* links two states: one to transition\nfrom, and one to transition to; once a given trigger has occurred. A *state machine* is a component\nattached to an entity that keeps track of that entity's transitions, and automatically changes the\nentity's state according to those transitions.\n\nState machines are created like so:\n\n```Rust\ncommands.spawn((\n    // ... (other inserts)\n    MyInitialState::new(),\n    StateMachine::default()\n        .trans::\u003cMyInitialState, _\u003e(my_trigger_1, my_state_2)\n        .trans::\u003cAnyState, _\u003e(my_trigger_3, my_state_4)\n        .trans_builder(my_trigger_5, |my_state_6: \u0026MyState6, trigger_data| {\n            make_state_7(my_state_6, trigger_data)\n        })\n        .on_enter::\u003cMyState7\u003e(move |entity| entity.insert(my_bundle.clone()))\n        .on_exit::\u003cMyState7\u003e(|entity| entity.remove::\u003cMyBundle\u003e())\n        // etc.\n));\n```\n\nFor more complete examples, see the `examples` directory. The `chase.rs` example is written like a\nguide, so it is good for learning. If you need help, feel free to ping me on\n[the Bevy Discord server](https://discord.com/invite/bevy) (`@Seldom`)! If anything needs\nimprovement, feel free to submit an issue or pr!\n\n## Features\n\n- State machine component with user-defined states and triggers\n- 30 built-in triggers\n    - `always`: always triggers\n    - `NotTrigger`, `AndTrigger`, and `OrTrigger`: combines triggers with boolean logic\n    - `done`: triggers when the `Done` component is added to the entity\n    - 24 more triggers enabled by the `leafwing_input` feature: `action_data`, `axis_pair`,\n    `axis_pair_length_bounds`, `axis_pair_max_length`, `axis_pair_min_length`,\n    `axis_pair_rotation_bounds`, `axis_pair_unbounded`, `clamped_axis_pair`,\n    `clamped_axis_pair_length_bounds`, `clamped_axis_pair_max_length`,\n    `clamped_axis_pair_min_length`, `clamped_axis_pair_rotation_bounds`,\n    `clamped_axis_pair_unbounded`, `clamped_value`, `clamped_value_max`, `clamped_value_min`,\n    `clamped_value_unbounded`, `just_pressed`, `just_released`, `pressed`, `value`, `value_max`,\n    `value_min`, and `value_unbounded`\n    - `on_event`: triggers when it reads an event of the given type\n    - Bevy's [built-in run conditions](https://docs.rs/bevy/latest/bevy/ecs/schedule/common_conditions/index.html)\n    also work as triggers\n- `AnyState` state, that can be used in type parameters to represent any state\n- `OneOfState` and `NotState` states, which can be used in type parameters to match groups of states\n- Transition builders that allow dataflow from outgoing states and triggers to incoming states\n(`StateMachine::trans_builder`)\n- Automatically perform behavior upon entering or exiting states (`StateMachine::on_enter`,\n`StateMachine::on_exit`, `StateMachine::command_on_enter` and `StateMachine::command_on_exit`)\n\n## Comparison with [`big-brain`](https://github.com/zkat/big-brain)\n\nFinite state machine is an old and well-worn pattern in game AI, so its strengths and limitations\nare known. It is good for entities that:\n\n1. Do not have a huge number of interconnected states, since the number of transitions can grow\nquadratically. Then it becomes easy to forget to add a transition, causing difficult bugs.\n2. Act rigidly, like the enemies in Spelunky, who act according to clear triggers such as\ngot-jumped-on-by-player and waited-for-5-seconds and are predictable to the player, and unlike the\ndwarves in Dwarf Fortress, who weigh their options of what to do before taking an action and feel\nlively.\n\n`seldom_state` is a finite state machine implementation, so it may not be suitable for all types of\ngame AI. If you need a solution that works with more complex states and transitions, then you may\nwant to implement\n[a behavior tree](https://www.gamedeveloper.com/programming/behavior-trees-for-ai-how-they-work) (I\nhad little luck turning existing implementations into a Bevy plugin without forking them). If you\nneed a solution that operates on fuzzy logic, and do not need to define which transitions should be\nallowed, then I recommend `big-brain`. If you need fuzzy logic *and* discrete transitions, you may\nwant to implement a fuzzy state machine. If you need discrete transitions, but not fuzzy logic,\nconsider `seldom_state`!\n\n`seldom_state` is not just an AI crate, though. So, you may want to use `big-brain` for your\nenemies' AI, and `seldom_state` to manage state for your player, and control enemies' animation, or\nsomething.\n\n## Design patterns\n\n`seldom_state` is rather versatile, so some problems may be solved in multiple ways. If you're lost,\nhere is some advice.\n\n### I have an entity whose animations depend on behavior\n\nThere are a few solutions to this. The most straightforward is to add the animations to the entity\nwith `on_enter`. This works for animation systems that rigidly follow behavior, such as the player\ncontroller in a 2D fighter or a basic enemy controller. Of course, this is rigid, and anything that\nthe animations must remember between states must be handled manually. In a platformer like Celeste\nthat has multiple animations for a single state (lets assume it has states `Grounded`, `Airborne`,\n`Dashing`, and `Climbing`), you might manage some animations, like the dashing animation, through\n`on_enter`, and others, like the walk cycle, through systems. Or, you might manage all animations\nthrough systems. This is up to preference.\n\nOn the other hand, if your animations are less constrained to behavior, consider using multiple\nstate machines, as in the next section:\n\n### My entity needs to be in multiple states at once\n\nConsider a 2D platformer, where the player has a sword. The player can run and jump around, and they\ncan swing the sword. So whether you're running, jumping, or dashing, you always swing the sword the\nsame way, independently of movement state. In this case, you might want to have a movement state\nmachine and an attack state machine. Since entities can only have one state machine, spawn another\nentity (as a child, I would suggest) with its own state machine, and capture the original `Entity`\nin closures in `command_on_enter` and `command_on_exit`.\n\nHowever, perhaps your states are not so independent. Maybe attacking while dashing puts the player\nin a `PowerAttack` state, or the attack cooldown doesn't count down while moving. Depending on the\nscale of the dependency, you might want to just have your state machines communicate through\ncommands and observing each other's states, or you might want to combine the state machines,\npermuting relevant states into states like `DashAttack` and `IdleAttackCooldown`.\n\nAlso, consider managing one set of states through a state machine and another through systems.\n\n### I have some other problem that's difficult to express through the `StateMachine` API\n\nRemember that `StateMachine` is component-based, so you can solve some problems normally through\nBevy's ECS. Instead of `on_enter::\u003cMyState\u003e` you can use `Added\u003cMyState\u003e` in a system, and you can\neven change state manually through `remove` and `insert` commands. If you do change state manually,\ncallbacks like `on_enter` will not be called, and you will have to make sure that the state machine\nremains in exactly one state at a time. Else, it will panic.\n\n## Usage\n\nAdd to your `Cargo.toml`\n\n```toml\n# Replace * with your desired version\n\n[dependencies]\nseldom_state = \"*\"\n```\n\nSee the `chase.rs` example for further usage.\n\n## Compatibility\n\n| Bevy | `leafwing-input-manager` | `seldom_state` |\n| ---- | ------------------------ | -------------- |\n| 0.16 | 0.17                     | 0.14           |\n| 0.15 | 0.16                     | 0.12 - 0.13    |\n| 0.14 | 0.14                     | 0.11           |\n| 0.13 | 0.13                     | 0.10           |\n| 0.12 | 0.11                     | 0.8 - 0.9      |\n| 0.11 | 0.10                     | 0.7            |\n| 0.10 | 0.9                      | 0.5 - 0.6      |\n| 0.9  | 0.8                      | 0.4            |\n| 0.9  |                          | 0.3            |\n| 0.8  |                          | 0.1 - 0.2      |\n\n## License\n\n`seldom_state` is dual-licensed under MIT and Apache 2.0 at your option.\n\n## Contributing\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the\nwork by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any\nadditional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseldom-se%2Fseldom_state","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseldom-se%2Fseldom_state","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseldom-se%2Fseldom_state/lists"}