{"id":15137980,"url":"https://github.com/iiyese/aery","last_synced_at":"2025-05-16T01:06:52.682Z","repository":{"id":172898922,"uuid":"629565169","full_name":"iiYese/aery","owner":"iiYese","description":"A plugin that enables a subset of entity relationship features for bevy","archived":false,"fork":false,"pushed_at":"2025-01-26T23:27:48.000Z","size":257,"stargazers_count":158,"open_issues_count":2,"forks_count":9,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-09T12:08:47.855Z","etag":null,"topics":["bevy","ecs","entity-component-system","entity-relationships","game","game-development","relations","rust"],"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/iiYese.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2023-04-18T15:08:19.000Z","updated_at":"2025-03-24T15:53:53.000Z","dependencies_parsed_at":"2023-12-16T23:51:29.006Z","dependency_job_id":"2498716e-512e-4bf7-8e0c-5cea0b3412e4","html_url":"https://github.com/iiYese/aery","commit_stats":{"total_commits":159,"total_committers":5,"mean_commits":31.8,"dds":0.09433962264150941,"last_synced_commit":"4e8867d9735f2cc896ce7b1b0f1af81b09358b7e"},"previous_names":["iiyese/aery"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iiYese%2Faery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iiYese%2Faery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iiYese%2Faery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iiYese%2Faery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iiYese","download_url":"https://codeload.github.com/iiYese/aery/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254448579,"owners_count":22072764,"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","ecs","entity-component-system","entity-relationships","game","game-development","relations","rust"],"created_at":"2024-09-26T07:04:05.917Z","updated_at":"2025-05-16T01:06:47.659Z","avatar_url":"https://github.com/iiYese.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Status\nLooking for maintainer\n\n## Aery\nA plugin that adds a subset of Entity Relationship features to Bevy.\n\n[![Crates.io](https://img.shields.io/crates/v/aery)](https://crates.io/crates/aery)\n[![Docs.rs](https://img.shields.io/docsrs/aery)](https://docs.rs/aery/latest/aery/)\n\n### Currently supported:\n- ZST edge types only (simply means edges can't hold data)\n- Fragmenting on edge types\n- Cleanup policies\n- Declarative APIs for:\n  - Joining\n  - Traversing\n  - Spawning\n\n# API tour:\nNon exhaustive. Covers most common parts. It's modeling RPG mechanics resembling tears of the\nkingdom (please nintendo leave me alone I beg).\n\n\u003cdetails\u003e\n\u003csummary\u003eBoilerplate\u003c/summary\u003e\n\n\n```rust\nuse bevy::prelude::*;\nuse aery::prelude::*;\n\n#[derive(Clone, Copy, Component)]\nstruct Pos(Vec3);\n\n#[derive(Component)]\nstruct Character;\n\n#[derive(Component)]\nstruct Weapon {\n    uses: u32,\n    strength: u32,\n}\n\n#[derive(Component)]\nstruct Stick;\n\n#[derive(Clone, Copy)]\nenum Climate {\n    Freezing,\n    Cold,\n    Neutral,\n    Hot,\n    Blazing,\n}\n\n#[derive(Resource)]\nstruct ClimateMap {\n    // ..\n}\n\nimpl ClimateMap {\n    fn climate_at(\u0026self, pos: Pos) -\u003e Climate {\n        todo!()\n    }\n}\n\n#[derive(Component)]\nenum Food {\n    Raw { freshness: f32 },\n    Cooked,\n    Spoiled,\n}\n\nimpl Food {\n    fn tick(\u0026mut self, climate: Climate) {\n        let Food::Raw { freshness } = self else { return };\n\n        if *freshness \u003c 0. {\n            *self = Food::Spoiled;\n            return\n        }\n\n        match climate {\n            Climate::Neutral =\u003e *freshness -= 1.,       // spoils over time\n            Climate::Cold =\u003e *freshness -= 0.1,         // spoils slowly\n            Climate::Freezing =\u003e *freshness -= 0.01,    // spoils very slowly\n            Climate::Hot =\u003e *freshness -= 5.,           // spoils quickly\n            Climate::Blazing =\u003e *self = Food::Cooked,   // Cooks food (should add a timer)\n        }\n    }\n}\n\n#[derive(Component)]\nstruct Apple;\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eModeling a player inventory (Making relations)\u003c/summary\u003e\n\n\n```rust\n#[derive(Relation)]\nstruct Inventory;\n\nfn setup(mut cmds: Commands) {\n    // Spawn character with some starting items.\n    cmds.spawn((Character, Pos(Vec3::default())))\n        .scope::\u003cInventory\u003e(|invt| {\n            // Give them a starting weapon \u0026 3 food items\n            invt.add((Weapon { uses: 32, strength: 4 }, Stick))\n                .add((Food::Raw { freshness: 128. }, Apple))\n                .add((Food::Raw { freshness: 128. }, Apple))\n                .add((Food::Raw { freshness: 128. }, Apple));\n        });\n\n    // Alternatively construct relatiosn manually.\n    // This might be more appropriate for changing an inventory or making more complex graphs.\n    let char = cmds.spawn((Character, Pos(Vec3::default()))).id();\n    cmds.spawn((Weapon { uses: 32, strength: 4, }, Stick)).set::\u003cInventory\u003e(char);\n    cmds.spawn((Food::Raw { freshness: 128. }, Apple)).set::\u003cInventory\u003e(char);\n    cmds.spawn((Food::Raw { freshness: 128. }, Apple)).set::\u003cInventory\u003e(char);\n    cmds.spawn((Food::Raw { freshness: 128. }, Apple)).set::\u003cInventory\u003e(char);\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eMaking items respond to enviornment (Join operations)\u003c/summary\u003e\n\n\n```rust\nfn tick_food(\n    mut characters: Query\u003c((\u0026Character, \u0026Pos), Relations\u003cInventory\u003e)\u003e,\n    mut inventory_food: Query\u003c\u0026mut Food, Without\u003cPos\u003e\u003e,\n    mut food: Query\u003c(\u0026mut Food, \u0026Pos)\u003e,\n    climate_map: Res\u003cClimateMap\u003e,\n) {\n    // Tick foods that are just in the world somewhere\n    for (mut food, pos) in food.iter_mut() {\n        food.tick(climate_map.climate_at(*pos));\n    }\n\n    // Tick foods that are in a character's inventory based on the character's position\n    for ((_, pos), edges) in characters.iter() {\n        let climate = climate_map.climate_at(*pos);\n        edges.join::\u003cInventory\u003e(\u0026mut inventory_food).for_each(|mut food| {\n            food.tick(climate);\n        });\n    }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDropping inventory items into the world (Responding to relation changes)\u003c/summary\u003e\n\n\n```rust\nfn drop_item_from_inventory(\n    trigger: Trigger\u003cUnsetEvent\u003cInventory\u003e\u003e,\n    mut commands: Commands,\n    characters: Query\u003c\u0026Pos, With\u003cCharacter\u003e\u003e,\n    food: Query\u003cEntity, With\u003cFood\u003e\u003e,\n) {\n    // Set an items position to the position of the character that last had the item\n    // in their inventory when they drop it.\n    let Ok(pos) = characters.get(trigger.event().target) else { return };\n    commands.entity(trigger.entity()).insert(*pos);\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003ePowering connected devices (Traversing relations \u0026 relation properties)\u003c/summary\u003e\n\n\n```rust\n// This relation has a custom property. Properties can be overriden by supplying arguments to\n// the attribute macro. See the `Relation` trait \u0026 `CleanupPolicy` enum for more details.\n// - Symmetric: Makes relations symmetric. Setting A -R-\u003e B also sets B -R-\u003e A.\n// - Poly: Allows holding multiple relations of that type to different entities.\n//\n// There are also cleanup properties. Only one of these can be supplied to the attribute macro.\n// - Counted: Edge counted cleanup (eg. despawn a parent if all its children are despawned)\n// - Recursive: Recursively cleans up (eg. despawn all children of a parent with the parent)\n// - Total: Does both counted \u0026 recursive cleanup\n#[derive(Relation)]\n#[aery(Symmetric, Poly)]\nstruct FuseJoint;\n\n#[derive(Component)]\nstruct Fan {\n    orientation: Quat\n}\n\n#[derive(Component)]\nstruct Powered;\n\nfn tick_devices(\n    mut devices: Query\u003c((Entity, \u0026mut Pos), Relations\u003cFuseJoint\u003e)\u003e,\n    mut fans: Query\u003c(Entity, \u0026Fan, \u0026mut Pos), With\u003cPowered\u003e\u003e,\n) {\n    for (entity, fan, pos) in fans.iter_mut() {\n        // Move the fan based on its orientation\n        pos = todo!();\n\n        // Track visited nodes because this is a symmetric relationship\n        let mut updated = vec![entity];\n\n        devices.traverse_mut::\u003cFuseJoint\u003e([entity]).for_each(|(entity, ref mut pos), _| {\n            if updated.contains(\u0026entity) {\n                TCF::Close\n            } else {\n                // Move connected device based on fan direction\n                pos = todo!();\n                updated.push(*entity);\n                TCF::Continue\n            }\n        });\n    }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eReflecting relations\u003c/summary\u003e\n\n```rust\nApp::new()\n    // We just need to register the types for relfection.\n    .register_relation::\u003cInventory\u003e()\n    .register_relation::\u003cFuseJoint\u003e()\n    // ..\n    .run();\n```\n\n\u003c/details\u003e\n\n\n### Version table\n| Bevy version | Aery verison |\n|--------------|--------------|\n| 0.15         | 0.8          |\n| 0.14         | 0.7          |\n| 0.13         | 0.6          |\n| 0.12         | 0.5          |\n| 0.11         | 0.3 - 0.4    |\n| 0.10         | 0.1 - 0.2    |\n\n### Credits\n- [Sander Mertens](https://github.com/SanderMertens):\nResponsible for pioneering Entity Relationships in ECS and the author of Flecs which Aery has taken \na lot of inspiration from.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiiyese%2Faery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiiyese%2Faery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiiyese%2Faery/lists"}