Compare commits

...

2 Commits

Author SHA1 Message Date
SeanOMik 9b1cc8c364
game: improve event handling, update input systems to use new event handling
CI / build (push) Failing after 12m37s Details
2024-09-24 20:43:08 -04:00
SeanOMik 9125b91977
ecs: add WorldTick query, implement IntoSystem and FnSystem for funcs with 11 args
accidentially missed the macro call for 11 arguments
2024-09-24 20:30:37 -04:00
14 changed files with 528 additions and 435 deletions

1
Cargo.lock generated
View File

@ -2107,6 +2107,7 @@ dependencies = [
"anyhow", "anyhow",
"async-std", "async-std",
"async-trait", "async-trait",
"atomic_refcell",
"bind_match", "bind_match",
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",

View File

@ -31,6 +31,10 @@ mod optional;
#[allow(unused_imports)] #[allow(unused_imports)]
pub use optional::*; pub use optional::*;
mod world_tick;
#[allow(unused_imports)]
pub use world_tick::*;
pub mod dynamic; pub mod dynamic;
pub mod filter; pub mod filter;

View File

@ -0,0 +1,102 @@
use std::ops::Deref;
use crate::{system::FnArgFetcher, Tick, World};
use super::{Fetch, Query, AsQuery};
/// Fetcher used to fetch the current tick of the world.
pub struct FetchWorldTick {
tick: Tick
}
impl<'a> Fetch<'a> for FetchWorldTick {
type Item = WorldTick;
fn dangling() -> Self {
unreachable!()
}
fn can_visit_item(&mut self, _entity: crate::ArchetypeEntityId) -> bool {
true
}
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
WorldTick(self.tick)
}
}
/// Query used to query the current tick of the world.
#[derive(Clone, Copy)]
pub struct QueryWorldTick;
impl Default for QueryWorldTick {
fn default() -> Self {
Self
}
}
impl Query for QueryWorldTick {
type Item<'a> = WorldTick;
type Fetch<'a> = FetchWorldTick;
const ALWAYS_FETCHES: bool = true;
fn new() -> Self {
QueryWorldTick
}
fn can_visit_archetype(&self, _archetype: &crate::archetype::Archetype) -> bool {
true
}
unsafe fn fetch<'a>(&self, world: &'a World, _archetype: &'a crate::archetype::Archetype, _tick: crate::Tick) -> Self::Fetch<'a> {
FetchWorldTick {
tick: world.current_tick()
}
}
unsafe fn fetch_world<'a>(&self, world: &'a World) -> Option<Self::Fetch<'a>> {
Some(FetchWorldTick {
tick: world.current_tick()
})
}
}
impl AsQuery for QueryWorldTick {
type Query = Self;
}
/// Type that can be used in an fn system for fetching the current world tick.
#[derive(Debug, Clone, Copy)]
pub struct WorldTick(Tick);
impl Deref for WorldTick {
type Target = Tick;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsQuery for WorldTick {
type Query = QueryWorldTick;
}
impl FnArgFetcher for WorldTick {
type State = ();
type Arg<'a, 'state> = WorldTick;
fn create_state(_: std::ptr::NonNull<World>) -> Self::State {
()
}
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: std::ptr::NonNull<World>) -> Self::Arg<'a, 'state> {
let world = world.as_ref();
WorldTick(world.current_tick())
}
fn apply_deferred(_: Self::State, _: std::ptr::NonNull<World>) {
}
}

View File

@ -130,6 +130,7 @@ impl_fn_system_tuple!{ A, B, C, D, E, F2, G }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N }

View File

@ -40,6 +40,7 @@ petgraph = { version = "0.6.5", features = ["matrix_graph"] }
bind_match = "0.1.2" bind_match = "0.1.2"
round_mult = "0.1.3" round_mult = "0.1.3"
fast_poisson = { version = "1.0.0", features = ["single_precision"] } fast_poisson = { version = "1.0.0", features = ["single_precision"] }
atomic_refcell = "0.1.13"
[features] [features]
tracy = ["dep:tracing-tracy"] tracy = ["dep:tracing-tracy"]

137
lyra-game/src/event.rs Normal file
View File

