#[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, music: HashMap, } 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 = 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::(hidpi_factor); ( window, size.width.round() as u32, size.height.round() as u32, hidpi_factor, ) }