From 1a652ae57ec090ac308f035a7b1d6da1e3bc3f3b Mon Sep 17 00:00:00 2001 From: Louis Capitanchik <contact@louiscap.co> Date: Fri, 18 Nov 2022 11:05:05 +0000 Subject: [PATCH] Import from Cryptikkvania --- .gitignore | 2 + Cargo.toml | 8 ++ src/lib.rs | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e0187a8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "micro_bevy_world_utils" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3d9a02f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,247 @@ +/// `micro_bevy_world_utils` packages some common functions used to interrogate the world state from +/// exclusive bevy systems. One frequent use case for these functions is in implementing a monolithic +/// physics collisions processor. +/// +/// ## World Interrogation +/// +/// ## World Mutation +/// +/// - `send_event`: Send an event +/// +/// ## Entity Sorting +/// +/// There are several functions that take the form "get_{specifier}_{specifier}_entities", such as +/// `get_left_right_entities`. These are used to sort two entities according to which specified +/// components they contain, or to fetch the _parent_ of one or both entities matching the criteria. +/// +/// `{specifier}` takes the following form: `[any_](left|right)[[_any]_parent]` +/// +/// - `any_` implies that no components need to be matched for that side of the query to be +/// successful. This would be equivalent to supplying `()` to the version without `any_` +/// - `_parent` implies that the returned entity for that side will, if a match is found, be the +/// _parent_ entity of the matching input entity. +/// +/// **N.B.** The left component will always be queried first +/// +/// ### Examples +/// +/// - `get_left_right_entities`: Match entities to the left and right components +/// - `get_left_any_right_parent`: Match against the left component, and match the parent of the second entity against the right component +/// - `get_left_right_any_parent`: Match entities to the left and right components, but return the _parent_ entity of the right entity +/// +use bevy_ecs::{ + component::Component, + entity::Entity, + event::EventWriter, + query::{With, ReadOnlyWorldQuery}, + system::{Query, SystemState}, + world::World, +}; +use bevy_hierarchy::Parent; + +pub type Left = Entity; +pub type Right = Entity; +pub type SortedEntities = (Left, Right); + +fn inner_get_left_right_entities< + LeftSide: ReadOnlyWorldQuery + 'static, + RightSide: ReadOnlyWorldQuery + 'static, +>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + let mut state = SystemState::<(Query<(), LeftSide>, Query<(), RightSide>)>::new(world); + let (left_query, right_query) = state.get(world); + + if left_query.contains(*first) && right_query.contains(*second) { + Some((*first, *second)) + } else if left_query.contains(*second) && right_query.contains(*first) { + Some((*second, *first)) + } else { + None + } +} + +fn inner_get_left_right_parent_entities< + LeftSide: ReadOnlyWorldQuery + 'static, + RightSide: ReadOnlyWorldQuery + 'static, + ParentSide: ReadOnlyWorldQuery + 'static, +>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + let mut state = SystemState::<( + Query<(), LeftSide>, + Query<&Parent, RightSide>, + Query<(), ParentSide>, + )>::new(world); + let (left_query, right_query, parent_query) = state.get(world); + if left_query.contains(*first) { + if let Ok(parent) = right_query.get(*second) { + let parent = parent.get(); + if parent_query.contains(parent) { + return Some((*first, parent)); + } + } + } else if left_query.contains(*second) { + if let Ok(parent) = right_query.get(*first) { + let parent = parent.get(); + if parent_query.contains(parent) { + return Some((*second, parent)); + } + } + } + + None +} + +fn inner_get_left_parent_right_parent_entities< + LeftSide: ReadOnlyWorldQuery + 'static, + LeftParent: ReadOnlyWorldQuery + 'static, + RightSide: ReadOnlyWorldQuery + 'static, + RightParent: ReadOnlyWorldQuery + 'static, +>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + let mut state = SystemState::<( + Query<&Parent, LeftSide>, + Query<&Parent, RightSide>, + Query<(), LeftParent>, + Query<(), RightParent>, + )>::new(world); + let (left_query, right_query, left_parent_query, right_parent_query) = state.get(world); + if let Ok(left_parent) = left_query.get(*first) { + if left_parent_query.contains(left_parent.get()) { + if let Ok(right_parent) = right_query.get(*second) { + if right_parent_query.contains(right_parent.get()) { + return Some((left_parent.get(), right_parent.get())); + } + } + } + } else if let Ok(left_parent) = left_query.get(*second) { + if left_parent_query.contains(left_parent.get()) { + if let Ok(right_parent) = right_query.get(*first) { + if right_parent_query.contains(right_parent.get()) { + return Some((left_parent.get(), right_parent.get())); + } + } + } + } + + None +} + +pub fn get_left_right_entities<LeftSide: Component, RightSide: Component>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + inner_get_left_right_entities::<With<LeftSide>, With<RightSide>>(world, first, second) +} + +pub fn get_left_right_parent_entities< + LeftSide: Component, + RightSide: Component, + ParentSide: Component, +>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + inner_get_left_right_parent_entities::<With<LeftSide>, With<RightSide>, With<ParentSide>>( + world, first, second, + ) +} + +pub fn get_left_any_right_entities<LeftComponent: Component>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + inner_get_left_right_entities::<With<LeftComponent>, ()>(world, first, second) +} + +pub fn get_left_parent_right_parent_entities< + LeftSide: Component, + LeftParent: Component, + RightSide: Component, + RightParent: Component, +>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + inner_get_left_parent_right_parent_entities::< + With<LeftSide>, + With<LeftParent>, + With<RightSide>, + With<RightParent>, + >(world, first, second) +} + +pub fn get_any_left_parent_any_right_parent_entities< + LeftParent: Component, + RightParent: Component, +>( + world: &mut World, + first: &Entity, + second: &Entity, +) -> Option<SortedEntities> { + inner_get_left_parent_right_parent_entities::<(), With<LeftParent>, (), With<RightParent>>( + world, first, second, + ) +} + +/// Send an event to the appropriate event writer. Writer type is derived from the +/// second parameter passed in, and must be registered with the application before +/// trying to send. +/// +/// ## Example +/// +/// ```rust +/// # use bevy_ecs::world::World; +/// +/// struct MyEventType { +/// message: usize +/// } +/// +/// // Do some app setup +/// +/// use micro_bevy_world_utils::send_event; +/// pub fn my_world_system(world: &mut World) { +/// // Do some processing here +/// send_event(world, MyEventType { message: 1234 }); +/// } +/// ``` +pub fn send_event<Event: Sync + Send + 'static>(world: &mut World, event: Event) { + SystemState::<(EventWriter<Event>)>::new(world) + .get_mut(world) + .send(event); +} + +/// Clone the data from a specific component of the target entity, as long as that entity +/// has that component and the component itself implements `Clone` +/// +/// ## Example +/// +/// ```rust +/// +/// ``` +pub fn clone_entity_component<C: Component + Clone>( + world: &mut World, + entity: &Entity, +) -> Option<C> { + let mut state = SystemState::<Query<&'static C>>::new(world); + let query = state.get(world); + let item = query.get(*entity); + + if let Ok(value) = item { + Some(value.clone()) + } else { + None + } +} -- GitLab