mirror of https://github.com/sotrh/learn-wgpu
Merge branch 'web'
commit
ac73aee9f5
@ -0,0 +1 @@
|
||||
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/pong code/showcase/pong
|
@ -1,81 +1,97 @@
|
||||
use anyhow::*;
|
||||
use fs_extra::copy_items;
|
||||
use fs_extra::dir::CopyOptions;
|
||||
use glob::glob;
|
||||
use std::fs::{read_to_string, write};
|
||||
use std::path::PathBuf;
|
||||
use naga::back::wgsl;
|
||||
use naga::front::glsl::Options;
|
||||
use naga::front::glsl::Parser;
|
||||
use rayon::prelude::*;
|
||||
use std::env;
|
||||
use std::{fs::read_to_string, path::PathBuf};
|
||||
|
||||
struct ShaderData {
|
||||
src: String,
|
||||
src_path: PathBuf,
|
||||
spv_path: PathBuf,
|
||||
kind: shaderc::ShaderKind,
|
||||
}
|
||||
pub fn load_shader(src_path: PathBuf) -> Result<()> {
|
||||
let extension = src_path
|
||||
.extension()
|
||||
.context("File has no extension")?
|
||||
.to_str()
|
||||
.context("Extension cannot be converted to &str")?;
|
||||
let kind = match extension {
|
||||
"vert" => naga::ShaderStage::Vertex,
|
||||
"frag" => naga::ShaderStage::Fragment,
|
||||
"comp" => naga::ShaderStage::Compute,
|
||||
_ => bail!("Unsupported shader: {}", src_path.display()),
|
||||
};
|
||||
|
||||
let src = read_to_string(src_path.clone())?;
|
||||
// let spv_path = src_path.with_extension(format!("{}.spv", extension));
|
||||
let wgsl_path = src_path.with_extension(format!("{}.wgsl", extension));
|
||||
|
||||
let mut parser = Parser::default();
|
||||
let options = Options::from(kind);
|
||||
let module = match parser.parse(&options, &src) {
|
||||
Ok(it) => it,
|
||||
Err(errors) => {
|
||||
bail!(
|
||||
"Failed to compile shader: {}\nErrors:\n{:#?}",
|
||||
src_path.display(),
|
||||
errors
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
impl ShaderData {
|
||||
pub fn load(src_path: PathBuf) -> Result<Self> {
|
||||
let extension = src_path
|
||||
.extension()
|
||||
.context("File has no extension")?
|
||||
.to_str()
|
||||
.context("Extension cannot be converted to &str")?;
|
||||
let kind = match extension {
|
||||
"vert" => shaderc::ShaderKind::Vertex,
|
||||
"frag" => shaderc::ShaderKind::Fragment,
|
||||
"comp" => shaderc::ShaderKind::Compute,
|
||||
_ => bail!("Unsupported shader: {}", src_path.display()),
|
||||
};
|
||||
let info = naga::valid::Validator::new(
|
||||
naga::valid::ValidationFlags::all(),
|
||||
naga::valid::Capabilities::empty(),
|
||||
)
|
||||
.validate(&module)?;
|
||||
std::fs::write(wgsl_path, wgsl::write_string(&module, &info)?)?;
|
||||
|
||||
let src = read_to_string(src_path.clone())?;
|
||||
let spv_path = src_path.with_extension(format!("{}.spv", extension));
|
||||
// let flags = spv::WriterFlags::DEBUG | spv::WriterFlags::ADJUST_COORDINATE_SPACE;
|
||||
// let options = spv::Options {
|
||||
// flags,
|
||||
// ..Default::default()
|
||||
// };
|
||||
// let spv = spv::write_vec(&module, &info, &options)?;
|
||||
// let dis = rspirv::dr::load_words(spv)
|
||||
// .expect("Unable to disassemble shader")
|
||||
// .disassemble();
|
||||
// std::fs::write(spv_path, &spv)?;
|
||||
|
||||
Ok(Self {
|
||||
src,
|
||||
src_path,
|
||||
spv_path,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// This tells cargo to rerun this script if something in /src/ changes.
|
||||
println!("cargo:rerun-if-changed=src/*");
|
||||
|
||||
// Collect all shaders recursively within /src/
|
||||
let mut shader_paths = [
|
||||
glob("./res/**/*.vert")?,
|
||||
glob("./res/**/*.frag")?,
|
||||
glob("./res/**/*.comp")?,
|
||||
];
|
||||
// UDPATED!
|
||||
let shader_paths = {
|
||||
let mut data = Vec::new();
|
||||
data.extend(glob("./res/**/*.vert")?);
|
||||
data.extend(glob("./res/**/*.frag")?);
|
||||
data.extend(glob("./res/**/*.comp")?);
|
||||
data
|
||||
};
|
||||
|
||||
// This could be parallelized
|
||||
let shaders = shader_paths
|
||||
.iter_mut()
|
||||
.flatten()
|
||||
.map(|glob_result| ShaderData::load(glob_result?))
|
||||
// UPDATED!
|
||||
// This is parallelized
|
||||
shader_paths
|
||||
.into_par_iter()
|
||||
.map(|glob_result| load_shader(glob_result?))
|
||||
.collect::<Vec<Result<_>>>()
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
|
||||
|
||||
// This can't be parallelized. The [shaderc::Compiler] is not
|
||||
// thread safe. Also, it creates a lot of resources. You could
|
||||
// spawn multiple processes to handle this, but it would probably
|
||||
// be better just to only compile shaders that have been changed
|
||||
// recently.
|
||||
for shader in shaders {
|
||||
// This tells cargo to rerun this script if something in /src/ changes.
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
shader.src_path.as_os_str().to_str().unwrap()
|
||||
);
|
||||
// This tells cargo to rerun this script if something in /res/ changes.
|
||||
println!("cargo:rerun-if-changed=res/*");
|
||||
|
||||
let compiled = compiler.compile_into_spirv(
|
||||
&shader.src,
|
||||
shader.kind,
|
||||
shader.src_path.to_str().unwrap(),
|
||||
"main",
|
||||
None,
|
||||
)?;
|
||||
write(shader.spv_path, compiled.as_binary_u8())?;
|
||||
}
|
||||
let out_dir = env::var("OUT_DIR")?;
|
||||
let mut copy_options = CopyOptions::new();
|
||||
let mut paths_to_copy = Vec::new();
|
||||
copy_options.overwrite = true;
|
||||
paths_to_copy.push("res/");
|
||||
copy_items(&paths_to_copy, out_dir, ©_options)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
<!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 {
|
||||
background-color: black;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,17 @@
|
||||
struct FragmentOutput {
|
||||
[[location(0)]] fColor: vec4<f32>;
|
||||
};
|
||||
|
||||
var<private> fColor: vec4<f32>;
|
||||
|
||||
fn main1() {
|
||||
fColor = vec4<f32>(f32(1));
|
||||
return;
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main() -> FragmentOutput {
|
||||
main1();
|
||||
let e3: vec4<f32> = fColor;
|
||||
return FragmentOutput(e3);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
struct VertexOutput {
|
||||
[[builtin(position)]] member: vec4<f32>;
|
||||
};
|
||||
|
||||
var<private> aPosition1: vec2<f32>;
|
||||
var<private> gl_Position: vec4<f32>;
|
||||
|
||||
fn main1() {
|
||||
let e2: vec2<f32> = aPosition1;
|
||||
gl_Position = vec4<f32>(e2, f32(0), f32(1));
|
||||
return;
|
||||
}
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn main([[location(0)]] aPosition: vec2<f32>) -> VertexOutput {
|
||||
aPosition1 = aPosition;
|
||||
main1();
|
||||
let e5: vec4<f32> = gl_Position;
|
||||
return VertexOutput(e5);
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
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_if::cfg_if! {
|
||||
if #[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));
|
||||
} else {
|
||||
env_logger::init();
|
||||
}
|
||||
}
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let monitor = event_loop.primary_monitor().unwrap();
|
||||
let video_mode = monitor.video_modes().next();
|
||||
let size = video_mode.clone().map_or(PhysicalSize::new(800, 600), |vm| vm.size());
|
||||
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, 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,216 +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);
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
let sound_system = sound::SoundSystem::new();
|
||||
let sound_pack = sound::SoundPack::new();
|
||||
let mut events = Vec::new();
|
||||
let mut input = Input::new();
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="wasm-example">
|
||||
<div class="error" v-if="error">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: { example: "" },
|
||||
data() {
|
||||
return {
|
||||
error: {
|
||||
value: "",
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
await this.$nextTick();
|
||||
|
||||
try {
|
||||
const init = await import(`./wasm/${this.example}/${this.example}.js`);
|
||||
init().then(() => {
|
||||
console.log("WASM Loaded");
|
||||
});
|
||||
} catch (e) {
|
||||
this.error = `An error occurred loading "${this.example}": ${e}`;
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
@ -0,0 +1,23 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--
|
||||
This is adapted from https://github.com/gfx-rs/wgpu/blob/master/run-wasm-example.sh
|
||||
|
||||
Eventually I'll have all the examples create there own components for vuepress to
|
||||
use, but this will do for now.
|
||||
-->
|
||||
<script type="module">
|
||||
import init from "./pong.js";
|
||||
window.addEventListener("load", () => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue