Skip to content
Snippets Groups Projects
lib.rs 6.93 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
///
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
	}
}