gsa/src/run.rs

223 lines
7.8 KiB
Rust

use crate::buttons::{button_from_gilrs, button_from_scancode};
use crate::gsa_render_to_screen::{render_to_screen, render_to_window};
use crate::tileset::load_tileset;
use crate::{Buttons, Gsa, Sprite, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH};
use gilrs::EventType;
use glam::IVec2;
use std::cmp::min;
use std::num::NonZeroU32;
use std::time::{Duration, Instant};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event;
use winit::event::{ElementState, Event, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
/// Creates main function, includes gfx.gif, and calls run
///
/// Pass the following two functions to it:
/// - fn init(gsa: &mut Gsa) -> TGame
/// - fn update(game: &mut Game, gsa: &mut Gsa)
///
/// TGame can be any type, usually a struct that contains your game's state
#[macro_export]
macro_rules! run {
($init: ident, $update: ident) => {
fn main() {
run($init, $update, include_bytes!("gfx.gif"));
}
};
}
/// This is called by [run!]
pub fn run<TGame: 'static>(
init_fn: fn(gsa: &mut Gsa) -> TGame,
update_fn: fn(game: &mut TGame, gsa: &mut Gsa),
image_data: &[u8],
) {
let (tileset, palette) = load_tileset(image_data);
let mut gsa = Gsa {
sprites: [Sprite::default(); MAX_SPRITES],
maps: Default::default(),
palette,
font: 0x1010,
pressed: 0,
released: 0,
down: 0,
};
let mut game = init_fn(&mut gsa);
/*let state = State::<TGame> {
gsa,
game,
tileset,
update_fn,
first: true,
gilrs: Gilrs::new().unwrap(),
};
*/
let event_loop = EventLoop::new();
let size = LogicalSize::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32);
let window = WindowBuilder::new()
.with_title("Game Skunk Advance v0.1")
.with_inner_size(size)
.with_min_inner_size(size)
.build(&event_loop)
.unwrap();
{
let monitor_size = window.primary_monitor().unwrap().size();
let scale_factor = 0.9;
let window_scale = ((monitor_size.width as f32 * scale_factor) as u32
/ SCREEN_WIDTH as u32)
.min((monitor_size.height as f32 * scale_factor) as u32 / SCREEN_HEIGHT as u32);
window.set_inner_size(PhysicalSize {
width: window_scale * SCREEN_WIDTH as u32,
height: window_scale * SCREEN_HEIGHT as u32,
});
let outer_size = window.outer_size();
window.set_outer_position(PhysicalPosition {
x: (monitor_size.width - outer_size.width) / 2,
y: (monitor_size.height - outer_size.height) / 2,
});
}
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
let mut frames_since_last_second = 0;
let mut last_second = Instant::now();
let mut last_frame = last_second;
let target_frame_duration = Duration::from_secs(1).div_f64(60.0);
let mut scale = 1usize;
let mut off_x = 0usize;
let mut off_y = 0usize;
let mut new_buttons = Buttons::default();
// in case button gets pressed and released within the same frame need to catch them
// seperately from the last-now comparison to catch it
let mut new_pressed = Buttons::default();
let mut new_released = Buttons::default();
let mut gilrs = gilrs::Gilrs::new().unwrap();
event_loop.run(move |event, _, control_flow| {
let scale = &mut scale;
let off_x = &mut off_x;
let off_y = &mut off_y;
let new_buttons = &mut new_buttons;
let new_pressed = &mut new_pressed;
let new_released = &mut new_released;
let gilrs = &mut gilrs;
if Instant::now() - last_second > Duration::from_secs(1) {
println!("fps: {}", frames_since_last_second);
frames_since_last_second = 0;
last_second += Duration::from_secs(1);
}
let mut screen_buffer = [0u8; SCREEN_WIDTH * SCREEN_HEIGHT];
match event {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
event::KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
},
..
} => {
*control_flow = ControlFlow::Exit;
}
Event::WindowEvent {
event: WindowEvent::KeyboardInput { input, .. },
..
} => {
let button = button_from_scancode(input.scancode);
match input.state {
ElementState::Pressed => {
*new_buttons |= button;
*new_pressed |= button;
}
ElementState::Released => {
*new_buttons &= !button;
*new_released |= button;
}
};
}
Event::MainEventsCleared {} => {
if Instant::now() - last_frame >= target_frame_duration {
//input
while let Some(event) = gilrs.next_event() {
match event.event {
EventType::ButtonPressed(button, ..) => {
let button = button_from_gilrs(button);
*new_buttons |= button;
*new_pressed |= button;
}
EventType::ButtonReleased(button, ..) => {
let button = button_from_gilrs(button);
*new_buttons &= !button;
*new_released |= button;
}
_ => {}
}
}
gsa.down = *new_buttons;
gsa.released = *new_released;
gsa.pressed = *new_pressed;
*new_released = 0;
*new_pressed = 0;
//update
update_fn(&mut game, &mut gsa);
//graphics
let size = window.inner_size();
surface
.resize(
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
)
.unwrap();
*scale = min(
size.width / SCREEN_WIDTH as u32,
size.height / SCREEN_HEIGHT as u32,
) as usize;
*off_x = (size.width as usize - SCREEN_WIDTH * *scale) / 2;
*off_y = (size.height as usize - SCREEN_HEIGHT * *scale) / 2;
let mut window_buffer = surface.buffer_mut().unwrap();
render_to_screen(&mut screen_buffer, &gsa, &tileset);
render_to_window(
&mut window_buffer,
&mut screen_buffer,
&palette,
IVec2 {
x: size.width as i32,
y: size.height as i32,
},
*scale,
*off_x,
*off_y,
);
window_buffer.present().unwrap();
frames_since_last_second += 1;
last_frame += target_frame_duration;
}
}
_ => {}
}
});
}