diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4fffb2f89cbd8f2169ce9914bd16bd43785bb368 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e0187a8d049e205b1a85d8b1e0f99b5386740059 --- /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 0000000000000000000000000000000000000000..3d9a02ff87c0b917eef33d2e43eaca5f690f7886 --- /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 + } +}