schule-pong/src/engine.rs

230 lines
8.6 KiB
Rust

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<KeyEventType, KeyHandlerFn>,
down_keys: HashSet<KeyCode>,
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<dyn MovingObject>) -> 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);
}
}