diff --git a/framework_crates/bones_bevy_renderer/src/input.rs b/framework_crates/bones_bevy_renderer/src/input.rs index 544560d2d1..9ee889c5ab 100644 --- a/framework_crates/bones_bevy_renderer/src/input.rs +++ b/framework_crates/bones_bevy_renderer/src/input.rs @@ -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<( @@ -94,3 +98,32 @@ pub fn get_bones_input( }, ) } + +pub fn insert_mouse_position( + In((screen_pos, world_pos)): In<(Option, Option)>, + mut game: ResMut, +) { + 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>, + q_camera: Query<(&Camera, &GlobalTransform)>, +) -> (Option, Option) { + 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) + } + }, + } +} diff --git a/framework_crates/bones_bevy_renderer/src/lib.rs b/framework_crates/bones_bevy_renderer/src/lib.rs index 403999809c..c43d5fa6de 100644 --- a/framework_crates/bones_bevy_renderer/src/lib.rs +++ b/framework_crates/bones_bevy_renderer/src/lib.rs @@ -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() diff --git a/framework_crates/bones_ecs/src/bitset.rs b/framework_crates/bones_ecs/src/bitset.rs index ee07cbbd82..92503a5806 100644 --- a/framework_crates/bones_ecs/src/bitset.rs +++ b/framework_crates/bones_ecs/src/bitset.rs @@ -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. diff --git a/framework_crates/bones_ecs/src/entities.rs b/framework_crates/bones_ecs/src/entities.rs index 53eb3ff688..b579057522 100644 --- a/framework_crates/bones_ecs/src/entities.rs +++ b/framework_crates/bones_ecs/src/entities.rs @@ -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() { @@ -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 = ∁ @@ -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 = ∁ let mut bitset = entities.bitset().clone(); @@ -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()); @@ -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(); @@ -892,7 +892,7 @@ mod tests { let state = AtomicCell::new(ComponentStore::::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({ @@ -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::::default()); state.borrow_mut().insert(e, a.clone()); @@ -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()); } @@ -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()); @@ -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(); @@ -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(); @@ -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()); diff --git a/framework_crates/bones_ecs/src/world.rs b/framework_crates/bones_ecs/src/world.rs index 566cebc44f..7c3a820ed3 100644 --- a/framework_crates/bones_ecs/src/world.rs +++ b/framework_crates/bones_ecs/src/world.rs @@ -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::().unwrap(); + let mut entities = self.resource_mut::(); for components in self.components.components.read_only_view().values() { let mut components = components.borrow_mut(); let killed = entities.killed(); @@ -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( + &self, + entity: Entity, + query: Q, + ) -> ::Item { + self.get_entity_components(entity, query).unwrap() + } + + /// Get an entity's components. + pub fn get_entity_components( + &self, + entity: Entity, + query: Q, + ) -> Option<::Item> { + let mut bitset = BitSetVec::default(); + if self.resource::().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(&mut self) -> RefMut { if unlikely(!self.resources.contains::()) { @@ -115,7 +151,7 @@ impl World { match self.resources.get::() { 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::() ), @@ -130,7 +166,7 @@ impl World { match self.resources.get_mut::() { 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::() ), @@ -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); @@ -323,10 +363,28 @@ mod tests { #[test] fn world_is_send() { + fn send(_: T) {} send(World::new()) } - fn send(_: T) {} + #[test] + fn get_entity_components() { + let w = World::default(); + + let (e1, e2) = { + let mut entities = w.resource_mut::(); + (entities.create(), entities.create()) + }; + + let state = w.components.get::(); + 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 diff --git a/framework_crates/bones_framework/src/input/mouse.rs b/framework_crates/bones_framework/src/input/mouse.rs index f1e1e647d0..76c99e9c6b 100644 --- a/framework_crates/bones_framework/src/input/mouse.rs +++ b/framework_crates/bones_framework/src/input/mouse.rs @@ -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); + +/// 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);