@ -0,0 +1,137 @@
use std::sync::Arc;
use atomic_refcell::AtomicRefCell;
use lyra_ecs::{query::{ResMut, WorldTick}, system::FnArgFetcher, Tick};
pub trait Event: Clone + Send + Sync + 'static {}
impl<T: Clone + Send + Sync + 'static> Event for T {}
pub struct Events<T: Event> {
events: Arc<AtomicRefCell<Vec<T>>>,
last_updated_tick: Option<Tick>,
}
impl<T: Event> Default for Events<T> {
fn default() -> Self {
Self { events: Default::default(), last_updated_tick: None }
}
}
impl<T: Event> Events<T> {
pub fn new() -> Self {
Self::default()
}
pub fn push_event(&mut self, event: T) {
let mut events = self.events.borrow_mut();
events.push(event);
}
pub fn reader(&self) -> EventReader<T> {
EventReader {
events: self.events.clone(),
cursor: 0,
}
}
pub fn writer(&self) -> EventWriter<T> {
EventWriter {
events: self.events.clone(),
}
}
}
pub struct EventReader<T: Event> {
events: Arc<AtomicRefCell<Vec<T>>>,
cursor: usize,
}
impl<T: Event> EventReader<T> {
pub fn read(&mut self) -> Option<atomic_refcell::AtomicRef<T>> {
let events = self.events.borrow();
if self.cursor >= events.len() {
None
} else {
let e = atomic_refcell::AtomicRef::map(events,
|e| e.get(self.cursor).unwrap());
self.cursor += 1;
Some(e)
}
}
}
pub struct EventWriter<T: Event> {
events: Arc<AtomicRefCell<Vec<T>>>,
}
impl<T: Event> EventWriter<T> {
pub fn write(&mut self, event: T) {
let mut events = self.events.borrow_mut();
events.push(event);
}
}
/// Clean events of event type `T` every 2 ticks.
pub fn event_cleaner_system<T>(tick: WorldTick, mut events: ResMut<Events<T>>) -> anyhow::Result<()>
where
T: Event
{
let last_tick = *events.last_updated_tick.unwrap_or(*tick);
let world_tick = **tick;
if last_tick + 2 < world_tick {
events.last_updated_tick = Some(*tick);
let mut events = events.events.borrow_mut();
events.clear();
}
Ok(())
}
impl<T: Event> FnArgFetcher for EventReader<T> {
type State = usize;
type Arg<'a, 'state> = EventReader<T>;
fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State {
0
}
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {
let world = world.as_ref();
let events = world.get_resource::<Events<T>>();
// reset the reader cursor when the events are cleared
let world_tick = world.current_tick();
if world_tick == events.last_updated_tick.unwrap_or(world_tick) {
*state = 0;
}
let mut reader = events.reader();
reader.cursor = *state;
reader
}
fn apply_deferred(_: Self::State, _: std::ptr::NonNull<lyra_ecs::World>) { }
}
impl<T: Event> FnArgFetcher for EventWriter<T> {
type State = ();
type Arg<'a, 'state> = EventWriter<T>;
fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State {
()
}
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {
let world = world.as_ref();
let events = world.get_resource::<Events<T>>();
events.writer()
}
fn apply_deferred(_: Self::State, _: std::ptr::NonNull<lyra_ecs::World>) { }
}

View File

