309 lines
9.7 KiB
Rust
309 lines
9.7 KiB
Rust
#[windows_subsystem = "windows"]
|
|
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
use log::error;
|
|
use pixels::{Error, SurfaceTexture, PixelsBuilder};
|
|
use pixels::wgpu::BackendBit;
|
|
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalSize};
|
|
use winit::event::{Event, VirtualKeyCode};
|
|
use winit::event_loop::{ControlFlow, EventLoop};
|
|
use winit_input_helper::WinitInputHelper;
|
|
|
|
use peachy::Game;
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct Image {
|
|
pixels: [u8; 64]
|
|
}
|
|
|
|
struct State {
|
|
game: Game,
|
|
player_position: (u8, u8),
|
|
player_avatar: Image,
|
|
palette: [[u8; 4]; 4],
|
|
current_music: Option<String>,
|
|
music: HashMap<String, rodio::Sink>,
|
|
}
|
|
|
|
impl State {
|
|
fn draw(&self, screen: &mut [u8]) {
|
|
// clear screen
|
|
for pixel in screen.chunks_exact_mut(4) {
|
|
pixel.copy_from_slice(&self.palette[0]);
|
|
}
|
|
|
|
let (player_x, player_y) = self.player_position;
|
|
|
|
// each row of player avatar
|
|
for (tile_y, row) in self.player_avatar.pixels.chunks(8).enumerate() {
|
|
for (tile_x, pixel) in row.iter().enumerate() {
|
|
let colour = self.palette[*pixel as usize];
|
|
|
|
for (v, value) in colour.iter().enumerate() {
|
|
screen[
|
|
(
|
|
// player vertical offset (number of lines above)
|
|
(player_y as usize * 8 * (8 * self.game.config.width as usize))
|
|
+
|
|
// player horizontal offset; number of pixels to the left
|
|
(player_x as usize * 8)
|
|
+
|
|
// tile vertical offset; number of lines within tile
|
|
(tile_y as usize * (8 * self.game.config.width as usize))
|
|
+
|
|
(tile_x as usize) // tile horizontal offset
|
|
)
|
|
* 4 // we're dealing with rgba values so multiply everything by 4
|
|
+ v // value offset: which of the rgba values?
|
|
] = value.clone();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// fn update(&self) {
|
|
//
|
|
// }
|
|
}
|
|
|
|
fn main() -> Result<(), Error> {
|
|
env_logger::init();
|
|
|
|
let path = PathBuf::from("src/test-resources/basic");
|
|
let game = peachy::Game::from_dir(path).unwrap();
|
|
|
|
let mut state = State {
|
|
game,
|
|
player_position: (8, 4),
|
|
player_avatar: Image { pixels: [
|
|
0,0,0,2,2,0,0,0,
|
|
0,0,0,2,2,0,0,0,
|
|
0,0,1,1,1,0,2,0,
|
|
0,1,1,1,1,1,0,0,
|
|
2,0,1,1,1,0,0,0,
|
|
0,0,3,3,3,0,0,0,
|
|
0,0,3,0,3,0,0,0,
|
|
0,0,3,0,3,0,0,0,
|
|
]},
|
|
palette: [
|
|
[0xff, 0x7f, 0x7f, 0xff],
|
|
[0xff, 0xb2, 0x7f, 0xff],
|
|
[0xff, 0xe9, 0x7f, 0xff],
|
|
[0x00, 0x7f, 0x7f, 0x46],
|
|
],
|
|
current_music: None,
|
|
music: HashMap::new(),
|
|
};
|
|
|
|
let event_loop = EventLoop::new();
|
|
let mut input = WinitInputHelper::new();
|
|
|
|
let (window, p_width, p_height, mut _hidpi_factor) = create_window(
|
|
"pixels test",
|
|
(state.game.config.width * 8) as f64,
|
|
(state.game.config.height * 8) as f64,
|
|
&event_loop
|
|
);
|
|
|
|
let surface_texture = SurfaceTexture::new(p_width, p_height, &window);
|
|
|
|
let mut pixels = PixelsBuilder::new(
|
|
(state.game.config.width * 8) as u32,
|
|
(state.game.config.height * 8) as u32,
|
|
surface_texture
|
|
)
|
|
.wgpu_backend(BackendBit::GL | BackendBit::PRIMARY)
|
|
.enable_vsync(false)
|
|
.build()?;
|
|
|
|
// let device = rodio::default_output_device().unwrap();
|
|
//
|
|
// let source = rodio_xm::XMSource::from_bytes(
|
|
// include_bytes!("../ninety degrees.xm")
|
|
// );
|
|
//
|
|
// let sink = rodio::Sink::new(&device);
|
|
// sink.append(source);
|
|
// sink.pause();
|
|
//
|
|
// game.music.insert(":ninety degrees".into(), sink);
|
|
//
|
|
// let source = rodio_xm::XMSource::from_bytes(
|
|
// include_bytes!("../orn_keygentheme2001.xm")
|
|
// );
|
|
//
|
|
// let sink = rodio::Sink::new(&device);
|
|
// sink.append(source);
|
|
// sink.pause();
|
|
//
|
|
// game.music.insert("orn_keygentheme2001".into(), sink);
|
|
|
|
state.current_music = None;
|
|
|
|
event_loop.run(move |event, _, control_flow| {
|
|
// The one and only event that winit_input_helper doesn't have for us...
|
|
if let Event::RedrawRequested(_) = event {
|
|
state.draw(pixels.get_frame());
|
|
|
|
if pixels
|
|
.render()
|
|
.map_err(|e| error!("pixels.render() failed: {:?}", e))
|
|
.is_err()
|
|
{
|
|
*control_flow = ControlFlow::Exit;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// For everything else, for let winit_input_helper collect events to build its state.
|
|
// It returns `true` when it is time to update our game state and request a redraw.
|
|
if input.update(&event) {
|
|
// Close events
|
|
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
|
|
*control_flow = ControlFlow::Exit;
|
|
return;
|
|
}
|
|
|
|
if input.key_pressed(VirtualKeyCode::M) {
|
|
// pause the current tune
|
|
if state.current_music.is_some() {
|
|
state.music.get(state.current_music.as_ref().unwrap()).unwrap().pause();
|
|
}
|
|
|
|
if state.current_music.is_none() || state.current_music.as_ref().unwrap() == "orn_keygentheme2001" {
|
|
// play the first tune
|
|
state.current_music = Some(":ninety degrees".into());
|
|
state.music.get(state.current_music.as_ref().unwrap()).unwrap().play();
|
|
} else {
|
|
// play the second tune
|
|
state.current_music = Some("orn_keygentheme2001".into());
|
|
state.music.get(state.current_music.as_ref().unwrap()).unwrap().play();
|
|
}
|
|
}
|
|
|
|
if input.key_pressed(VirtualKeyCode::Left) {
|
|
let (x, y) = state.player_position;
|
|
if x > 0 {
|
|
state.player_position = (x - 1, y);
|
|
window.request_redraw();
|
|
}
|
|
}
|
|
|
|
if input.key_pressed(VirtualKeyCode::Right) {
|
|
let (x, y) = state.player_position;
|
|
if x < state.game.config.width as u8 - 1 {
|
|
state.player_position = (x + 1, y);
|
|
window.request_redraw();
|
|
}
|
|
}
|
|
|
|
if input.key_pressed(VirtualKeyCode::Up) {
|
|
let (x, y) = state.player_position;
|
|
if y > 0 {
|
|
state.player_position = (x, y - 1);
|
|
window.request_redraw();
|
|
}
|
|
}
|
|
|
|
if input.key_pressed(VirtualKeyCode::Down) {
|
|
let (x, y) = state.player_position;
|
|
if y < state.game.config.height as u8 - 1 {
|
|
state.player_position = (x, y + 1);
|
|
window.request_redraw();
|
|
}
|
|
}
|
|
|
|
// Adjust high DPI factor
|
|
if let Some(factor) = input.scale_factor_changed() {
|
|
_hidpi_factor = factor;
|
|
window.request_redraw();
|
|
}
|
|
|
|
// Resize the window
|
|
if let Some(size) = input.window_resized() {
|
|
pixels.resize_surface(size.width, size.height);
|
|
window.request_redraw();
|
|
}
|
|
|
|
*control_flow = ControlFlow::Wait;
|
|
}
|
|
});
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
fn window_builder(title: &str, event_loop: &EventLoop<()>) -> winit::window::Window {
|
|
winit::window::WindowBuilder::new()
|
|
.with_visible(false)
|
|
.with_title(title).build(&event_loop).unwrap()
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn window_builder(title: &str, event_loop: &EventLoop<()>) -> winit::window::Window {
|
|
use winit::platform::windows::WindowBuilderExtWindows;
|
|
|
|
winit::window::WindowBuilder::new()
|
|
.with_drag_and_drop(false)
|
|
.with_visible(false)
|
|
.with_title(title).build(&event_loop).unwrap()
|
|
}
|
|
|
|
/// Create a window for the game.
|
|
///
|
|
/// Automatically scales the window to cover about 2/3 of the monitor height.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Tuple of `(window, surface, width, height, hidpi_factor)`
|
|
/// `width` and `height` are in `PhysicalSize` units.
|
|
fn create_window(
|
|
title: &str,
|
|
width: f64,
|
|
height: f64,
|
|
event_loop: &EventLoop<()>,
|
|
) -> (winit::window::Window, u32, u32, f64) {
|
|
let window = window_builder(title, event_loop);
|
|
|
|
let hidpi_factor = window.scale_factor();
|
|
|
|
// Get dimensions
|
|
|
|
let (monitor_width, monitor_height) = {
|
|
if let Some(monitor) = window.current_monitor() {
|
|
let size = monitor.size().to_logical(hidpi_factor);
|
|
(size.width, size.height)
|
|
} else {
|
|
(width, height)
|
|
}
|
|
};
|
|
|
|
let scale = (monitor_height / height * 2.0 / 3.0).round().max(1.0);
|
|
|
|
// Resize, center, and display the window
|
|
let min_size: winit::dpi::LogicalSize<f64> =
|
|
PhysicalSize::new(width, height).to_logical(hidpi_factor);
|
|
|
|
let default_size =
|
|
LogicalSize::new(width * scale, height * scale);
|
|
|
|
let center = LogicalPosition::new(
|
|
(monitor_width - width * scale) / 2.0,
|
|
(monitor_height - height * scale) / 2.0,
|
|
);
|
|
|
|
window.set_inner_size(default_size);
|
|
// window.set_min_inner_size(Some(min_size));
|
|
window.set_outer_position(center);
|
|
window.set_visible(true);
|
|
|
|
let size = default_size.to_physical::<f64>(hidpi_factor);
|
|
|
|
(
|
|
window,
|
|
size.width.round() as u32,
|
|
size.height.round() as u32,
|
|
hidpi_factor,
|
|
)
|
|
}
|