generated from tversteeg/library-github-template
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(example): simplify window example a lot by using the 'pixel-game…
…-lib' library
- Loading branch information
Showing
2 changed files
with
75 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,160 +1,87 @@ | ||
use blit::{prelude::Size, Blit, BlitOptions, ToBlitBuffer}; | ||
use pixels::{PixelsBuilder, SurfaceTexture}; | ||
use rotsprite::Rotsprite; | ||
use web_time::SystemTime; | ||
use winit::{ | ||
dpi::LogicalSize, | ||
event::{Event, WindowEvent}, | ||
event_loop::{ControlFlow, EventLoop}, | ||
window::WindowBuilder, | ||
use std::time::SystemTime; | ||
|
||
use blit::{prelude::Size, Blit, BlitBuffer, BlitOptions, ToBlitBuffer}; | ||
use pixel_game_lib::{ | ||
canvas::Canvas, | ||
vek::{Extent2, Vec2}, | ||
window::{Input, KeyCode, WindowConfig}, | ||
PixelGame, | ||
}; | ||
use rotsprite::Rotsprite; | ||
|
||
/// Mask color of the source image. | ||
const MASK_COLOR: u32 = 0xFF_FF_FF; | ||
|
||
// Window settings | ||
const DST_SIZE: Size = Size { | ||
width: 200, | ||
height: 200, | ||
}; | ||
|
||
async fn run() { | ||
// Load the image from disk | ||
let img = image::load_from_memory(include_bytes!("./threeforms.png")) | ||
.unwrap() | ||
.to_rgb8() | ||
.to_blit_buffer_with_mask_color(MASK_COLOR); | ||
log::info!("Loaded RGBA image with size {:?}", img.size()); | ||
/// State wrapping the rotation. | ||
struct State { | ||
/// Current rotation of the sprite. | ||
rotation: f64, | ||
/// Last time since rotation. | ||
last_time: SystemTime, | ||
/// The actual image. | ||
img: BlitBuffer, | ||
/// The last rotated variant. | ||
rotated: BlitBuffer, | ||
} | ||
|
||
// Setup a winit window | ||
let size = LogicalSize::new( | ||
DST_SIZE.width as f64 * 2.0 + 10.0, | ||
DST_SIZE.height as f64 * 2.0 + 10.0, | ||
); | ||
let event_loop = EventLoop::new(); | ||
let mut window_builder = WindowBuilder::new() | ||
.with_title("Rotsprite") | ||
.with_inner_size(size); | ||
impl PixelGame for State { | ||
fn update(&mut self, input: &Input, _mouse_pos: Option<Vec2<usize>>, _dt: f32) -> bool { | ||
// Rotate every second | ||
if self.last_time.elapsed().unwrap().as_secs_f64() >= 1.0 { | ||
self.last_time = SystemTime::now(); | ||
|
||
// Setup the WASM canvas if running on the browser | ||
#[cfg(target_arch = "wasm32")] | ||
{ | ||
use winit::platform::web::WindowBuilderExtWebSys; | ||
self.rotation += 15.0; | ||
|
||
window_builder = window_builder.with_canvas(Some(wasm::setup_canvas())); | ||
} | ||
// Rotate the sprite | ||
self.rotated = self | ||
.img | ||
.rotsprite((self.rotation / 15.0).round() * 15.0) | ||
.unwrap(); | ||
|
||
let window = window_builder.build(&event_loop).unwrap(); | ||
println!( | ||
"Rotated sprite in {}ms", | ||
self.last_time.elapsed().unwrap().as_millis() | ||
); | ||
} | ||
|
||
let mut pixels = { | ||
let surface_texture = | ||
SurfaceTexture::new(DST_SIZE.width * 2 + 10, DST_SIZE.height * 2 + 10, &window); | ||
PixelsBuilder::new(DST_SIZE.width, DST_SIZE.height, surface_texture) | ||
.clear_color(pixels::wgpu::Color { | ||
r: 0.3, | ||
g: 0.1, | ||
b: 0.3, | ||
a: 1.0, | ||
}) | ||
.build_async() | ||
.await | ||
// Exit when escape is pressed | ||
input.key_pressed(KeyCode::Escape) | ||
} | ||
.unwrap(); | ||
|
||
// Rotate a tiny bit every frame | ||
let mut rotation = 0.0f64; | ||
|
||
event_loop.run(move |event, _, control_flow| { | ||
*control_flow = ControlFlow::Wait; | ||
|
||
match event { | ||
Event::RedrawRequested(window_id) if window_id == window.id() => { | ||
let mut buffer = bytemuck::cast_slice_mut(pixels.frame_mut()); | ||
buffer.fill(0); | ||
|
||
let now = SystemTime::now(); | ||
|
||
// Rotate the sprite | ||
let rotated_blit_buffer = img.rotsprite((rotation / 15.0).round() * 15.0).unwrap(); | ||
|
||
log::info!("Rotated sprite in {}ms", now.elapsed().unwrap().as_millis()); | ||
|
||
// Draw the rotated sprite | ||
rotated_blit_buffer.blit(&mut buffer, DST_SIZE, &BlitOptions::new()); | ||
|
||
rotation += 0.5; | ||
|
||
// Blit draws the pixels in RGBA format, but the pixels crate expects BGRA, so convert it | ||
pixels.frame_mut().chunks_exact_mut(4).for_each(|color| { | ||
let (r, g, b, a) = (color[0], color[1], color[2], color[3]); | ||
|
||
color[0] = b; | ||
color[1] = g; | ||
color[2] = r; | ||
color[3] = a; | ||
}); | ||
fn render(&mut self, canvas: &mut Canvas<'_>) { | ||
// Reset the canvas | ||
canvas.fill(0xFFFFFFFF); | ||
|
||
if let Err(err) = pixels.render() { | ||
log::error!("Pixels error:\n{err}"); | ||
} | ||
} | ||
Event::MainEventsCleared => { | ||
// Animate the next frame | ||
window.request_redraw(); | ||
} | ||
Event::WindowEvent { | ||
event: WindowEvent::CloseRequested, | ||
window_id, | ||
} if window_id == window.id() => { | ||
*control_flow = ControlFlow::Exit; | ||
} | ||
_ => {} | ||
} | ||
}); | ||
} | ||
|
||
fn main() { | ||
#[cfg(target_arch = "wasm32")] | ||
{ | ||
std::panic::set_hook(Box::new(console_error_panic_hook::hook)); | ||
console_log::init_with_level(log::Level::Info).expect("error initializing logger"); | ||
|
||
wasm_bindgen_futures::spawn_local(run()); | ||
} | ||
#[cfg(not(target_arch = "wasm32"))] | ||
{ | ||
pollster::block_on(run()); | ||
// Draw the rotated sprite | ||
let canvas_size = Size::new(canvas.width(), canvas.height()); | ||
self.rotated | ||
.blit(canvas.raw_buffer(), canvas_size, &BlitOptions::new()); | ||
} | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
mod wasm { | ||
use wasm_bindgen::JsCast; | ||
use web_sys::HtmlCanvasElement; | ||
|
||
/// Attach the winit window to a canvas. | ||
pub fn setup_canvas() -> HtmlCanvasElement { | ||
log::debug!("Binding window to HTML canvas"); | ||
|
||
let window = web_sys::window().unwrap(); | ||
|
||
let document = window.document().unwrap(); | ||
let body = document.body().unwrap(); | ||
body.style().set_css_text("text-align: center"); | ||
|
||
let canvas = document | ||
.create_element("canvas") | ||
.unwrap() | ||
.dyn_into::<HtmlCanvasElement>() | ||
.unwrap(); | ||
|
||
canvas.set_id("canvas"); | ||
body.append_child(&canvas).unwrap(); | ||
canvas.style().set_css_text("display:block; margin: auto"); | ||
|
||
let header = document.create_element("h2").unwrap(); | ||
header.set_text_content(Some("Rotsprite")); | ||
body.append_child(&header).unwrap(); | ||
|
||
canvas | ||
} | ||
/// Open the empty window. | ||
fn main() { | ||
// Load the image from disk | ||
let img = image::load_from_memory(include_bytes!("./threeforms.png")) | ||
.unwrap() | ||
.to_rgb8() | ||
.to_blit_buffer_with_mask_color(MASK_COLOR); | ||
let rotated = img.clone(); | ||
|
||
// Active modifiable state | ||
let state = State { | ||
rotation: 0.0, | ||
last_time: SystemTime::now(), | ||
img, | ||
rotated, | ||
}; | ||
|
||
// Window configuration with huge pixels | ||
let window_config = WindowConfig { | ||
buffer_size: Extent2::new(200, 200), | ||
scaling: 2, | ||
..Default::default() | ||
}; | ||
|
||
state.run(window_config).expect("Error running game"); | ||
} |