@ -1,202 +0,0 @@
use std::{any::{Any, TypeId}, collections::{HashMap, VecDeque}, marker::PhantomData, mem::{self, MaybeUninit}, ops::Range};
use crate::plugin::Plugin;
pub trait Event: Clone + Send + Sync + 'static {}
impl<T: Clone + Send + Sync + 'static> Event for T {}
pub type Events<T> = VecDeque<T>;
#[derive(Clone)]
struct TypeErasedBuffer {
type_id: TypeId,
item_size: usize,
data: Vec<MaybeUninit<u8>>,
fn_drop_item: fn(item: *mut ()),
}
impl Drop for TypeErasedBuffer {
fn drop(&mut self) {
let range = self.data.as_mut_ptr_range();
let mut current = range.start;
let end = range.end;
while current < end {
// SAFETY: current pointer will either be the start of the buffer, or at the start of
// the next item
(self.fn_drop_item)(current.cast());
// SAFETY: the items are placed right after eachother
current = unsafe { current.add(self.item_size) };
}
// Safety: all of the events were just dropped
unsafe { self.data.set_len(0) };
}
}
impl TypeErasedBuffer {
pub fn new<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
item_size: mem::size_of::<T>(),
data: Default::default(),
// dropped immediately after the read
fn_drop_item: |item| unsafe { item.cast::<T>().drop_in_place() },
}
}
pub fn push<T: 'static>(&mut self, value: T) {
debug_assert!(TypeId::of::<T>() == self.type_id, "The type of the buffer does not match the type that was added to it");
// reserve enough bytes to store T
let old_len = self.data.len();
self.data.reserve(mem::size_of::<T>());
// Get a pointer to the end of the data.
// SAFETY: we just reserved enough memory to store the data.
let end_ptr = unsafe { self.data.as_mut_ptr().add(old_len) };
unsafe {
// write the command and its runner into the buffer
end_ptr.cast::<T>()
// written unaligned to keep everything packed
.write_unaligned(value);
// we wrote to the vec's buffer without using its api, so we need manually
// set the length of the vec.
self.data.set_len(old_len + mem::size_of::<T>());
}
}
pub fn len(&self) -> usize {
self.data.len() / self.item_size
}
}
pub struct EventQueue {
events: HashMap<TypeId, TypeErasedBuffer>,
event_write_queue: HashMap<TypeId, TypeErasedBuffer>,
}
impl Default for EventQueue {
fn default() -> Self {
Self::new()
}
}
impl EventQueue {
pub fn new() -> Self {
Self {
event_write_queue: HashMap::new(),
events: HashMap::new(),
}
}
/// Trigger an event
pub fn trigger_event<E>(&mut self, event: E)
where
E: Event
{
// the compiler wants me to explicit right here for some reason
/* let default = || RefCell::new(Box::<Events::<E>>::default() as Box<dyn AsSyncAny>);
// Get, or create, a list of events of this type
let type_id = event.type_id();
let mut events = self.event_write_queue.entry(type_id)
.or_insert_with(default)
.borrow_mut();
// downcast resource as an events storage
let e: &mut Events<E> = events.as_mut().as_any_mut().downcast_mut().unwrap();
e.push_back(event); */
let type_id = event.type_id();
let events = self.event_write_queue.entry(type_id)
.or_insert_with(|| TypeErasedBuffer::new::<E>());
events.push(event);
}
// Clear events, this should happen at the start of every tick since events are cloned
// before being given to the reader.
pub fn update_events(&mut self) {
self.events.clear();
// get all keys of events
let keys: Vec<TypeId> = self.event_write_queue.keys().copied().collect();
// remove all elements from self.event_write_queue and insert them to events
for k in keys.into_iter() {
let v = self.event_write_queue.remove(&k).unwrap();
self.events.insert(k, v);
}
}
pub fn read_events<E>(&self) -> Option<EventReader<E>>
where
E: Event
{
self.events.get(&TypeId::of::<E>())
.map(|event| EventReader::new(event.clone()))
}
}
pub struct EventReader<E: Event> {
buf: TypeErasedBuffer,
range: Option<Range<*mut MaybeUninit<u8>>>,
_marker: PhantomData<E>,
}
impl<E: Event> EventReader<E> {
fn new(buffer: TypeErasedBuffer) -> Self {
Self {
buf: buffer,
range: None,
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<E: Event> Iterator for EventReader<E> {
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
if self.range.is_none() {
self.range = Some(self.buf.data.as_mut_ptr_range());
}
let range = self.range.as_mut().unwrap();
if range.start < range.end {
// Retrieve the event in the buffer.
// SAFETY: current pointer will either be the start of the buffer, or at the start
// of the next event.
let event = unsafe { range.start.cast::<E>().read_unaligned() };
// Advance the pointer to be after this event.
range.start = unsafe { range.start.add(mem::size_of::<E>()) };
Some(event)
} else {
None
}
}
}
#[derive(Default)]
pub struct EventsPlugin;
impl Plugin for EventsPlugin {
fn setup(&mut self, app: &mut crate::game::App) {
app.world.add_resource(EventQueue::new());
}
}

View File

@ -10,7 +10,7 @@ use tracing_subscriber::{
util::SubscriberInitExt, fmt, util::SubscriberInitExt, fmt,
}; };
use crate::{plugin::Plugin, render::renderer::Renderer, Stage, StagedExecutor}; use crate::{event_cleaner_system, plugin::Plugin, render::renderer::Renderer, Event, Events, Stage, StagedExecutor};
#[derive(Clone, Copy, Hash, Debug)] #[derive(Clone, Copy, Hash, Debug)]
pub enum GameStages { pub enum GameStages {
@ -214,4 +214,21 @@ impl App {
.expect("No run function set"); .expect("No run function set");
f(self); f(self);
} }
pub fn register_event<T: Event>(&mut self) {
let world = &mut self.world;
// only register the event if it isn't already registered.
if !world.has_resource::<Events<T>>() {
world.add_resource_default::<Events<T>>();
let sys_name = format!("{}_event_cleaner_system", std::any::type_name::<T>().to_lowercase());
self.add_system_to_stage(GameStages::First, &sys_name, event_cleaner_system::<T>, &[]);
}
}
pub fn push_event<T: Event>(&mut self, event: T) {
let world = &mut self.world;
let mut events = world.try_get_resource_mut::<Events<T>>()
.expect("missing events for event type! Must use `App::register_event` first");
events.push_event(event);
}
} }

View File

