wasm now loading

web
Ben Hansen 3 years ago
parent 0da913d253
commit 141c94f48a

1
Cargo.lock generated

@ -1760,6 +1760,7 @@ dependencies = [
"fs_extra",
"getrandom",
"glob",
"instant",
"log",
"naga",
"pollster",

@ -4,6 +4,9 @@ version = "0.1.0"
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
winit = "0.25"
anyhow = "1.0"
@ -15,6 +18,7 @@ wgpu_glyph = "0.15"
rand = "0.8"
rodio = { version = "0.14", default-features = false, features = ["wav"] }
log = "0.4"
instant = "0.1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pong with WASM</title>
</head>
<body>
<script type="module">
import init from "./pkg/pong.js";
init().then(() => {
console.log("WASM Loaded");
});
</script>
<style>
canvas {
border: red 1px solid;
}
</style>
</body>
</html>

@ -0,0 +1,252 @@
mod input;
mod render;
mod sound;
mod state;
mod system;
mod util;
use input::Input;
use system::System;
#[cfg(target_arch="wasm32")]
use wasm_bindgen::prelude::*;
use winit::dpi::PhysicalSize;
use winit::event::*;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{Fullscreen, WindowBuilder};
#[cfg_attr(target_arch="wasm32", wasm_bindgen(start))]
pub fn start() {
#[cfg(target_arch = "wasm32")]
{
console_log::init_with_level(log::Level::Info).expect("Could't initialize logger");
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
}
let event_loop = EventLoop::new();
let monitor = event_loop.primary_monitor().unwrap();
let video_mode = monitor.video_modes().next();
let window = WindowBuilder::new()
.with_visible(false)
.with_title("Pong")
.with_fullscreen(video_mode.map(|vm| Fullscreen::Exclusive(vm)))
.build(&event_loop)
.unwrap();
if window.fullscreen().is_none() {
window.set_inner_size(PhysicalSize::new(512, 512));
}
window.set_cursor_visible(false);
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.body())
.and_then(|body| {
body.append_child(&web_sys::Element::from(window.canvas())).ok()
})
.expect("Couldn't append cavas to document body.");
}
log::info!("Setup...");
let mut render = pollster::block_on(render::Render::new(&window, window.inner_size()));
let mut state = state::State {
ball: state::Ball {
position: (0.0, 0.0).into(),
velocity: (0.0, 0.0).into(),
radius: 0.05,
visible: true,
},
player1: state::Player {
position: (-0.8, 0.0).into(),
size: (0.05, 0.4).into(),
score: 0,
visible: true,
},
player2: state::Player {
position: (0.8, 0.0).into(),
size: (0.05, 0.4).into(),
score: 0,
visible: true,
},
title_text: state::Text {
position: (20.0, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("PONG"),
size: 64.0,
..Default::default()
},
play_button: state::Text {
position: (40.0, 100.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("Play"),
size: 32.0,
centered: false,
..Default::default()
},
quit_button: state::Text {
position: (40.0, 160.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("Quit"),
size: 32.0,
..Default::default()
},
player1_score: state::Text {
position: (render.width() * 0.25, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("0"),
size: 32.0,
..Default::default()
},
player2_score: state::Text {
position: (render.width() * 0.75, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("0"),
size: 32.0,
..Default::default()
},
win_text: state::Text {
position: (render.width() * 0.5, render.height() * 0.5).into(),
bounds: (render.width(), state::UNBOUNDED_F32).into(),
size: 32.0,
centered: true,
..Default::default()
},
game_state: state::GameState::MainMenu,
};
log::info!("Sound...");
let sound_system = sound::SoundSystem::new();
let sound_pack = sound::SoundPack::new();
let mut events = Vec::new();
let mut input = Input::new();
log::info!("Initializing Systems...");
let mut menu_system = system::MenuSystem;
let mut serving_system = system::ServingSystem::new();
let mut play_system = system::PlaySystem;
let ball_system = system::BallSystem;
let mut game_over_system = system::GameOverSystem::new();
let mut visiblity_system = system::VisibilitySystem;
visiblity_system.start(&mut state);
menu_system.start(&mut state);
window.set_visible(true);
log::info!("Event Loop...");
event_loop.run(move |event, _, control_flow| {
*control_flow = if state.game_state == state::GameState::Quiting {
ControlFlow::Exit
} else {
ControlFlow::Poll
};
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
state.game_state = state::GameState::Quiting;
}
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: element_state,
virtual_keycode: Some(key),
..
},
..
},
..
} => {
let input_handled = match state.game_state {
state::GameState::Quiting => true,
_ => input.update(key, element_state),
};
if !input_handled {
process_input(element_state, key, control_flow);
}
}
Event::RedrawRequested(_) => {
for event in &events {
match event {
state::Event::FocusChanged | state::Event::ButtonPressed => {
sound_system.queue(sound_pack.bounce());
}
state::Event::BallBounce(_pos) => {
sound_system.queue(sound_pack.bounce());
}
state::Event::Score(_) => {
sound_system.queue(sound_pack.bounce());
}
}
}
events.clear();
visiblity_system.update_state(&input, &mut state, &mut events);
match state.game_state {
state::GameState::MainMenu => {
menu_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
}
}
state::GameState::Serving => {
serving_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Playing {
play_system.start(&mut state);
}
}
state::GameState::Playing => {
ball_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
} else if state.game_state == state::GameState::GameOver {
game_over_system.start(&mut state);
}
}
state::GameState::GameOver => {
game_over_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::MainMenu {
menu_system.start(&mut state);
}
}
state::GameState::Quiting => {}
}
render.render_state(&state);
if state.game_state != state::GameState::Quiting {
window.request_redraw();
}
}
_ => {}
}
});
}
fn process_input(
element_state: ElementState,
keycode: VirtualKeyCode,
control_flow: &mut ControlFlow,
) {
match (keycode, element_state) {
(VirtualKeyCode::Escape, ElementState::Pressed) => {
*control_flow = ControlFlow::Exit;
}
_ => {}
}
}

