Skip to content
Snippets Groups Projects
lib.rs 8.6 KiB
Newer Older
Louis's avatar
Louis committed
//! `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
//!
Louis's avatar
Louis committed
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
}

Louis's avatar
Louis committed
/// Given two entities and two component types, order those entities based on which entity contains
/// which component.
///
/// The generic parameters `LeftSide` and `RightSide` determine the components being queried for,
/// and will place their matching entity in positions `0` and `1` of the tuple, respectively. If
/// one of both entities do not match with a distinct component type, this function will return `None`; i.e.
/// each entity must match exactly one component, and those matches must be unique.
Louis's avatar
Louis committed
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)
}

Louis's avatar
Louis committed
/// Given two entities and three component types, order those entities based on which entity contains
/// the left hand component, and which entity contains the right hand component while having a parent
/// that contains the right hand component. The resulting tuple will contain the left component and
/// the parent component, but _not_ the right component.
///
/// The generic parameters `LeftSide` and `ParentSide` determine the components being queried for,
/// and will place their matching entity in positions `0` and `1` of the tuple, respectively. `RightSide`
/// will be used to determine which entity should have its parent fetched. If one of both entities do not
/// match with a distinct component type, this function will return `None`; i.e. each entity must match
/// exactly one component, and those matches must be unique.
Louis's avatar
Louis committed
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,
	)
}

Louis's avatar
Louis committed
/// Given two entities and a Component type, order those entities based on which one matches the
/// Component type.
///
/// The first entity to contain `LeftComponent` will be in position `0` of the
/// resulting tuple, and the other will be in position `1`, regardless of whether they both match
/// or just one matches. Returns `None` if neither match
Louis's avatar
Louis committed
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
	}
}