@ -1,10 +1,10 @@
use std::{collections::HashMap, ops::Deref, hash::{Hash, DefaultHasher, Hasher}, fmt::Debug}; use std::{collections::HashMap, hash::{Hash, DefaultHasher, Hasher}, fmt::Debug};
use glam::Vec2; use glam::Vec2;
use lyra_ecs::World; use lyra_ecs::query::{Res, ResMut};
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use crate::{plugin::Plugin, game::GameStages, EventQueue}; use crate::{game::GameStages, plugin::Plugin, EventReader};
use super::{Button, InputButtons, KeyCode, MouseButton, MouseMotion}; use super::{Button, InputButtons, KeyCode, MouseButton, MouseMotion};
@ -497,64 +497,64 @@ impl ActionHandlerBuilder {
} }
} }
fn actions_system(world: &mut World) -> anyhow::Result<()> { //fn actions_system(world: &mut World) -> anyhow::Result<()> {
let keys = world.try_get_resource::<InputButtons<KeyCode>>() fn actions_system(
.map(|r| r.deref().clone()); input_btns: Res<InputButtons<KeyCode>>,
let mouse_events = world mut mouse_ev: EventReader<MouseMotion>,
.try_get_resource::<EventQueue>() mut handler: ResMut<ActionHandler>,
.and_then(|q| q.read_events::<MouseMotion>()); ) -> anyhow::Result<()> {
//let mouse = world.try_get_resource()
// clear the states of all axises each frame // clear the states of all axises each frame
{
let mut handler = world.try_get_resource_mut::<ActionHandler>()
.expect("No Input Action handler was created in the world!");
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout"); let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping"); let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
for (action, _) in mapping.action_binds.clone().iter() { for (action, _) in mapping.action_binds.clone().iter() {
let action = handler.actions.get_mut(action).expect("Action name for binding is invalid!"); let action = handler.actions.get_mut(action).expect("Action name for binding is invalid!");
if action.kind == ActionKind::Axis { if action.kind == ActionKind::Axis {
action.state = ActionState::Axis(0.0); action.state = ActionState::Axis(0.0);
} }
} }
}
let motion_avg = if let Some(mouse_events) = mouse_events { // collect all events to a list
let mouse_events = {
let mut evs = vec![];
while let Some(ev) = mouse_ev.read() {
evs.push(ev.clone());
}
evs
};
// get the average motion from the events that were collected last frame
let motion_avg = if !mouse_events.is_empty() {
let count = mouse_events.len(); let count = mouse_events.len();
let mut sum = Vec2::ZERO; let mut sum = Vec2::ZERO;
for mm in mouse_events { for mm in mouse_events {
sum += mm.delta; sum += mm.delta;
} }
Some(sum / count as f32)
} else { None };
let mut handler = world.try_get_resource_mut::<ActionHandler>() sum / count as f32
.expect("No Input Action handler was created in the world!"); } else { Vec2::new(0.0, 0.0) };
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout"); let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping"); let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
for (action_lbl, binds) in mapping.action_binds.clone().iter() { for (action_lbl, binds) in mapping.action_binds.clone().iter() {
let action = handler.actions.get_mut(action_lbl).expect("Action name for binding is invalid!"); let action = handler.actions.get_mut(action_lbl).expect("Action name for binding is invalid!");
let mut new_state = None; let mut new_state = None;
for bind in binds.iter() { for bind in binds.iter() {
match bind.source { match bind.source {
ActionSource::Keyboard(key) => if let Some(keys) = &keys { ActionSource::Keyboard(key) => {
// JustPressed needs to be first, since is_pressed includes buttons that // JustPressed needs to be first, since is_pressed includes buttons that
// were just pressed. // were just pressed.
match action.kind { match action.kind {
ActionKind::Button => { ActionKind::Button => {
if keys.was_just_pressed(key) { if input_btns.was_just_pressed(key) {
new_state = Some(ActionState::JustPressed(bind.modifier)); new_state = Some(ActionState::JustPressed(bind.modifier));
} else if keys.is_pressed(key) { } else if input_btns.is_pressed(key) {
new_state = Some(ActionState::Pressed(bind.modifier)); new_state = Some(ActionState::Pressed(bind.modifier));
} }
}, },
ActionKind::Axis => { ActionKind::Axis => {
if keys.is_pressed(key) { if input_btns.is_pressed(key) {
new_state = Some(ActionState::Axis(bind.modifier)); new_state = Some(ActionState::Axis(bind.modifier));
} }
} }
@ -564,7 +564,7 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> {
ActionSource::Gamepad(_, _) => todo!(), ActionSource::Gamepad(_, _) => todo!(),
ActionSource::Mouse(m) => match m { ActionSource::Mouse(m) => match m {
MouseInput::Button(_) => todo!(), MouseInput::Button(_) => todo!(),
MouseInput::Axis(a) => if let Some(motion_avg) = motion_avg { MouseInput::Axis(a) => {
match a { match a {
MouseAxis::X => { MouseAxis::X => {
new_state = Some(ActionState::Axis(motion_avg.x)); new_state = Some(ActionState::Axis(motion_avg.x));

View File

@ -1,49 +1,14 @@
use std::ptr::NonNull; use std::ops::Deref;
use glam::Vec2; use glam::Vec2;
use lyra_ecs::{World, system::IntoSystem}; use lyra_ecs::query::ResMut;
use winit::{event::MouseScrollDelta, keyboard::PhysicalKey}; use winit::{event::{MouseScrollDelta, WindowEvent}, keyboard::PhysicalKey};
use crate::{EventQueue, plugin::Plugin, game::GameStages}; use crate::{game::GameStages, plugin::Plugin, winit::DeviceEventPair, EventReader, EventWriter};
use super::{events::*, InputButtons, InputEvent}; use super::{events::*, InputButtons, InputEvent};
#[derive(Default)] fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &MouseScrollDelta) {
pub struct InputSystem;
impl InputSystem {
pub fn process_event(&mut self, world: &mut World, event: &InputEvent) -> bool {
let event_queue = world.try_get_resource_mut::<EventQueue>();
if event_queue.is_none() {
return false;
}
let mut event_queue = event_queue.unwrap();
match event {
InputEvent::KeyboardInput { device_id, event, .. } => {
if let PhysicalKey::Code(code) = event.physical_key {
drop(event_queue);
let mut e = world.get_resource_or_else(InputButtons::<winit::keyboard::KeyCode>::new);
//let mut e = with_resource_mut(world, || InputButtons::<KeyCode>::new());
e.add_input_from_winit(code, event.state);
}
},
InputEvent::CursorMoved { position, .. } => {
let exact = MouseExact {
pos: Vec2::new(position.x as f32, position.y as f32)
};
event_queue.trigger_event(exact);
},
InputEvent::CursorEntered { .. } => {
let event = CursorEnteredWindow {};
event_queue.trigger_event(event);
},
InputEvent::CursorLeft { .. } => {
let event = CursorLeftWindow {};
event_queue.trigger_event(event);
},
InputEvent::MouseWheel { delta, .. } => {
let event = match delta { let event = match delta {
MouseScrollDelta::LineDelta(x, y) => MouseScroll { MouseScrollDelta::LineDelta(x, y) => MouseScroll {
unit: MouseScrollUnit::Line(Vec2::new(*x, *y)), unit: MouseScrollUnit::Line(Vec2::new(*x, *y)),
@ -53,9 +18,46 @@ impl InputSystem {
}, },
}; };
event_queue.trigger_event(event); mouse_scroll_ev.write(event);
}, //MouseButton }
InputEvent::MouseInput { button, state, .. } => {
pub fn input_system(
mut key_code_res: ResMut<InputButtons<winit::keyboard::KeyCode>>,
mut mouse_btn_res: ResMut<InputButtons<MouseButton>>,
mut touches_res: ResMut<Touches>,
mut window_ev: EventReader<WindowEvent>,
mut device_ev: EventReader<DeviceEventPair>,
mut mouse_scroll_ev: EventWriter<MouseScroll>,
mut mouse_btn_ev: EventWriter<MouseButton>,
mut mouse_exact_ev: EventWriter<MouseExact>,
mut mouse_entered_ev: EventWriter<CursorEnteredWindow>,
mut mouse_left_ev: EventWriter<CursorLeftWindow>,
mut mouse_motion_ev: EventWriter<MouseMotion>,
) -> anyhow::Result<()> {
while let Some(event) = window_ev.read() {
match event.deref() {
WindowEvent::KeyboardInput { event, .. } => {
if let PhysicalKey::Code(code) = event.physical_key {
key_code_res.add_input_from_winit(code, event.state);
}
},
WindowEvent::CursorMoved { position, .. } => {
let exact = MouseExact {
pos: Vec2::new(position.x as f32, position.y as f32)
};
mouse_exact_ev.write(exact);
},
WindowEvent::CursorEntered { .. } => {
mouse_entered_ev.write(CursorEnteredWindow);
},
WindowEvent::CursorLeft { .. } => {
mouse_left_ev.write(CursorLeftWindow);
},
WindowEvent::MouseWheel { delta, .. } => {
write_scroll_delta(&mut mouse_scroll_ev, delta);
},
WindowEvent::MouseInput { button, state, .. } => {
let button_event = match button { let button_event = match button {
winit::event::MouseButton::Left => MouseButton::Left, winit::event::MouseButton::Left => MouseButton::Left,
winit::event::MouseButton::Right => MouseButton::Right, winit::event::MouseButton::Right => MouseButton::Right,
@ -65,15 +67,10 @@ impl InputSystem {
winit::event::MouseButton::Other(v) => MouseButton::Other(*v), winit::event::MouseButton::Other(v) => MouseButton::Other(*v),
}; };
event_queue.trigger_event(button_event); mouse_btn_ev.write(button_event);
drop(event_queue); mouse_btn_res.add_input_from_winit(button_event, *state);
let mut e = world.get_resource_or_else(InputButtons::<MouseButton>::new);
e.add_input_from_winit(button_event, *state);
}, },
InputEvent::Touch(t) => { WindowEvent::Touch(t) => {
drop(event_queue);
let touch = Touch { let touch = Touch {
phase: TouchPhase::from(t.phase), phase: TouchPhase::from(t.phase),
location: Vec2::new(t.location.x as f32, t.location.y as f32), location: Vec2::new(t.location.x as f32, t.location.y as f32),
@ -81,61 +78,55 @@ impl InputSystem {
finger_id: t.id, finger_id: t.id,
}; };
let mut touches = world.get_resource_or_else(Touches::new); touches_res.touches.push(touch);
touches.touches.push(touch);
}, },
_ => {}, _ => {},
} }
false
}
} }
impl crate::ecs::system::System for InputSystem { while let Some(device) = device_ev.read() {
fn execute(&mut self, mut world: NonNull<World>) -> anyhow::Result<()> { match &device.event {
let world = unsafe { world.as_mut() }; winit::event::DeviceEvent::Motion { .. } => {
let queue = world.try_get_resource_mut::<EventQueue>() // TODO: handle device motion events
.and_then(|q| q.read_events::<InputEvent>()); // A todo! isn't used since these are triggered alongside MouseMotion events
let mut e = world.get_resource_or_else(InputButtons::<winit::keyboard::KeyCode>::new);
e.update();
drop(e);
if queue.is_none() {
return Ok(());
} }
winit::event::DeviceEvent::MouseMotion { delta } => {
let delta = MouseMotion {
delta: Vec2::new(delta.0 as f32, delta.1 as f32)
};
let events = queue.unwrap(); mouse_motion_ev.write(delta);
for event in events { },
self.process_event(world, &event); winit::event::DeviceEvent::MouseWheel { delta } => {
write_scroll_delta(&mut mouse_scroll_ev, delta);
},
_ => {
todo!("unhandled device event: {:?}", device.event);
}
}
} }
Ok(()) Ok(())
} }
fn world_access(&self) -> lyra_ecs::Access {
lyra_ecs::Access::Write
}
fn execute_deferred(&mut self, _: NonNull<World>) -> anyhow::Result<()> {
Ok(())
}
}
impl IntoSystem<()> for InputSystem {
type System = Self;
fn into_system(self) -> Self::System {
self
}
}
/// Plugin that runs InputSystem /// Plugin that runs InputSystem
#[derive(Default)] #[derive(Default)]
pub struct InputPlugin; pub struct InputPlugin;
impl Plugin for InputPlugin { impl Plugin for InputPlugin {
fn setup(&mut self, app: &mut crate::game::App) { fn setup(&mut self, app: &mut crate::game::App) {
app.add_system_to_stage(GameStages::PreUpdate, "input", InputSystem, &[]); app.add_resource(InputButtons::<winit::keyboard::KeyCode>::default());
app.add_resource(InputButtons::<MouseButton>::default());
app.add_resource(Touches::default());
app.register_event::<InputEvent>();
app.register_event::<MouseScroll>();
app.register_event::<MouseButton>();
app.register_event::<MouseMotion>();
app.register_event::<MouseExact>();
app.register_event::<CursorEnteredWindow>();
app.register_event::<CursorLeftWindow>();
app.add_system_to_stage(GameStages::PreUpdate, "input", input_system, &[]);
} }
} }

View File

@ -1,5 +1,4 @@
#![feature(hash_extract_if)] #![feature(hash_extract_if)]
#![feature(lint_reasons)]
#![feature(trait_alias)] #![feature(trait_alias)]
#![feature(map_many_mut)] #![feature(map_many_mut)]
@ -14,8 +13,8 @@ pub mod as_any;
pub mod plugin; pub mod plugin;
pub mod change_tracker; pub mod change_tracker;
pub mod events; mod event;
pub use events::*; pub use event::*;
pub mod stage; pub mod stage;
pub use stage::*; pub use stage::*;

View File

@ -3,7 +3,6 @@ use lyra_resource::ResourceManager;
use crate::game::App; use crate::game::App;
use crate::winit::WinitPlugin; use crate::winit::WinitPlugin;
use crate::EventsPlugin;
use crate::DeltaTimePlugin; use crate::DeltaTimePlugin;
use crate::input::InputPlugin; use crate::input::InputPlugin;
use crate::render::window::WindowPlugin; use crate::render::window::WindowPlugin;
@ -113,7 +112,6 @@ impl Plugin for DefaultPlugins {
fn setup(&mut self, app: &mut App) { fn setup(&mut self, app: &mut App) {
WinitPlugin::default().setup(app); WinitPlugin::default().setup(app);
CommandQueuePlugin.setup(app); CommandQueuePlugin.setup(app);
EventsPlugin.setup(app);
InputPlugin.setup(app); InputPlugin.setup(app);
ResourceManagerPlugin.setup(app); ResourceManagerPlugin.setup(app);
WindowPlugin::default().setup(app); WindowPlugin::default().setup(app);

View File

@ -1,13 +1,13 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use lyra_ecs::{query::{filter::Changed, Entities, ResMut}, Component, World}; use lyra_ecs::{query::{filter::Changed, Entities, Res, View}, Component};
use lyra_resource::Image; use lyra_resource::Image;
use tracing::error; use tracing::error;
use winit::window::{CustomCursor, Fullscreen, Window}; use winit::window::{CustomCursor, Fullscreen, Window};
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel}; pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
use crate::{change_tracker::Ct, plugin::Plugin, winit::WinitWindows}; use crate::{plugin::Plugin, winit::WinitWindows};
#[derive(Default, Clone, Copy, PartialEq)] #[derive(Default, Clone, Copy, PartialEq)]
pub struct Area { pub struct Area {
@ -485,8 +485,12 @@ impl WindowOptions {
} }
} }
/// The state of the window last time it was changed.
///
/// This is used in [`window_sync_system`] to see what fields of [`WindowOptions`] changed
/// when syncing the winit window with the component.
#[derive(Clone, Component)] #[derive(Clone, Component)]
struct LastWindow { pub struct LastWindow {
last: WindowOptions, last: WindowOptions,
} }
@ -511,8 +515,8 @@ pub struct WindowPlugin {
} }
/// A system that syncs Winit Windows with [`WindowOptions`] components. /// A system that syncs Winit Windows with [`WindowOptions`] components.
pub fn window_sync_system(world: &mut World) -> anyhow::Result<()> { pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &WindowOptions, &mut LastWindow), Changed<WindowOptions>>) -> anyhow::Result<()> {
for (entity, opts, mut last, windows) in world.filtered_view_iter::<(Entities, &WindowOptions, &mut LastWindow, ResMut<WinitWindows>), Changed<WindowOptions>>() { for (entity, opts, mut last) in view.iter() {
let window = windows.get_entity_window(entity) let window = windows.get_entity_window(entity)
.expect("entity's window is missing"); .expect("entity's window is missing");
@ -644,9 +648,6 @@ pub fn window_sync_system(world: &mut World) -> anyhow::Result<()> {
impl Plugin for WindowPlugin { impl Plugin for WindowPlugin {
fn setup(&mut self, app: &mut crate::game::App) { fn setup(&mut self, app: &mut crate::game::App) {
let window_options = WindowOptions::default();
app.world.add_resource(Ct::new(window_options));
app.with_system("window_sync", window_sync_system, &[]); app.with_system("window_sync", window_sync_system, &[]);
} }
} }

View File

@ -4,10 +4,30 @@ use async_std::task::block_on;
use glam::IVec2; use glam::IVec2;
use lyra_ecs::Entity; use lyra_ecs::Entity;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use tracing::{debug, error, info, warn}; use tracing::{debug, error, warn};
use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowAttributes, WindowId}}; use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowAttributes, WindowId},
};
use crate::{game::{App, WindowState}, input::InputEvent, plugin::Plugin, render::{renderer::BasicRenderer, window::{PrimaryWindow, Size, WindowOptions}}, EventQueue}; use crate::{
game::{App, WindowState},
plugin::Plugin,
render::{
renderer::BasicRenderer,
window::{PrimaryWindow, Size, WindowOptions},
},
};
/// A struct that contains a [`DeviceEvent`](winit::event::DeviceEvent) with its source
/// [`DeviceId`](winit::event::DeviceId).
#[derive(Debug, Clone)]
pub struct DeviceEventPair {
pub device_src: winit::event::DeviceId,
pub event: winit::event::DeviceEvent,
}
pub struct WinitPlugin { pub struct WinitPlugin {
/// The primary window that will be created. /// The primary window that will be created.
@ -20,13 +40,17 @@ pub struct WinitPlugin {
impl Default for WinitPlugin { impl Default for WinitPlugin {
fn default() -> Self { fn default() -> Self {
Self { primary_window: Some(WindowOptions::default()) } Self {
primary_window: Some(WindowOptions::default()),
}
} }
} }
impl Plugin for WinitPlugin { impl Plugin for WinitPlugin {
fn setup(&mut self, app: &mut crate::game::App) { fn setup(&mut self, app: &mut crate::game::App) {
app.set_run_fn(winit_app_runner); app.set_run_fn(winit_app_runner);
app.register_event::<WindowEvent>();
app.register_event::<DeviceEventPair>();
if let Some(prim) = self.primary_window.take() { if let Some(prim) = self.primary_window.take() {
app.add_resource(WinitWindows::with_window(prim)); app.add_resource(WinitWindows::with_window(prim));
@ -39,13 +63,9 @@ impl Plugin for WinitPlugin {
true true
} }
fn complete(&self, _app: &mut crate::game::App) { fn complete(&self, _app: &mut crate::game::App) {}
} fn cleanup(&self, _app: &mut crate::game::App) {}
fn cleanup(&self, _app: &mut crate::game::App) {
}
} }
#[derive(Default)] #[derive(Default)]
@ -65,7 +85,12 @@ impl WinitWindows {
} }
} }
pub fn create_window(&mut self, event_loop: &ActiveEventLoop, entity: Entity, attr: WindowAttributes) -> Result<WindowId, winit::error::OsError> { pub fn create_window(
&mut self,
event_loop: &ActiveEventLoop,
entity: Entity,
attr: WindowAttributes,
) -> Result<WindowId, winit::error::OsError> {
let win = event_loop.create_window(attr)?; let win = event_loop.create_window(attr)?;
let id = win.id(); let id = win.id();
@ -77,37 +102,34 @@ impl WinitWindows {
} }
pub fn get_entity_window(&self, entity: Entity) -> Option<&Arc<Window>> { pub fn get_entity_window(&self, entity: Entity) -> Option<&Arc<Window>> {
self.entity_to_window.get(&entity) self.entity_to_window
.get(&entity)
.and_then(|id| self.windows.get(id)) .and_then(|id| self.windows.get(id))
} }
} }
pub fn winit_app_runner(app: App) { pub fn winit_app_runner(app: App) {
let evloop = EventLoop::new() let evloop = EventLoop::new().expect("failed to create winit EventLoop");
.expect("failed to create winit EventLoop");
let mut winit_runner = WinitRunner { let mut winit_runner = WinitRunner { app };
app, evloop.run_app(&mut winit_runner).expect("loop error");
};
evloop.run_app(&mut winit_runner)
.expect("loop error");
} }
struct WinitRunner { struct WinitRunner {
app: App app: App,
} }
impl ApplicationHandler for WinitRunner { impl ApplicationHandler for WinitRunner {
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
self.app.update(); self.app.update();
let renderer = self.app.renderer.get_mut().expect("renderer was not initialized"); let renderer = self
.app
.renderer
.get_mut()
.expect("renderer was not initialized");
renderer.prepare(&mut self.app.world); renderer.prepare(&mut self.app.world);
if let Some(mut event_queue) = self.app.world.try_get_resource_mut::<EventQueue>() {
event_queue.update_events();
}
match renderer.render() { match renderer.render() {
Ok(_) => {} Ok(_) => {}
// Reconfigure the surface if lost // Reconfigure the surface if lost
@ -149,86 +171,107 @@ impl ApplicationHandler for WinitRunner {
} }
} }
fn device_event(
&mut self,
_: &ActiveEventLoop,
device_src: winit::event::DeviceId,
event: winit::event::DeviceEvent,
) {
self.app.push_event(DeviceEventPair { device_src, event });
}
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &winit::event_loop::ActiveEventLoop, event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId, window_id: winit::window::WindowId,
event: WindowEvent, event: WindowEvent,
) { ) {
let windows = self.app.world.get_resource::<WinitWindows>(); /* let windows = self.app.world.get_resource::<WinitWindows>();
let window = match windows.windows.get(&window_id) { let window = match windows.windows.get(&window_id) {
Some(w) => w.clone(), Some(w) => w.clone(),
None => return, None => return,
}; };
drop(windows); drop(windows); */
// If try_from failed, that means that the WindowEvent is not an
// input related event.
if let Some(input_ev) = InputEvent::from_window_event(&event) {
// Its possible to receive multiple input events before the update event for
// the InputSystem is called, so we must use a queue for the events.
if let Some(mut event_queue) = self.app.world.try_get_resource_mut::<EventQueue>() {
event_queue.trigger_event(input_ev.clone());
}
} else {
self.app.push_event(event.clone());
match event { match event {
WindowEvent::ActivationTokenDone { serial, token } => todo!(), WindowEvent::ActivationTokenDone { .. } => todo!(),
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
self.app.on_resize(physical_size); self.app.on_resize(physical_size);
let mut window_opts = self.app.world.get_resource::<WinitWindows>() let mut window_opts = self
.window_to_entity.get(&window_id) .app
.world
.get_resource::<WinitWindows>()
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get()) .and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.unwrap(); .unwrap();
window_opts.size = Size::new_physical(physical_size.width, physical_size.height); window_opts.size = Size::new_physical(physical_size.width, physical_size.height);
}, }
WindowEvent::Moved(physical_position) => { WindowEvent::Moved(physical_position) => {
let mut state = self.app.world.get_resource_or_else(WindowState::new); let mut state = self.app.world.get_resource_or_else(WindowState::new);
state.position = IVec2::new(physical_position.x, physical_position.y); state.position = IVec2::new(physical_position.x, physical_position.y);
}, }
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
self.app.on_exit(); self.app.on_exit();
event_loop.exit(); event_loop.exit();
}, }
WindowEvent::Destroyed => todo!(), WindowEvent::Destroyed => todo!(),
WindowEvent::DroppedFile(path_buf) => todo!(), WindowEvent::DroppedFile(_path_buf) => todo!(),
WindowEvent::HoveredFile(path_buf) => todo!(), WindowEvent::HoveredFile(_path_buf) => todo!(),
WindowEvent::HoveredFileCancelled => todo!(), WindowEvent::HoveredFileCancelled => todo!(),
WindowEvent::Focused(focused) => { WindowEvent::Focused(focused) => {
let mut window_opts = self.app.world.get_resource::<WinitWindows>() let mut window_opts = self
.window_to_entity.get(&window_id) .app
.world
.get_resource::<WinitWindows>()
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get()) .and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.unwrap(); .unwrap();
window_opts.focused = focused; window_opts.focused = focused;
}, }
WindowEvent::ModifiersChanged(modifiers) => debug!("modifiers changed: {:?}", modifiers), WindowEvent::ModifiersChanged(modifiers) => {
debug!("modifiers changed: {:?}", modifiers)
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => { WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
let mut window_opts = self.app.world.get_resource::<WinitWindows>() let mut window_opts = self
.window_to_entity.get(&window_id) .app
.world
.get_resource::<WinitWindows>()
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get()) .and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.unwrap(); .unwrap();
window_opts.scale_factor = scale_factor; window_opts.scale_factor = scale_factor;
}, }
WindowEvent::ThemeChanged(theme) => { WindowEvent::ThemeChanged(theme) => {
let mut window_opts = self.app.world.get_resource::<WinitWindows>() let mut window_opts = self
.window_to_entity.get(&window_id) .app
.world
.get_resource::<WinitWindows>()
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get()) .and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.unwrap(); .unwrap();
window_opts.theme = Some(theme); window_opts.theme = Some(theme);
}, }
WindowEvent::Occluded(occ) => { WindowEvent::Occluded(occ) => {
let mut window_opts = self.app.world.get_resource::<WinitWindows>() let mut window_opts = self
.window_to_entity.get(&window_id) .app
.world
.get_resource::<WinitWindows>()
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get()) .and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.unwrap(); .unwrap();
window_opts.occluded = occ; window_opts.occluded = occ;
}, }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
//debug!("should redraw"); //debug!("should redraw");
}, }
_ => {} _ => {}
} }
} }
} }
}