Skip to content

Commit

Permalink
feat: add more world utilities (#465)
Browse files Browse the repository at this point in the history
Closes #288

## Changes

- Add resources `MouseScreenPosition` and `MouseWorldPosition` which
both contain an `Option<Vec2>`
- Add utility methods `World::{get_,}entity_components` to query for the
components of an entity
- Clean up noisy data in `Entities` and `QueryItem` tests
  • Loading branch information
nelson137 authored Sep 16, 2024
1 parent 97478b4 commit 833e2cb
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 30 deletions.
41 changes: 37 additions & 4 deletions framework_crates/bones_bevy_renderer/src/input.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use super::*;
use bevy::input::{
gamepad::GamepadEvent,
keyboard::KeyboardInput,
mouse::{MouseButtonInput, MouseMotion, MouseWheel},
use bevy::{
input::{
gamepad::GamepadEvent,
keyboard::KeyboardInput,
mouse::{MouseButtonInput, MouseMotion, MouseWheel},
},
window::PrimaryWindow,
};
use bones::{MouseScreenPosition, MouseWorldPosition};

pub fn insert_bones_input(
In((mouse_inputs, keyboard_inputs, gamepad_inputs)): In<(
Expand Down Expand Up @@ -94,3 +98,32 @@ pub fn get_bones_input(
},
)
}

pub fn insert_mouse_position(
In((screen_pos, world_pos)): In<(Option<Vec2>, Option<Vec2>)>,
mut game: ResMut<BonesGame>,
) {
game.insert_shared_resource(MouseScreenPosition(screen_pos));
game.insert_shared_resource(MouseWorldPosition(world_pos));
}

// Source: https://bevy-cheatbook.github.io/cookbook/cursor2world.html
pub fn get_mouse_position(
mut q_primary_windows: Query<&Window, With<PrimaryWindow>>,
q_camera: Query<(&Camera, &GlobalTransform)>,
) -> (Option<Vec2>, Option<Vec2>) {
match q_primary_windows
.get_single_mut()
.ok()
.and_then(Window::cursor_position)
{
None => (None, None),
screen_pos @ Some(sp) => match q_camera.get_single() {
Err(_) => (screen_pos, None),
Ok((camera, camera_transform)) => {
let world_pos = camera.viewport_to_world_2d(camera_transform, sp);
(screen_pos, world_pos)
}
},
}
}
3 changes: 3 additions & 0 deletions framework_crates/bones_bevy_renderer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ impl BonesBevyRenderer {
(
setup_egui,
get_bones_input.pipe(insert_bones_input).after(InputSystem),
get_mouse_position
.pipe(insert_mouse_position)
.after(InputSystem),
egui_input_hook,
)
.chain()
Expand Down
6 changes: 6 additions & 0 deletions framework_crates/bones_ecs/src/bitset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ impl BitSetVec {
pub fn contains(&self, entity: Entity) -> bool {
self.bit_test(entity.index() as usize)
}

/// Set an entity on the the bitset.
#[inline]
pub fn set(&mut self, entity: Entity) {
self.bit_set(entity.index() as usize);
}
}

/// Creates a bitset big enough to contain the index of each entity.
Expand Down
44 changes: 22 additions & 22 deletions framework_crates/bones_ecs/src/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,11 +767,11 @@ mod tests {

#[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)]
#[repr(C)]
struct A(String);
struct A(u32);

#[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)]
#[repr(C)]
struct B(String);
struct B(u32);

#[test]
fn query_item__get_single_with_one_required() {
Expand All @@ -794,7 +794,7 @@ mod tests {
let mut comp = state.borrow_mut();

let e = entities.create();
let a = A("e".to_string());
let a = A(1);
comp.insert(e, a.clone());

let query = &comp;
Expand All @@ -812,10 +812,10 @@ mod tests {
let mut comp = state.borrow_mut();

let e1 = entities.create();
comp.insert(e1, A("e1".to_string()));
comp.insert(e1, A(1));

let e2 = entities.create();
comp.insert(e2, A("e2".to_string()));
comp.insert(e2, A(2));

let query = &comp;
let mut bitset = entities.bitset().clone();
Expand Down Expand Up @@ -848,8 +848,8 @@ mod tests {

{
let e = entities.create();
let a = A("e".to_string());
let b = B("e".to_string());
let a = A(1);
let b = B(1);
state_a.borrow_mut().insert(e, a.clone());
state_b.borrow_mut().insert(e, b.clone());

Expand All @@ -866,12 +866,12 @@ mod tests {

{
let e1 = entities.create();
state_a.borrow_mut().insert(e1, A("e1".to_string()));
state_b.borrow_mut().insert(e1, B("e1".to_string()));
state_a.borrow_mut().insert(e1, A(1));
state_b.borrow_mut().insert(e1, B(1));

let e2 = entities.create();
state_a.borrow_mut().insert(e2, A("e2".to_string()));
state_b.borrow_mut().insert(e2, B("e2".to_string()));
state_a.borrow_mut().insert(e2, A(2));
state_b.borrow_mut().insert(e2, B(2));

let query = (&state_a.borrow(), &state_b.borrow());
let mut bitset = entities.bitset().clone();
Expand All @@ -892,7 +892,7 @@ mod tests {
let state = AtomicCell::new(ComponentStore::<A>::default());

let e = entities.create();
state.borrow_mut().insert(e, A("unexpected".to_string()));
state.borrow_mut().insert(e, A(u32::MAX));

let query = &state.borrow();
let bitset = Rc::new({
Expand Down Expand Up @@ -1033,7 +1033,7 @@ mod tests {
let mut entities = Entities::default();
(0..3).map(|_| entities.create()).count();
let e = entities.create();
let a = A("a".to_string());
let a = A(4);

let state = AtomicCell::new(ComponentStore::<A>::default());
state.borrow_mut().insert(e, a.clone());
Expand Down Expand Up @@ -1064,7 +1064,7 @@ mod tests {

for i in 0..3 {
let e = entities.create();
let a = A(i.to_string());
let a = A(i);
state.borrow_mut().insert(e, a.clone());
}

Expand All @@ -1086,14 +1086,14 @@ mod tests {
let _e1 = entities.create();

let e2 = entities.create();
state_a.borrow_mut().insert(e2, A("a2".to_string()));
state_a.borrow_mut().insert(e2, A(2));

let e3 = entities.create();
state_b.borrow_mut().insert(e3, B("b3".to_string()));
state_b.borrow_mut().insert(e3, B(3));

let e4 = entities.create();
let a4 = A("a4".to_string());
let b4 = B("b4".to_string());
let a4 = A(4);
let b4 = B(4);
state_a.borrow_mut().insert(e4, a4.clone());
state_b.borrow_mut().insert(e4, b4.clone());

Expand Down Expand Up @@ -1127,7 +1127,7 @@ mod tests {

{
let e = entities.create();
let mut a = A("a".to_string());
let mut a = A(1);
state.borrow_mut().insert(e, a.clone());

let mut comp = state.borrow_mut();
Expand All @@ -1154,7 +1154,7 @@ mod tests {

{
let e = entities.create();
let a = A("a".to_string());
let a = A(1);
state_a.borrow_mut().insert(e, a.clone());

let comp_a = state_a.borrow();
Expand All @@ -1175,8 +1175,8 @@ mod tests {

{
let e = entities.create();
let a = A("a".to_string());
let mut b = B("b".to_string());
let a = A(1);
let mut b = B(1);
state_a.borrow_mut().insert(e, a.clone());
state_b.borrow_mut().insert(e, b.clone());

Expand Down
66 changes: 62 additions & 4 deletions framework_crates/bones_ecs/src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl World {
/// This will remove the component storage for all killed entities, and allow their slots to be
/// re-used for any new entities.
pub fn maintain(&self) {
let mut entities = self.resources.get_mut::<Entities>().unwrap();
let mut entities = self.resource_mut::<Entities>();
for components in self.components.components.read_only_view().values() {
let mut components = components.borrow_mut();
let killed = entities.killed();
Expand Down Expand Up @@ -93,6 +93,42 @@ impl World {
s.run(self, input)
}

/// Get an entity's components.
///
/// # Panics
///
/// Panics if the entity does not have the required components from the query.
pub fn entity_components<Q: QueryItem>(
&self,
entity: Entity,
query: Q,
) -> <Q::Iter as Iterator>::Item {
self.get_entity_components(entity, query).unwrap()
}

/// Get an entity's components.
pub fn get_entity_components<Q: QueryItem>(
&self,
entity: Entity,
query: Q,
) -> Option<<Q::Iter as Iterator>::Item> {
let mut bitset = BitSetVec::default();
if self.resource::<Entities>().bitset().contains(entity) {
bitset.set(entity);
}
query.apply_bitset(&mut bitset);
match query.get_single_with_bitset(bitset.into()) {
Ok(components) => Some(components),
Err(QuerySingleError::NoEntities) => None,
Err(QuerySingleError::MultipleEntities) => {
panic!(
"Query returned a MultipleEntities error for a bitset that \
contains at most one enabled bit"
)
}
}
}

/// Initialize a resource of type `T` by inserting it's default value.
pub fn init_resource<R: HasSchema + FromWorld>(&mut self) -> RefMut<R> {
if unlikely(!self.resources.contains::<R>()) {
Expand All @@ -115,7 +151,7 @@ impl World {
match self.resources.get::<T>() {
Some(r) => r,
None => panic!(
"Requested resource {} does not exist in the `World`.
"Requested resource {} does not exist in the `World`. \
Did you forget to add it using `world.insert_resource` / `world.init_resource`?",
std::any::type_name::<T>()
),
Expand All @@ -130,7 +166,7 @@ impl World {
match self.resources.get_mut::<T>() {
Some(r) => r,
None => panic!(
"Requested resource {} does not exist in the `World`.
"Requested resource {} does not exist in the `World`. \
Did you forget to add it using `world.insert_resource` / `world.init_resource`?",
std::any::type_name::<T>()
),
Expand Down Expand Up @@ -201,6 +237,10 @@ mod tests {

use super::FromWorld;

#[derive(Clone, Copy, HasSchema, Debug, Eq, PartialEq, Default)]
#[repr(C)]
struct C(u32);

#[derive(Clone, HasSchema, Debug, Eq, PartialEq, Default)]
#[repr(C)]
struct Pos(i32, i32);
Expand Down Expand Up @@ -323,10 +363,28 @@ mod tests {

#[test]
fn world_is_send() {
fn send<T: Send>(_: T) {}
send(World::new())
}

fn send<T: Send>(_: T) {}
#[test]
fn get_entity_components() {
let w = World::default();

let (e1, e2) = {
let mut entities = w.resource_mut::<Entities>();
(entities.create(), entities.create())
};

let state = w.components.get::<C>();
let mut comp = state.borrow_mut();

let c2 = C(2);
comp.insert(e2, c2);

assert_eq!(w.get_entity_components(e1, &comp), None);
assert_eq!(w.get_entity_components(e2, &comp), Some(&c2));
}

// ============
// From World
Expand Down
12 changes: 12 additions & 0 deletions framework_crates/bones_framework/src/input/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,15 @@ pub enum MouseButton {
/// Another mouse button with the associated number.
Other(u16),
}

/// The position of the mouse in screen-space.
///
/// `None` if there is no cursor within the window.
#[derive(HasSchema, Clone, Copy, Debug, Default, PartialEq)]
pub struct MouseScreenPosition(pub Option<Vec2>);

/// The position of the mouse in world-space.
///
/// `None` if there is no cursor within the window.
#[derive(HasSchema, Clone, Copy, Debug, Default, PartialEq)]
pub struct MouseWorldPosition(pub Option<Vec2>);

0 comments on commit 833e2cb

Please sign in to comment.