Add resource system, early input system, custom hecs world wrapper
This commit is contained in:
parent
a47d5b00ef
commit
f5bfa93f63
File diff suppressed because it is too large
Load Diff
|
@ -21,7 +21,8 @@ tobj = { version = "3.2.1", features = [
|
||||||
]}
|
]}
|
||||||
instant = "0.1"
|
instant = "0.1"
|
||||||
async-trait = "0.1.65"
|
async-trait = "0.1.65"
|
||||||
specs = { version = "0.18.0", features = [ "derive" ] }
|
|
||||||
hecs = "0.10.3"
|
hecs = "0.10.3"
|
||||||
glam = { version = "0.24.0", features = ["bytemuck"] }
|
glam = { version = "0.24.0", features = ["bytemuck"] }
|
||||||
petgraph = "0.6.3"
|
petgraph = "0.6.3"
|
||||||
|
resources = "1.1.0"
|
||||||
|
gilrs-core = "0.5.6"
|
||||||
|
|
|
@ -11,6 +11,7 @@ mkShell rec {
|
||||||
valgrind
|
valgrind
|
||||||
heaptrack
|
heaptrack
|
||||||
mold
|
mold
|
||||||
|
udev
|
||||||
];
|
];
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
udev alsa-lib vulkan-loader
|
udev alsa-lib vulkan-loader
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
pub struct Controls {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
use specs::{Component, DenseVecStorage};
|
|
||||||
|
|
||||||
use crate::render::{vertex::Vertex, mesh::Mesh, material::Material};
|
use crate::render::{vertex::Vertex, mesh::Mesh, material::Material};
|
||||||
|
|
||||||
#[derive(Component, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MeshComponent {
|
pub struct MeshComponent {
|
||||||
pub mesh: Mesh,
|
pub mesh: Mesh,
|
||||||
pub material: Material,
|
pub material: Material,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use specs::{Component, DenseVecStorage};
|
|
||||||
|
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
|
|
||||||
#[derive(Component, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TransformComponent {
|
pub struct TransformComponent {
|
||||||
pub transform: Transform,
|
pub transform: Transform,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,31 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use hecs::World;
|
|
||||||
use petgraph::{prelude::StableDiGraph, stable_graph::NodeIndex, visit::Topo};
|
use petgraph::{prelude::StableDiGraph, stable_graph::NodeIndex, visit::Topo};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
use crate::game::Controls;
|
||||||
|
|
||||||
pub mod components;
|
pub mod components;
|
||||||
|
pub mod world;
|
||||||
|
|
||||||
|
use world::World;
|
||||||
|
|
||||||
/// A trait that represents a simple system
|
/// A trait that represents a simple system
|
||||||
pub trait SimpleSystem {
|
pub trait SimpleSystem {
|
||||||
fn setup(&mut self, world: &mut World) -> anyhow::Result<()> {
|
fn setup(&mut self, controls: &mut Controls) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: make async?
|
// todo: make async?
|
||||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()>;
|
fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> SimpleSystem for S
|
impl<S> SimpleSystem for S
|
||||||
where S: FnMut(&mut World) -> anyhow::Result<()>
|
where S: FnMut(&mut World) -> anyhow::Result<()>
|
||||||
{
|
{
|
||||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()> {
|
||||||
self(world)
|
self(controls.world)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +44,9 @@ impl BatchedSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSystem for BatchedSystem {
|
impl SimpleSystem for BatchedSystem {
|
||||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()> {
|
||||||
for system in self.systems.iter_mut() {
|
for system in self.systems.iter_mut() {
|
||||||
system.execute_mut(world)?;
|
system.execute_mut(controls)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -111,13 +115,13 @@ impl SystemDispatcher {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn execute_systems(&mut self, world: &mut World) {
|
pub(crate) fn execute_systems(&mut self, controls: &mut Controls) {
|
||||||
let mut topo = Topo::new(&self.graph);
|
let mut topo = Topo::new(&self.graph);
|
||||||
|
|
||||||
while let Some(nx) = topo.next(&self.graph) {
|
while let Some(nx) = topo.next(&self.graph) {
|
||||||
let node = self.graph.node_weight_mut(nx).unwrap();
|
let node = self.graph.node_weight_mut(nx).unwrap();
|
||||||
|
|
||||||
match node.system.execute_mut(world) {
|
match node.system.execute_mut(controls) {
|
||||||
Ok(()) => {},
|
Ok(()) => {},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("System execution of {} resulted in an error! '{}'.", node.name, e);
|
warn!("System execution of {} resulted in an error! '{}'.", node.name, e);
|
||||||
|
@ -133,8 +137,8 @@ impl SystemDispatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSystem for SystemDispatcher {
|
impl SimpleSystem for SystemDispatcher {
|
||||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()> {
|
||||||
self.execute_systems(world);
|
self.execute_systems(controls);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
use std::{any::{TypeId, Any}, collections::{HashMap, HashSet, VecDeque}, ops::{Deref, DerefMut}};
|
||||||
|
|
||||||
|
use winit::event::WindowEvent;
|
||||||
|
|
||||||
|
pub trait Resource: Send + Sync + 'static {
|
||||||
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for WindowEvent<'static> {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sync + Send + 'static> Resource for VecDeque<T> {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct World {
|
||||||
|
inner: hecs::World,
|
||||||
|
resources: HashMap<TypeId, Box<dyn Resource>>,
|
||||||
|
updated_resources: HashSet<TypeId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for World {
|
||||||
|
type Target = hecs::World;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for World {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl World {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::from_hecs(hecs::World::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_hecs(hecs_world: hecs::World) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: hecs_world,
|
||||||
|
resources: HashMap::new(),
|
||||||
|
updated_resources: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a resource into the World. You can only have one resource of the same type.
|
||||||
|
/// If you attempt to add another resource of the same type, it will be replaced.
|
||||||
|
///
|
||||||
|
/// This will also mark the resource as updated in this frame.
|
||||||
|
pub fn insert_resource<R>(&mut self, res: R)
|
||||||
|
where
|
||||||
|
R: Resource
|
||||||
|
{
|
||||||
|
let type_id = res.type_id();
|
||||||
|
self.resources.insert(type_id, Box::new(res));
|
||||||
|
self.updated_resources.insert(type_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a resource was updated this frame.
|
||||||
|
pub fn was_res_updated<R>(&self) -> bool
|
||||||
|
where
|
||||||
|
R: Resource
|
||||||
|
{
|
||||||
|
self.updated_resources.contains(&TypeId::of::<R>())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear_updated_resources(&mut self) {
|
||||||
|
self.updated_resources.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query a resource.
|
||||||
|
///
|
||||||
|
/// This is O(1), resources are stored in HashMaps.
|
||||||
|
pub fn query_res<R>(&self) -> Option<&R>
|
||||||
|
where
|
||||||
|
R: Resource
|
||||||
|
{
|
||||||
|
self.resources
|
||||||
|
.get(&TypeId::of::<R>())
|
||||||
|
.map(|r| r.as_any().downcast_ref())
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query a resource mutably.
|
||||||
|
///
|
||||||
|
/// This will mark the resource as changed, even if it actually wasn't modified when mutably borrowed.
|
||||||
|
/// This is O(1), resources are stored in HashMaps.
|
||||||
|
pub fn query_res_mut<R>(&mut self) -> Option<&mut R>
|
||||||
|
where
|
||||||
|
R: Resource
|
||||||
|
{
|
||||||
|
self.resources
|
||||||
|
.get_mut(&TypeId::of::<R>())
|
||||||
|
.map(|r| {
|
||||||
|
// update resource
|
||||||
|
self.updated_resources.insert(TypeId::of::<R>());
|
||||||
|
|
||||||
|
r.as_any_mut().downcast_mut()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
}
|
73
src/game.rs
73
src/game.rs
|
@ -1,9 +1,10 @@
|
||||||
use std::{sync::Arc, cell::RefCell};
|
use std::{sync::Arc, cell::RefCell, borrow::Borrow, collections::VecDeque};
|
||||||
|
|
||||||
use async_std::{task::block_on, sync::Mutex};
|
use async_std::{task::block_on, sync::Mutex};
|
||||||
|
|
||||||
use hecs::World;
|
//use hecs::World;
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
|
use resources::{Resources, Resource};
|
||||||
use tracing::{metadata::LevelFilter, info, debug, warn, error};
|
use tracing::{metadata::LevelFilter, info, debug, warn, error};
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
layer::{Layer, SubscriberExt},
|
layer::{Layer, SubscriberExt},
|
||||||
|
@ -13,7 +14,12 @@ use tracing_subscriber::{
|
||||||
|
|
||||||
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}};
|
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}};
|
||||||
|
|
||||||
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SimpleSystem, SystemDispatcher}};
|
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SimpleSystem, SystemDispatcher, world::World}, input::Input};
|
||||||
|
|
||||||
|
pub struct Controls<'a> {
|
||||||
|
pub world: &'a mut World,
|
||||||
|
pub resources: &'a mut Resources,
|
||||||
|
}
|
||||||
|
|
||||||
struct TickCounter {
|
struct TickCounter {
|
||||||
counter: u32,
|
counter: u32,
|
||||||
|
@ -75,6 +81,7 @@ struct GameLoop {
|
||||||
renderer: Box<dyn Renderer>,
|
renderer: Box<dyn Renderer>,
|
||||||
|
|
||||||
world: Arc<Mutex<World>>,
|
world: Arc<Mutex<World>>,
|
||||||
|
resources: Arc<Mutex<Resources>>,
|
||||||
/// higher priority systems
|
/// higher priority systems
|
||||||
engine_sys_dispatcher: SystemDispatcher,
|
engine_sys_dispatcher: SystemDispatcher,
|
||||||
user_sys_dispatcher: SystemDispatcher,
|
user_sys_dispatcher: SystemDispatcher,
|
||||||
|
@ -82,12 +89,13 @@ struct GameLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameLoop {
|
impl GameLoop {
|
||||||
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, user_systems: SystemDispatcher) -> GameLoop {
|
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, resources: Arc<Mutex<Resources>>, user_systems: SystemDispatcher) -> GameLoop {
|
||||||
Self {
|
Self {
|
||||||
window: Arc::clone(&window),
|
window: Arc::clone(&window),
|
||||||
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
||||||
|
|
||||||
world,
|
world,
|
||||||
|
resources,
|
||||||
engine_sys_dispatcher: SystemDispatcher::new(),
|
engine_sys_dispatcher: SystemDispatcher::new(),
|
||||||
user_sys_dispatcher: user_systems,
|
user_sys_dispatcher: user_systems,
|
||||||
fps_counter: TickCounter::new(),
|
fps_counter: TickCounter::new(),
|
||||||
|
@ -104,21 +112,20 @@ impl GameLoop {
|
||||||
|
|
||||||
async fn update(&mut self) {
|
async fn update(&mut self) {
|
||||||
let mut world = self.world.lock().await;
|
let mut world = self.world.lock().await;
|
||||||
|
let mut resources = self.resources.lock().await;
|
||||||
|
|
||||||
if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut world) {
|
let mut controls = Controls {
|
||||||
|
world: &mut world,
|
||||||
|
resources: &mut resources
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut controls) {
|
||||||
error!("Error when executing engine ecs systems: '{}'", e);
|
error!("Error when executing engine ecs systems: '{}'", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = self.user_sys_dispatcher.execute_mut(&mut world) {
|
if let Err(e) = self.user_sys_dispatcher.execute_mut(&mut controls) {
|
||||||
error!("Error when executing user ecs systems: '{}'", e);
|
error!("Error when executing user ecs systems: '{}'", e);
|
||||||
}
|
}
|
||||||
/* for esystem in self.engine_systems.iter_mut() {
|
|
||||||
esystem.execute_mut(&mut world).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
for system in self.systems.iter_mut() {
|
|
||||||
system.execute_mut(&mut world).unwrap();
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
|
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
|
||||||
|
@ -138,7 +145,7 @@ impl GameLoop {
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Got unhandled input event: \"{:?}\"", event);
|
//debug!("Got unhandled input event: \"{:?}\"", event);
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -164,9 +171,27 @@ impl GameLoop {
|
||||||
ref event,
|
ref event,
|
||||||
window_id,
|
window_id,
|
||||||
} if window_id == self.window.id() => {
|
} if window_id == self.window.id() => {
|
||||||
|
|
||||||
// If try_from failed, that means that the WindowEvent is not an
|
// If try_from failed, that means that the WindowEvent is not an
|
||||||
// input related event.
|
// input related event.
|
||||||
if let Ok(input_event) = InputEvent::try_from(event) {
|
if let Ok(input_event) = InputEvent::try_from(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.
|
||||||
|
{
|
||||||
|
let mut world = self.world.lock().await;
|
||||||
|
let event_queue = match world.query_res_mut::<VecDeque<InputEvent>>() {
|
||||||
|
Some(i) => i,
|
||||||
|
None => {
|
||||||
|
world.insert_resource(VecDeque::<InputEvent>::new());
|
||||||
|
|
||||||
|
// must succeed since it was just added
|
||||||
|
world.query_res_mut::<VecDeque<InputEvent>>().unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
event_queue.push_back(input_event.clone());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(new_control) = self.input_update(&input_event).await {
|
if let Some(new_control) = self.input_update(&input_event).await {
|
||||||
*control_flow = new_control;
|
*control_flow = new_control;
|
||||||
}
|
}
|
||||||
|
@ -202,6 +227,7 @@ impl GameLoop {
|
||||||
|
|
||||||
let mut world = self.world.lock().await;
|
let mut world = self.world.lock().await;
|
||||||
self.renderer.as_mut().prepare(&mut world).await;
|
self.renderer.as_mut().prepare(&mut world).await;
|
||||||
|
world.clear_updated_resources();
|
||||||
drop(world);
|
drop(world);
|
||||||
|
|
||||||
match self.renderer.as_mut().render().await {
|
match self.renderer.as_mut().render().await {
|
||||||
|
@ -225,6 +251,7 @@ impl GameLoop {
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
world: Option<Arc<Mutex<World>>>,
|
world: Option<Arc<Mutex<World>>>,
|
||||||
|
resources: Option<Arc<Mutex<Resources>>>,
|
||||||
system_dispatcher: Option<SystemDispatcher>
|
system_dispatcher: Option<SystemDispatcher>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,6 +259,7 @@ impl Default for Game {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
world: None,
|
world: None,
|
||||||
|
resources: Some(Arc::new(Mutex::new(Resources::new()))),
|
||||||
system_dispatcher: Some(SystemDispatcher::new()),
|
system_dispatcher: Some(SystemDispatcher::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,15 +310,30 @@ impl Game {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn with_res<T>(&mut self, r: T) -> &mut Self
|
||||||
|
where
|
||||||
|
T: Resource
|
||||||
|
{
|
||||||
|
let resources = self.resources.as_mut().unwrap();
|
||||||
|
let mut resources = resources.lock().await;
|
||||||
|
|
||||||
|
resources.insert(r);
|
||||||
|
drop(resources);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) {
|
pub async fn run(&mut self) {
|
||||||
let world = self.world.take().expect("ECS World was never given to Game!");
|
let world = self.world.take().expect("ECS World was never given to Game!");
|
||||||
|
let resources = self.resources.take().unwrap();
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
||||||
|
|
||||||
let systems = self.system_dispatcher.take().unwrap();
|
let systems = self.system_dispatcher.take().unwrap();
|
||||||
|
|
||||||
let mut g_loop = GameLoop::new(Arc::clone(&window), world, systems).await;
|
let mut g_loop = GameLoop::new(Arc::clone(&window), world, resources, systems).await;
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
g_loop.run_sync(event, control_flow);
|
g_loop.run_sync(event, control_flow);
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
use std::{any::Any, collections::{HashMap, hash_map::DefaultHasher, VecDeque}, hash::{Hash, Hasher}, sync::{Arc, Mutex}};
|
||||||
|
|
||||||
|
use gilrs_core::Gilrs;
|
||||||
|
use tracing::warn;
|
||||||
|
use winit::event::{VirtualKeyCode, ElementState};
|
||||||
|
|
||||||
|
use crate::{ecs::{SimpleSystem, world::Resource}, input_event::InputEvent};
|
||||||
|
|
||||||
|
pub type KeyCode = winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum ButtonEvent<T: Hash + 'static> {
|
||||||
|
Pressed(T),
|
||||||
|
Released(T),
|
||||||
|
JustPressed(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InputSystem;
|
||||||
|
|
||||||
|
impl InputSystem {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleSystem for InputSystem {
|
||||||
|
fn execute_mut(&mut self, controls: &mut crate::game::Controls) -> anyhow::Result<()> {
|
||||||
|
let world = &mut controls.world;
|
||||||
|
|
||||||
|
if let Some(queue) = world.query_res_mut::<VecDeque<InputEvent>>() {
|
||||||
|
let event = queue.pop_front();
|
||||||
|
|
||||||
|
if let Some(input) = world.query_res_mut::<Input>() {
|
||||||
|
input.update(event.as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Input {
|
||||||
|
gilrs: Option<Arc<Mutex<Gilrs>>>, // TODO
|
||||||
|
// the key is a u64. This is a hash of the value stored inside of the Box
|
||||||
|
events: HashMap<u64, Box<dyn Any + Send + Sync>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for Input {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
let gilrs = match Gilrs::new() {
|
||||||
|
Ok(g) => Some(Arc::new(Mutex::new(g))),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failure to initialize gilrs, gamepads will not work!\n{}", e);
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
gilrs,
|
||||||
|
events: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, event: Option<&InputEvent>) -> bool {
|
||||||
|
// Convert JustPressed inputs to Pressed, and remove Released events
|
||||||
|
self.events.retain(|_, v| {
|
||||||
|
if let Some(ev) = v.downcast_mut::<ButtonEvent<KeyCode>>() {
|
||||||
|
match ev {
|
||||||
|
ButtonEvent::Released(_) => {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
ButtonEvent::JustPressed(v) => {
|
||||||
|
*ev = ButtonEvent::Pressed(*v);
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(event) = event {
|
||||||
|
match event {
|
||||||
|
InputEvent::KeyboardInput { input, .. } => {
|
||||||
|
if let Some(code) = input.virtual_keycode {
|
||||||
|
// Get a hash of the input code
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
code.hash(&mut hasher);
|
||||||
|
let code_hash = hasher.finish();
|
||||||
|
|
||||||
|
// convert state
|
||||||
|
let buttonev = Box::new(match input.state {
|
||||||
|
ElementState::Pressed => ButtonEvent::JustPressed(code),
|
||||||
|
ElementState::Released => ButtonEvent::Released(code),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.events.insert(code_hash, buttonev);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* WindowEvent::CursorMoved { position, .. } => Some(EventType::PositionChanged(position.x, position.y, InputEventCode::CursorMoved)),
|
||||||
|
WindowEvent::CursorEntered { .. } => Some(EventType::Signal(InputEventCode::CursorEnteredWindow)),
|
||||||
|
WindowEvent::CursorLeft { .. } => Some(EventType::Signal(InputEventCode::CursorLeftWindow)), */
|
||||||
|
//WindowEvent::MouseWheel { delta, .. } => Some(EventType::AxisChanged(delta, InputEventCode::MouseWheel)),
|
||||||
|
/* WindowEvent::MouseWheel { .. } => Some(InputEventCode::MouseWheel),
|
||||||
|
WindowEvent::MouseInput { .. } => Some(InputEventCode::MouseInput),
|
||||||
|
WindowEvent::TouchpadMagnify { .. } => Some(InputEventCode::TouchpadMagnify),
|
||||||
|
WindowEvent::SmartMagnify { .. } => Some(InputEventCode::SmartMagnify),
|
||||||
|
WindowEvent::TouchpadRotate { .. } => Some(InputEventCode::TouchpadRotate),
|
||||||
|
WindowEvent::TouchpadPressure { .. } => Some(InputEventCode::TouchpadPressure),
|
||||||
|
WindowEvent::AxisMotion { .. } => Some(InputEventCode::AxisMotion),
|
||||||
|
WindowEvent::Touch { .. } => Some(InputEventCode::Touch), */
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: was_just_pressed
|
||||||
|
|
||||||
|
pub fn is_pressed(&self, code: VirtualKeyCode) -> bool {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
code.hash(&mut hasher);
|
||||||
|
let inner_hash = hasher.finish();
|
||||||
|
|
||||||
|
if let Some(e) = self.events.get(&inner_hash) {
|
||||||
|
if let Some(ev) = e.downcast_ref::<ButtonEvent<KeyCode>>() {
|
||||||
|
match ev {
|
||||||
|
ButtonEvent::Pressed(v) => {
|
||||||
|
if v.clone() == code {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ButtonEvent::JustPressed(v) => {
|
||||||
|
if v.clone() == code {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ButtonEvent::Released(_) => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,22 @@
|
||||||
use winit::{event::{DeviceId, KeyboardInput, ModifiersState, MouseScrollDelta, TouchPhase, MouseButton, AxisId, Touch, WindowEvent, ElementState}, dpi::PhysicalPosition};
|
use winit::{event::{DeviceId, KeyboardInput, ModifiersState, MouseScrollDelta, TouchPhase, MouseButton, AxisId, Touch, WindowEvent, ElementState}, dpi::PhysicalPosition};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
use crate::ecs::world::Resource;
|
||||||
|
|
||||||
|
/// Wrapper around events from `winit::WindowEvent` that are specific to input related events.
|
||||||
|
///
|
||||||
|
/// The `winit::WindowEvent` enum has many values that are related to inputs.
|
||||||
|
/// Ex:
|
||||||
|
/// * winit::WindowEvent::KeyboardInput
|
||||||
|
/// * winit::WindowEvent::CursorMoved
|
||||||
|
/// * winit::WindowEvent::CursorEntered
|
||||||
|
/// * winit::WindowEvent::MouseWheel
|
||||||
|
/// * winit::WindowEvent::CursorLeft
|
||||||
|
/// * winit::WindowEvent::MouseInput
|
||||||
|
/// etc.
|
||||||
|
///
|
||||||
|
/// Its easier for these to all be in a single `InputEvent` type enum to check if any input was received.
|
||||||
|
/// The comments for all the methods were taken from `winit::WindowEvent`
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum InputEvent {
|
pub enum InputEvent {
|
||||||
/// An event from the keyboard has been received.
|
/// An event from the keyboard has been received.
|
||||||
KeyboardInput {
|
KeyboardInput {
|
||||||
|
@ -216,33 +232,12 @@ impl<'a> TryFrom<&'a WindowEvent<'a>> for InputEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* impl<'a> TryFrom<WindowEvent<'a>> for InputEvent {
|
impl Resource for InputEvent {
|
||||||
type Error = InputEventConversionError<'a>;
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn try_from(value: WindowEvent<'a>) -> Result<Self, Self::Error> {
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
match value {
|
self
|
||||||
WindowEvent::KeyboardInput { device_id, input, is_synthetic } =>
|
|
||||||
Ok(InputEvent::KeyboardInput { device_id, input, is_synthetic }),
|
|
||||||
WindowEvent::CursorMoved { device_id, position, modifiers } =>
|
|
||||||
Ok(InputEvent::CursorMoved { device_id, position, modifiers }),
|
|
||||||
WindowEvent::CursorEntered { device_id } => Ok(InputEvent::CursorEntered { device_id }),
|
|
||||||
WindowEvent::CursorLeft { device_id } => Ok(InputEvent::CursorLeft { device_id }),
|
|
||||||
WindowEvent::MouseWheel { device_id, delta, phase, modifiers } =>
|
|
||||||
Ok(InputEvent::MouseWheel { device_id, delta, phase, modifiers }),
|
|
||||||
WindowEvent::MouseInput { device_id, state, button, modifiers } =>
|
|
||||||
Ok(InputEvent::MouseInput { device_id, state, button, modifiers }),
|
|
||||||
WindowEvent::TouchpadMagnify { device_id, delta, phase } =>
|
|
||||||
Ok(InputEvent::TouchpadMagnify { device_id, delta, phase }),
|
|
||||||
WindowEvent::SmartMagnify { device_id } => Ok(InputEvent::SmartMagnify { device_id }),
|
|
||||||
WindowEvent::TouchpadRotate { device_id, delta, phase } =>
|
|
||||||
Ok(InputEvent::TouchpadRotate { device_id, delta, phase }),
|
|
||||||
WindowEvent::TouchpadPressure { device_id, pressure, stage } =>
|
|
||||||
Ok(InputEvent::TouchpadPressure { device_id, pressure, stage }),
|
|
||||||
WindowEvent::AxisMotion { device_id, axis, value } =>
|
|
||||||
Ok(InputEvent::AxisMotion { device_id, axis, value }),
|
|
||||||
WindowEvent::Touch(t) => Ok(InputEvent::Touch(t)),
|
|
||||||
|
|
||||||
_ => Err(InputEventConversionError::FromError(value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} */
|
|
38
src/main.rs
38
src/main.rs
|
@ -4,14 +4,15 @@ mod input_event;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod ecs;
|
mod ecs;
|
||||||
mod math;
|
mod math;
|
||||||
|
mod input;
|
||||||
|
|
||||||
use ecs::components::mesh::MeshComponent;
|
use ecs::components::mesh::MeshComponent;
|
||||||
|
|
||||||
use ecs::components::transform::TransformComponent;
|
use ecs::components::transform::TransformComponent;
|
||||||
use game::Game;
|
use game::Game;
|
||||||
use hecs::World;
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
|
use crate::ecs::world::World;
|
||||||
|
use crate::input::{Input, KeyCode, InputSystem};
|
||||||
use crate::render::material::Material;
|
use crate::render::material::Material;
|
||||||
use crate::render::texture::Texture;
|
use crate::render::texture::Texture;
|
||||||
use crate::ecs::components::camera::CameraComponent;
|
use crate::ecs::components::camera::CameraComponent;
|
||||||
|
@ -84,16 +85,44 @@ async fn main() {
|
||||||
camera.transform.translation += glam::Vec3::new(0.0, 0.0, 2.0);
|
camera.transform.translation += glam::Vec3::new(0.0, 0.0, 2.0);
|
||||||
//camera.transform.rotate_y(Angle::Degrees(-25.0));
|
//camera.transform.rotate_y(Angle::Degrees(-25.0));
|
||||||
camera.transform.rotate_z(Angle::Degrees(-90.0));
|
camera.transform.rotate_z(Angle::Degrees(-90.0));
|
||||||
|
world.insert_resource(Input::new());
|
||||||
world.spawn((camera,));
|
world.spawn((camera,));
|
||||||
|
|
||||||
let jiggle_system = |world: &mut World| -> anyhow::Result<()> {
|
let jiggle_system = |world: &mut World| -> anyhow::Result<()> {
|
||||||
|
let input: &Input = world.query_res().unwrap();
|
||||||
|
|
||||||
|
let speed = 0.001;
|
||||||
|
|
||||||
|
let mut dir_x = 0.0;
|
||||||
|
let mut dir_y = 0.0;
|
||||||
|
|
||||||
|
if input.is_pressed(KeyCode::A) {
|
||||||
|
dir_x -= speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.is_pressed(KeyCode::D) {
|
||||||
|
dir_x += speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.is_pressed(KeyCode::S) {
|
||||||
|
dir_y -= speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.is_pressed(KeyCode::W) {
|
||||||
|
dir_y += speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug!("dir: ({}, {})", dir_x, dir_y);
|
||||||
|
|
||||||
for (_eid, (transform, )) in world.query_mut::<(&mut TransformComponent,)>() {
|
for (_eid, (transform, )) in world.query_mut::<(&mut TransformComponent,)>() {
|
||||||
let t = &mut transform.transform;
|
let t = &mut transform.transform;
|
||||||
|
|
||||||
debug!("Translation: {}", t.translation);
|
/* debug!("Translation: {}", t.translation);
|
||||||
|
|
||||||
t.translation += glam::Vec3::new(0.0, 0.001, 0.0);
|
t.translation += glam::Vec3::new(0.0, 0.001, 0.0);
|
||||||
t.translation.x *= -1.0
|
t.translation.x *= -1.0 */
|
||||||
|
t.translation.x += dir_x;
|
||||||
|
t.translation.y += dir_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -102,5 +131,6 @@ async fn main() {
|
||||||
Game::initialize().await
|
Game::initialize().await
|
||||||
.with_world(world)
|
.with_world(world)
|
||||||
.with_system("jiggle", jiggle_system, &[])
|
.with_system("jiggle", jiggle_system, &[])
|
||||||
|
.with_system("input_system", InputSystem::new(), &[])
|
||||||
.run().await;
|
.run().await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,12 @@ use wgpu::{BindGroup, BindGroupLayout};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use hecs::{World, Entity};
|
use hecs::Entity;
|
||||||
|
|
||||||
use crate::ecs::components::camera::CameraComponent;
|
use crate::ecs::components::camera::CameraComponent;
|
||||||
use crate::ecs::components::mesh::MeshComponent;
|
use crate::ecs::components::mesh::MeshComponent;
|
||||||
use crate::ecs::components::transform::TransformComponent;
|
use crate::ecs::components::transform::TransformComponent;
|
||||||
|
use crate::ecs::world::World;
|
||||||
use crate::math::{Transform, Angle};
|
use crate::math::{Transform, Angle};
|
||||||
use crate::resources;
|
use crate::resources;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue