use self::objs::{obj_traits::MovingObject, World}; use std::{ num::NonZeroU32, process, rc::Rc, time::{Duration, Instant}, }; use winit::{ dpi::PhysicalSize, event::{Event, KeyEvent, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, NamedKey}, raw_window_handle::{HasDisplayHandle, HasWindowHandle}, window::WindowBuilder, }; pub mod objs; mod render; mod timer; const GAME_SIZE: (u32, u32) = (1200, 800); const BORDER_WIDTH: u32 = 10; const FG: u32 = 0xebdbb2; const BG: u32 = 0x282828; type RenderFn = fn(&mut render::RenderCtx<'_, '_>, &mut World, &timer::GameTimer); // core game engine struct pub struct Engine { event_loop: EventLoop<()>, render_fn: RenderFn, world: World, } impl Engine { pub fn new() -> Self { let event_loop = EventLoop::new().unwrap(); Self { event_loop, render_fn: |_, _, _| {}, 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 } // runs the game and consumes self, this will finish the process pub fn run(self) -> ! { let Self { event_loop, mut world, render_fn, } = 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| { // shoddy vsync elwt.set_control_flow(ControlFlow::WaitUntil( Instant::now() + Duration::from_millis(1000 / 60), )); match event { // redraw Event::NewEvents(StartCause::ResumeTimeReached { .. }) => { window.request_redraw(); } 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); // top ctx.rect(0, 0, GAME_SIZE.0, BORDER_WIDTH, FG); // left ctx.rect( 0, BORDER_WIDTH, BORDER_WIDTH, GAME_SIZE.1 - BORDER_WIDTH * 2, FG, ); // right ctx.rect( GAME_SIZE.0 - BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH, GAME_SIZE.1 - BORDER_WIDTH * 2, FG, ); // bottom ctx.rect(0, GAME_SIZE.1 - BORDER_WIDTH, GAME_SIZE.0, 10, FG); } } } Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested, } => { if window_id == window.id() { if let (Some(width), Some(height)) = { let size = window.inner_size(); (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) } { 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: _event, is_synthetic: _is_synthetic, }, } => {} _ => {} } }) .unwrap(); process::exit(0); } }