223 lines
7.8 KiB
Rust
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;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
});
|
|
}
|