@ -1,238 +1,3 @@
mod input;
mod render;
mod sound;
mod state;
mod system;
mod util;
use input::Input;
use system::System;
use winit::event::*;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{Fullscreen, WindowBuilder};
fn main() {
let event_loop = EventLoop::new();
let monitor = event_loop.primary_monitor().unwrap();
let video_mode = monitor.video_modes().next().unwrap();
let window = WindowBuilder::new()
.with_visible(false)
.with_title("Pong")
.with_fullscreen(Some(Fullscreen::Exclusive(video_mode.clone())))
.build(&event_loop)
.unwrap();
window.set_cursor_visible(false);
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
console_log::init_with_level(log::Level::Info).expect("Could't initialize logger");
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.body())
.and_then(|body| {
body.append_child(&web_sys::Element::from(window.canvas())).ok()
})
.expect("Couldn't append cavas to document body.");
}
log::info!("Setup...");
let mut render = pollster::block_on(render::Render::new(&window, &video_mode));
let mut state = state::State {
ball: state::Ball {
position: (0.0, 0.0).into(),
velocity: (0.0, 0.0).into(),
radius: 0.05,
visible: true,
},
player1: state::Player {
position: (-0.8, 0.0).into(),
size: (0.05, 0.4).into(),
score: 0,
visible: true,
},
player2: state::Player {
position: (0.8, 0.0).into(),
size: (0.05, 0.4).into(),
score: 0,
visible: true,
},
title_text: state::Text {
position: (20.0, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("PONG"),
size: 64.0,
..Default::default()
},
play_button: state::Text {
position: (40.0, 100.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("Play"),
size: 32.0,
centered: false,
..Default::default()
},
quit_button: state::Text {
position: (40.0, 160.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("Quit"),
size: 32.0,
..Default::default()
},
player1_score: state::Text {
position: (render.width() * 0.25, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("0"),
size: 32.0,
..Default::default()
},
player2_score: state::Text {
position: (render.width() * 0.75, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("0"),
size: 32.0,
..Default::default()
},
win_text: state::Text {
position: (render.width() * 0.5, render.height() * 0.5).into(),
bounds: (render.width(), state::UNBOUNDED_F32).into(),
size: 32.0,
centered: true,
..Default::default()
},
game_state: state::GameState::MainMenu,
};
log::info!("Sound...");
let sound_system = sound::SoundSystem::new();
let sound_pack = sound::SoundPack::new();
let mut events = Vec::new();
let mut input = Input::new();
log::info!("Initializing Systems...");
let mut menu_system = system::MenuSystem;
let mut serving_system = system::ServingSystem::new();
let mut play_system = system::PlaySystem;
let ball_system = system::BallSystem;
let mut game_over_system = system::GameOverSystem::new();
let mut visiblity_system = system::VisibilitySystem;
visiblity_system.start(&mut state);
menu_system.start(&mut state);
window.set_visible(true);
log::info!("Event Loop...");
event_loop.run(move |event, _, control_flow| {
*control_flow = if state.game_state == state::GameState::Quiting {
ControlFlow::Exit
} else {
ControlFlow::Poll
};
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
state.game_state = state::GameState::Quiting;
}
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: element_state,
virtual_keycode: Some(key),
..
},
..
},
..
} => {
let input_handled = match state.game_state {
state::GameState::Quiting => true,
_ => input.update(key, element_state),
};
if !input_handled {
process_input(element_state, key, control_flow);
}
}
Event::RedrawRequested(_) => {
for event in &events {
match event {
state::Event::FocusChanged | state::Event::ButtonPressed => {
sound_system.queue(sound_pack.bounce());
}
state::Event::BallBounce(_pos) => {
sound_system.queue(sound_pack.bounce());
}
state::Event::Score(_) => {
sound_system.queue(sound_pack.bounce());
}
}
}
events.clear();
visiblity_system.update_state(&input, &mut state, &mut events);
match state.game_state {
state::GameState::MainMenu => {
menu_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
}
}
state::GameState::Serving => {
serving_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Playing {
play_system.start(&mut state);
}
}
state::GameState::Playing => {
ball_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
} else if state.game_state == state::GameState::GameOver {
game_over_system.start(&mut state);
}
}
state::GameState::GameOver => {
game_over_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::MainMenu {
menu_system.start(&mut state);
}
}
state::GameState::Quiting => {}
}
render.render_state(&state);
if state.game_state != state::GameState::Quiting {
window.request_redraw();
}
}
_ => {}
}
});
}
fn process_input(
element_state: ElementState,
keycode: VirtualKeyCode,
control_flow: &mut ControlFlow,
) {
match (keycode, element_state) {
(VirtualKeyCode::Escape, ElementState::Pressed) => {
*control_flow = ControlFlow::Exit;
}
_ => {}
}
}
pong::start();
}

