From b17a7636d8daf649e63fc5f6f394afce937a8756 Mon Sep 17 00:00:00 2001 From: reo Date: Sat, 22 Nov 2025 14:15:53 +0300 Subject: [PATCH] Implement MenuSystem, reimplement the mouse grab and menu system, add a new common module in the game systems for common functions, various other fixes --- game/src/main.rs | 35 +++---- game/src/systems/common.rs | 34 +++++++ game/src/systems/debug_camera.rs | 95 +++++-------------- game/src/systems/keybinds.rs | 36 ++++--- .../systems/kinematic_character_controller.rs | 85 +++++++---------- game/src/systems/menu.rs | 81 ++++++++++++++++ game/src/systems/mod.rs | 3 + 7 files changed, 219 insertions(+), 150 deletions(-) create mode 100644 game/src/systems/common.rs create mode 100644 game/src/systems/menu.rs diff --git a/game/src/main.rs b/game/src/main.rs index 939e4dd..582e2d5 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -6,7 +6,8 @@ use rapier3d::dynamics::{CoefficientCombineRule, RigidBodyType}; use rapier3d::prelude::ColliderBuilder; use winit::event::{Event, WindowEvent}; use systems::debug_camera::FPSDebugCameraSystem; -use crate::systems::{KeybindsSystem, KinematicCharacterController, PhysicsSystem}; +use crate::systems::common::should_draw_menu; +use crate::systems::{KeybindsSystem, KinematicCharacterController, MenuSystem, PhysicsSystem}; const TEST_GLTF: &str = "sphere.glb"; const PLANE_GLTF: &str = "plane.glb"; @@ -96,22 +97,23 @@ impl System for MainSystem { InputState, )>().unwrap(); - let mut egui_queue = pctx.egui_queue.borrow_mut(); - let time_ctx = pctx.time_ctx.clone(); - let mut character_pos = Vec3::ZERO; - for (_ent, (tr, ch_component)) in scene.world.query::<(&Transform, &CharacterBodyComponent)>().iter() { - character_pos = tr.translation; - } - egui_queue.queue(move |egui_ctx| { - egui::Window::new("Debug").show(egui_ctx, |ui| { - ui.label("Hello World!"); - ui.label(format!("Frame Delta: {}", time_ctx.frame_dt)); - ui.label(format!("Fixed Delta: {}", time_ctx.fixed_dt)); - ui.label(format!("FPS: {}", 1.0 / time_ctx.frame_dt)); - ui.label(format!("Character POS: {}", character_pos)); + if should_draw_menu(scene) { + let mut egui_queue = pctx.egui_queue.borrow_mut(); + let time_ctx = pctx.time_ctx.clone(); + let mut character_pos = Vec3::ZERO; + for (_ent, (tr, ch_component)) in scene.world.query::<(&Transform, &CharacterBodyComponent)>().iter() { + character_pos = tr.translation; + } + egui_queue.queue(move |egui_ctx| { + egui::Window::new("Debug").show(egui_ctx, |ui| { + ui.label("Hello World!"); + ui.label(format!("Frame Delta: {}", time_ctx.frame_dt)); + ui.label(format!("Fixed Delta: {}", time_ctx.fixed_dt)); + ui.label(format!("FPS: {}", 1.0 / time_ctx.frame_dt)); + ui.label(format!("Character POS: {}", character_pos)); + }); }); - }); - + } } } @@ -121,6 +123,7 @@ fn main() { .add_system::() .add_system::() .add_system::() + .add_system::() .add_system::() .add_system::() .add_scene(MAIN_SCENE_ID, Scene::new(MAIN_SCENE_ID.to_owned(), None)) diff --git a/game/src/systems/common.rs b/game/src/systems/common.rs new file mode 100644 index 0000000..8863f2e --- /dev/null +++ b/game/src/systems/common.rs @@ -0,0 +1,34 @@ +use glam::Vec3; +use raidillon_app::prelude::*; +use crate::systems::menu::MenuState; + +pub fn is_camera_mode_valid(scene: &mut Scene, mode: CameraMode) -> bool { + let mut q = scene.world.query::<(&Camera, &CameraMode)>(); + let (cam_ent, (cam, cam_mode)) = q + .iter() + .next() + .unwrap(); + *cam_mode == mode +} + +pub fn is_mouse_look_enabled(scene: &mut Scene) -> bool { + let mut q = scene.world.query::<(&MenuState)>(); + let (_ent, mode) = q.iter().next().unwrap(); + *mode == MenuState::Closed +} + +pub fn camera_front(yaw: f32, pitch: f32) -> Vec3 { + let yaw_rad = yaw.to_radians(); + let pitch_rad = pitch.to_radians(); + Vec3::new( + yaw_rad.cos() * pitch_rad.cos(), + pitch_rad.sin(), + yaw_rad.sin() * pitch_rad.cos(), + ).normalize() +} + +pub fn should_draw_menu(scene: &mut Scene) -> bool { + let mut q = scene.world.query::<(&MenuState)>(); + let (_ent, mode) = q.iter().next().unwrap(); + *mode == MenuState::Open +} diff --git a/game/src/systems/debug_camera.rs b/game/src/systems/debug_camera.rs index 959af5c..b687e08 100644 --- a/game/src/systems/debug_camera.rs +++ b/game/src/systems/debug_camera.rs @@ -5,9 +5,11 @@ use winit::keyboard::{KeyCode, PhysicalKey}; use winit::window::CursorGrabMode; use raidillon_app::prelude::*; +use crate::systems::common::{camera_front, is_camera_mode_valid, is_mouse_look_enabled}; +use crate::systems::menu::MenuState; + pub struct FPSDebugCameraSystem { mouse_delta: (f64, f64), - mouse_enabled: bool, position: Vec3, yaw: f32, pitch: f32, @@ -19,7 +21,6 @@ impl Default for FPSDebugCameraSystem { fn default() -> Self { Self { mouse_delta: Default::default(), - mouse_enabled: Default::default(), position: Vec3::new(0.0, 0.0, 2.0), yaw: -90.0, pitch: 0.0, @@ -31,7 +32,7 @@ impl Default for FPSDebugCameraSystem { impl System for FPSDebugCameraSystem { fn handle_event(&mut self, res: &mut EngineResources, scene: &mut Scene) { - if !self.is_camera_mode_valid(scene) { + if !(is_camera_mode_valid(scene, CameraMode::Debug) && is_mouse_look_enabled(scene)) { return } let pctx = res.get::().unwrap(); @@ -46,89 +47,43 @@ impl System for FPSDebugCameraSystem { _ => {} } }, - Event::WindowEvent { event, .. } => match event { - WindowEvent::MouseInput { state, button, .. } => { - if button == MouseButton::Right { - // blood and tear - let window = pctx.window.lock().unwrap(); - match state { - ElementState::Pressed => { - if window - .set_cursor_grab(CursorGrabMode::Confined) - .or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)) - .is_ok() - { - window.set_cursor_visible(false); - self.mouse_enabled = true; - } - } - ElementState::Released => { - let _ = window.set_cursor_grab(CursorGrabMode::None); - window.set_cursor_visible(true); - self.mouse_enabled = false; - } - } - } - } - _ => {}, - }, _ => {}, } } fn frame_update(&mut self, res: &mut EngineResources, scene: &mut Scene) { - if !self.is_camera_mode_valid(scene) { - return - } let (pctx, input) = res.get_many::<(PlatformContext, InputState)>().unwrap(); - if self.mouse_enabled { + if is_mouse_look_enabled(scene) { self.yaw += self.mouse_delta.0 as f32 * self.sensitivity; self.pitch -= self.mouse_delta.1 as f32 * self.sensitivity; self.pitch = self.pitch.clamp(-89.0, 89.0); } - let front = self.front(); + let front = camera_front(self.yaw, self.pitch); let right_vec = front.cross(Vec3::Y).normalize(); - if input.key_held(KeyCode::KeyW) { - self.position += front * pctx.time_ctx.frame_dt * self.speed; - } - if input.key_held(KeyCode::KeyS) { - self.position -= front * pctx.time_ctx.frame_dt * self.speed; - } - if input.key_held(KeyCode::KeyA) { - self.position -= right_vec * pctx.time_ctx.frame_dt * self.speed; - } - if input.key_held(KeyCode::KeyD) { - self.position += right_vec * pctx.time_ctx.frame_dt * self.speed; + if is_mouse_look_enabled(scene) { + if input.key_held(KeyCode::KeyW) { + self.position += front * pctx.time_ctx.frame_dt * self.speed; + } + if input.key_held(KeyCode::KeyS) { + self.position -= front * pctx.time_ctx.frame_dt * self.speed; + } + if input.key_held(KeyCode::KeyA) { + self.position -= right_vec * pctx.time_ctx.frame_dt * self.speed; + } + if input.key_held(KeyCode::KeyD) { + self.position += right_vec * pctx.time_ctx.frame_dt * self.speed; + } } - scene.world.query_mut::<&mut Camera>().into_iter().for_each(|(_, camera)| { - camera.eye = self.position; - camera.center = self.position + front; - }); + if is_camera_mode_valid(scene, CameraMode::Debug) { + scene.world.query_mut::<&mut Camera>().into_iter().for_each(|(_, camera)| { + camera.eye = self.position; + camera.center = self.position + front; + }); + } self.mouse_delta = (0.0, 0.0); } } - -impl FPSDebugCameraSystem { - pub fn front(&self) -> Vec3 { - let yaw_rad = self.yaw.to_radians(); - let pitch_rad = self.pitch.to_radians(); - Vec3::new( - yaw_rad.cos() * pitch_rad.cos(), - pitch_rad.sin(), - yaw_rad.sin() * pitch_rad.cos(), - ).normalize() - } - - fn is_camera_mode_valid(&self, scene: &mut Scene) -> bool { - let mut q = scene.world.query::<(&Camera, &CameraMode)>(); - let (cam_ent, (cam, cam_mode)) = q - .iter() - .next() - .unwrap(); - *cam_mode == CameraMode::Debug - } -} diff --git a/game/src/systems/keybinds.rs b/game/src/systems/keybinds.rs index d8df7b7..5519969 100644 --- a/game/src/systems/keybinds.rs +++ b/game/src/systems/keybinds.rs @@ -1,6 +1,8 @@ use winit::keyboard::KeyCode; use raidillon_app::prelude::*; +use crate::systems::{common::should_draw_menu, menu::MenuState}; + #[derive(Default)] pub struct KeybindsSystem { camera_toggle_held: bool, @@ -19,21 +21,31 @@ impl System for KeybindsSystem { } fn frame_update(&mut self, res: &mut EngineResources, scene: &mut Scene) { - let pctx = res.get_mut::().unwrap(); - let mut q = scene.world.query::<(&Camera, &CameraMode)>(); - let (cam_ent, (cam, cam_mode)) = q - .iter() - .next() - .unwrap(); + if should_draw_menu(scene) { + let pctx = res.get_mut::().unwrap(); + let mut q = scene.world.query::<(&Camera, &CameraMode)>(); + let (cam_ent, (cam, cam_mode)) = q + .iter() + .next() + .unwrap(); + let cam_mode_str = format!("Camera Mode: {:?}", cam_mode); - let cam_mode_str = format!("Camera Mode: {:?}", cam_mode); + let mut q = scene.world.query::<(&MenuState)>(); + let (_ent, menu_state) = q + .iter() + .next() + .unwrap(); - pctx.egui_queue.borrow_mut().queue(move |egui_ctx| { - egui::Window::new("Camera").show(egui_ctx, |ui| { - ui.label("F5 to switch camera"); - ui.label(cam_mode_str); + let menu_state_str = format!("Menu State: {:?}", menu_state); + + pctx.egui_queue.borrow_mut().queue(move |egui_ctx| { + egui::Window::new("Camera").show(egui_ctx, |ui| { + ui.label("F5 to switch camera"); + ui.label(cam_mode_str); + ui.label(menu_state_str) + }); }); - }); + } } } diff --git a/game/src/systems/kinematic_character_controller.rs b/game/src/systems/kinematic_character_controller.rs index 34de5b4..41561a5 100644 --- a/game/src/systems/kinematic_character_controller.rs +++ b/game/src/systems/kinematic_character_controller.rs @@ -8,6 +8,9 @@ use winit::event::Event; use winit::keyboard::KeyCode; use raidillon_app::prelude::*; +use crate::systems::common::{camera_front, is_camera_mode_valid, is_mouse_look_enabled}; +use crate::systems::menu::MenuState; + #[derive(Default)] pub struct KinematicCharacterController { character_controller: RapierKinematicCharacterController, @@ -57,7 +60,7 @@ impl System for KinematicCharacterController { } fn handle_event(&mut self, res: &mut EngineResources, scene: &mut Scene) { - if !self.is_camera_mode_valid(scene) { + if !is_camera_mode_valid(scene, CameraMode::Kinematic) { return } @@ -78,45 +81,44 @@ impl System for KinematicCharacterController { } fn frame_update(&mut self, res: &mut EngineResources, scene: &mut Scene) { - if !self.is_camera_mode_valid(scene) { - return - } - let (pctx, input) = res.get_many::<(PlatformContext, InputState)>().unwrap(); - - self.yaw += (self.mouse_delta.0 as f32) * self.sensitivity; - self.pitch -= (self.mouse_delta.1 as f32) * self.sensitivity; - self.pitch = self.pitch.clamp(-89.0, 89.0); - - let front = self.front(); + let front = camera_front(self.yaw, self.pitch); let right_vec = front.cross(Vec3::Y).normalize(); - if input.key_held(KeyCode::KeyW) { - self.desired_movement += front * pctx.time_ctx.frame_dt * self.speed; - } - if input.key_held(KeyCode::KeyS) { - self.desired_movement -= front * pctx.time_ctx.frame_dt * self.speed; - } - if input.key_held(KeyCode::KeyA) { - self.desired_movement -= right_vec * pctx.time_ctx.frame_dt * self.speed; - } - if input.key_held(KeyCode::KeyD) { - self.desired_movement += right_vec * pctx.time_ctx.frame_dt * self.speed; + if is_camera_mode_valid(scene, CameraMode::Kinematic) && is_mouse_look_enabled(scene) { + let (pctx, input) = res.get_many::<(PlatformContext, InputState)>().unwrap(); + + self.yaw += (self.mouse_delta.0 as f32) * self.sensitivity; + self.pitch -= (self.mouse_delta.1 as f32) * self.sensitivity; + self.pitch = self.pitch.clamp(-89.0, 89.0); + + if input.key_held(KeyCode::KeyW) { + self.desired_movement += front * pctx.time_ctx.frame_dt * self.speed; + } + if input.key_held(KeyCode::KeyS) { + self.desired_movement -= front * pctx.time_ctx.frame_dt * self.speed; + } + if input.key_held(KeyCode::KeyA) { + self.desired_movement -= right_vec * pctx.time_ctx.frame_dt * self.speed; + } + if input.key_held(KeyCode::KeyD) { + self.desired_movement += right_vec * pctx.time_ctx.frame_dt * self.speed; + } } - let pos = Physics::rapier_translation_to_glam(&self.last_position); + if is_camera_mode_valid(scene, CameraMode::Kinematic) { + let pos = Physics::rapier_translation_to_glam(&self.last_position); + + scene.world.query_mut::<&mut Camera>().into_iter().for_each(|(_, camera)| { + // INTERPOLATION NEEDED. + camera.eye = pos; + camera.center = pos + front; + }); + } - scene.world.query_mut::<&mut Camera>().into_iter().for_each(|(_, camera)| { - // INTERPOLATION NEEDED. - camera.eye = pos; - camera.center = pos + front; - }); self.mouse_delta = (0.0, 0.0); } fn fixed_update(&mut self, res: &mut EngineResources, scene: &mut Scene) { - if !self.is_camera_mode_valid(scene) { - return - } let p = scene.resources.get_mut::().unwrap(); let (pctx, input) = res.get_many::<(PlatformContext, InputState)>().unwrap(); @@ -166,24 +168,3 @@ impl System for KinematicCharacterController { self.desired_movement = Vec3::ZERO; } } - -impl KinematicCharacterController { - pub fn front(&self) -> Vec3 { - let yaw_rad = self.yaw.to_radians(); - let pitch_rad = self.pitch.to_radians(); - Vec3::new( - yaw_rad.cos() * pitch_rad.cos(), - pitch_rad.sin(), - yaw_rad.sin() * pitch_rad.cos(), - ).normalize() - } - - fn is_camera_mode_valid(&self, scene: &mut Scene) -> bool { - let mut q = scene.world.query::<(&Camera, &CameraMode)>(); - let (cam_ent, (cam, cam_mode)) = q - .iter() - .next() - .unwrap(); - *cam_mode == CameraMode::Kinematic - } -} diff --git a/game/src/systems/menu.rs b/game/src/systems/menu.rs new file mode 100644 index 0000000..60350b7 --- /dev/null +++ b/game/src/systems/menu.rs @@ -0,0 +1,81 @@ +use egui::Id; +use raidillon_app::prelude::*; +use winit::{dpi::{LogicalPosition, Position}, keyboard::KeyCode, window::CursorGrabMode}; + +#[derive(Default)] +pub struct MenuSystem { + escape_key_held: bool, + /// Unoptimal solution to fix windows event delay + times_ran_initial_win_event: u32, +} + +#[derive(Default, Eq, PartialEq, Debug)] +pub enum MenuState { + Open, + #[default] + Closed, +} + +impl System for MenuSystem { + fn load_world(&mut self, res: &mut EngineResources, scene: &mut Scene) { + scene.world.spawn((MenuState::Closed,)); + } + + fn frame_update(&mut self, res: &mut EngineResources, scene: &mut Scene) { + // Windows won't register some events in the first miliseconds after initialization + if self.times_ran_initial_win_event < 20 { + let window = res.get::().unwrap().window.lock().unwrap(); + window.set_cursor_grab(CursorGrabMode::Confined).or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)); + window.set_cursor_visible(false); + self.times_ran_initial_win_event += 1 + } + + let mut egui_queue = res.get::().unwrap().egui_queue.borrow_mut(); + egui_queue.queue(|egui_ctx| { + egui::Area::new(Id::new("esc to pause")) + .anchor(egui::Align2::RIGHT_TOP, [-10.0, 10.0]) + .show(egui_ctx, |ui| { + ui.label( + egui::RichText::new("ESC to pause").size(24.0).color(egui::Color32::BLACK) + ); + }); + }); + } + + fn handle_event(&mut self, res: &mut EngineResources, scene: &mut Scene) { + // The menu is toggled by pressing the escape key + let input = res.get::().unwrap(); + if input.key_held(KeyCode::Escape) { + if self.escape_key_held { return } + self.toggle_menu(res, scene); + self.escape_key_held = true; + } else { + self.escape_key_held = false; + } + } +} + +impl MenuSystem { + fn toggle_menu(&mut self, res: &mut EngineResources, scene: &mut Scene) { + let q = scene.world.query_mut::<(&mut MenuState)>(); + let (menu_ent, menu_state) = q + .into_iter() + .next() + .unwrap(); + + let window = res.get::().unwrap().window.lock().unwrap(); + + match *menu_state { + MenuState::Open => { + *menu_state = MenuState::Closed; + window.set_cursor_grab(CursorGrabMode::Confined).or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)); + window.set_cursor_visible(false); + }, + MenuState::Closed => { + *menu_state = MenuState::Open; + window.set_cursor_grab(CursorGrabMode::None); + window.set_cursor_visible(true); + }, + } + } +} diff --git a/game/src/systems/mod.rs b/game/src/systems/mod.rs index 2a387b1..2f23ed5 100644 --- a/game/src/systems/mod.rs +++ b/game/src/systems/mod.rs @@ -1,8 +1,11 @@ mod physics; mod kinematic_character_controller; mod keybinds; +mod menu; pub mod debug_camera; +pub mod common; pub use physics::PhysicsSystem; pub use kinematic_character_controller::KinematicCharacterController; pub use keybinds::KeybindsSystem; +pub use menu::MenuSystem;