use crate::{BG, GAME_SIZE}; use self::objs::{obj_traits::MovingObject, World}; use std::{ collections::{HashMap, HashSet}, num::NonZeroU32, process, rc::Rc, time::{Duration, Instant}, }; use winit::{ dpi::PhysicalSize, event::{Event, KeyEvent, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, KeyCode, NamedKey, PhysicalKey}, raw_window_handle::{HasDisplayHandle, HasWindowHandle}, window::WindowBuilder, }; pub mod objs; mod render; mod timer; type RenderFn = fn(&mut render::RenderCtx<'_, '_>, &mut World, &timer::GameTimer); type KeyHandlerFn = fn(&mut World, &timer::GameTimer); // core game engine struct pub struct Engine { event_loop: EventLoop<()>, render_fn: RenderFn, resize_fn: RenderFn, kb_handlers: HashMap, down_keys: HashSet, world: World, } #[derive(PartialEq, Eq, Hash)] pub enum KeyEventType { KeyDown(KeyCode), KeyDownFrameUpdate(KeyCode), KeyUp(KeyCode), } impl Engine { pub fn new() -> Self { let event_loop = EventLoop::new().unwrap(); Self { event_loop, render_fn: |_, _, _| {}, resize_fn: |_, _, _| {}, kb_handlers: HashMap::new(), down_keys: HashSet::new(), world: World::new(), } } pub fn insert_into_world(&mut self, obj: Rc) -> usize { self.world.insert(obj) } // sets the render function for the game pub fn set_render_fn(mut self, f: RenderFn) -> Self { self.render_fn = f; self } // sets the resize render function for the game pub fn set_resize_fn(mut self, f: RenderFn) -> Self { self.resize_fn = f; self } pub fn register_handler(mut self, ev_type: KeyEventType, f: KeyHandlerFn) -> Self { self.kb_handlers.insert(ev_type, f); self } // runs the game and consumes self, this will finish the process pub fn run(self) -> ! { let Self { event_loop, mut world, render_fn, resize_fn, kb_handlers, mut down_keys, } = self; // set up window let window = WindowBuilder::new() .with_inner_size(winit::dpi::Size::Physical(PhysicalSize::new(1200, 700))) .build(&event_loop) .unwrap(); // set up softbuffer let context = softbuffer::Context::new(window.display_handle().unwrap()).unwrap(); let mut surface = softbuffer::Surface::new(&context, window.window_handle().unwrap()).unwrap(); let mut timer = timer::GameTimer::new(); event_loop .run(|event, elwt| { match event { // redraw Event::NewEvents(StartCause::ResumeTimeReached { .. }) => { window.request_redraw(); } Event::NewEvents(StartCause::WaitCancelled { start, requested_resume, }) => { elwt.set_control_flow(ControlFlow::WaitUntil(requested_resume.unwrap())); } Event::WindowEvent { window_id, event: WindowEvent::Resized(PhysicalSize { width, height }), } => { if window_id == window.id() { if let (Some(width), Some(height)) = (NonZeroU32::new(width), NonZeroU32::new(height)) { surface.resize(width, height).unwrap(); let mut buffer = surface.buffer_mut().unwrap(); buffer.fill(BG); let mut ctx = render::RenderCtx::new(buffer, (width, height), GAME_SIZE); resize_fn(&mut ctx, &mut world, &timer); } } } Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested, } => { // shoddy vsync elwt.set_control_flow(ControlFlow::WaitUntil( Instant::now() + Duration::from_millis(1000 / 60), )); if window_id == window.id() { if let (Some(width), Some(height)) = { let size = window.inner_size(); (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) } { for frame_handler in kb_handlers.iter().filter_map(|(ev, handler)| { if let KeyEventType::KeyDownFrameUpdate(keycode) = ev { if down_keys.contains(keycode) { Some(handler) } else { None } } else { None } }) { frame_handler(&mut world, &timer) } surface.resize(width, height).unwrap(); let buffer = surface.buffer_mut().unwrap(); let mut ctx = render::RenderCtx::new(buffer, (width, height), GAME_SIZE); // render render_fn(&mut ctx, &mut world, &timer); ctx.force_present(); timer.frame_update(); } } } // crash game to exit lol Event::WindowEvent { window_id: _window_id, event: WindowEvent::CloseRequested | WindowEvent::KeyboardInput { event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. }, .. }, } => todo!(), // potential future keyboard handling Event::WindowEvent { window_id: window_id, event: WindowEvent::KeyboardInput { device_id: _device_id, event: KeyEvent { physical_key, state, repeat, .. }, is_synthetic: _is_synthetic, }, } => { if !repeat { if let PhysicalKey::Code(keycode) = physical_key { let ev_type = match state { winit::event::ElementState::Pressed => { down_keys.insert(keycode); KeyEventType::KeyDown(keycode) } winit::event::ElementState::Released => { down_keys.remove(&keycode); KeyEventType::KeyUp(keycode) } }; if let Some(handler) = kb_handlers.get(&ev_type) { handler(&mut world, &timer) } } } } _ => {} } }) .unwrap(); process::exit(0); } }