@ -3,7 +3,7 @@ mod buffer;
use std::iter;
use wgpu_glyph::{ab_glyph, Section, Text};
use winit::monitor::VideoMode;
use winit::dpi::PhysicalSize;
use winit::window::Window;
use buffer::*;
@ -36,7 +36,7 @@ impl Render {
self.config.height as f32
}
pub async fn new(window: &Window, video_mode: &VideoMode) -> Self {
pub async fn new(window: &Window, size: PhysicalSize<u32>) -> Self {
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::Backends::all());
@ -54,14 +54,13 @@ impl Render {
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: wgpu::Limits::default(),
limits: wgpu::Limits::downlevel_webgl2_defaults(),
},
None, // Trace path
)
.await
.unwrap();
let size = video_mode.size();
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface.get_preferred_format(&adapter).unwrap(),
@ -193,12 +192,14 @@ impl Render {
self.staging_belt.finish();
self.queue.submit(iter::once(encoder.finish()));
frame.present();
}
Err(wgpu::SurfaceError::Outdated) => {
log::info!("Outdated surface texture");
self.surface.configure(&self.device, &self.config);
}
Err(e) => {
eprintln!("Error: {}", e);
log::error!("Error: {}", e);
}
}
}

@ -67,15 +67,19 @@ impl System for MenuSystem {
events.push(state::Event::FocusChanged);
state.play_button.focused = false;
state.quit_button.focused = true;
log::info!("Quit selected");
} else if state.quit_button.focused && input.ui_up_pressed() {
events.push(state::Event::FocusChanged);
state.quit_button.focused = false;
state.play_button.focused = true;
log::info!("Play selected");
}
if state.play_button.focused && input.enter_pressed {
log::info!("Starting game");
events.push(state::Event::ButtonPressed);
state.game_state = state::GameState::Serving;
log::info!("Quitting");
} else if state.quit_button.focused && input.enter_pressed {
events.push(state::Event::ButtonPressed);
state.game_state = state::GameState::Quiting;
@ -118,6 +122,7 @@ impl System for PlaySystem {
}
if state.player1.score > 2 || state.player2.score > 2 {
log::info!("Gameover");
state.game_state = state::GameState::GameOver;
}
}
@ -156,10 +161,12 @@ impl System for BallSystem {
}
if state.ball.position.x > 1.0 {
log::info!("Player 1 scored");
state.player1.score += 1;
state.game_state = state::GameState::Serving;
events.push(state::Event::Score(0));
} else if state.ball.position.x < -1.0 {
log::info!("Player 1 scored");
state.player2.score += 1;
state.game_state = state::GameState::Serving;
events.push(state::Event::Score(1));
@ -168,20 +175,20 @@ impl System for BallSystem {
}
pub struct ServingSystem {
last_time: std::time::Instant,
last_time: instant::Instant,
}
impl ServingSystem {
pub fn new() -> Self {
Self {
last_time: std::time::Instant::now(),
last_time: instant::Instant::now(),
}
}
}
impl System for ServingSystem {
fn start(&mut self, state: &mut state::State) {
self.last_time = std::time::Instant::now();
self.last_time = instant::Instant::now();
let direction = state.ball.position.x.signum();
state.ball.position = (0.0, 0.0).into();
state.ball.velocity = cgmath::Vector2::unit_x() * direction * -util::BALL_SPEED;
@ -195,29 +202,30 @@ impl System for ServingSystem {
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let current_time = std::time::Instant::now();
let current_time = instant::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 2.0 {
log::info!("Serving...");
state.game_state = state::GameState::Playing;
}
}
}
pub struct GameOverSystem {
last_time: std::time::Instant,
last_time: instant::Instant,
}
impl GameOverSystem {
pub fn new() -> Self {
Self {
last_time: std::time::Instant::now(),
last_time: instant::Instant::now(),
}
}
}
impl System for GameOverSystem {
fn start(&mut self, state: &mut state::State) {
self.last_time = std::time::Instant::now();
self.last_time = instant::Instant::now();
state.player1_score.text = format!("{}", state.player1.score);
state.player2_score.text = format!("{}", state.player2.score);
@ -227,6 +235,8 @@ impl System for GameOverSystem {
} else {
String::from("Player 2 wins!")
};
log::info!("{}", state.win_text.text);
}
fn update_state(
@ -235,7 +245,7 @@ impl System for GameOverSystem {
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let current_time = std::time::Instant::now();
let current_time = instant::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 1.0 {
state.game_state = state::GameState::MainMenu;

Loading…
Cancel
Save