Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
/out
18 changes: 9 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "yt-low-poly-terrain-bevy-live"
name = "low-poly-terrain-bevy"
version = "0.1.0"
edition = "2021"

Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# steps to run the rust+bevy+wasm web projects

0.1 `cargo install -f wasm-bindgen-cli` - install latest wasm bindgen cli

-- if you get stuck here, I had to install a specific version e.g. `cargo install -f wasm-bindgen-cli --version 0.2.93` but this is already outdated, better try to keep things up to date.

0.2 `rustup target add wasm32-unknown-unknown` - done once in the project directory to add web support

1.1 `cargo build --release --target wasm32-unknown-unknown` - done every time you want to compile to build for the web

1.2 `wasm-bindgen --out-dir ./out/ --target web ./target/wasm32-unknown-unknown/release/low-poly-terrain-bevy.wasm` - done every time to generate wasm bindings for the web (make sure your-game-name.wasm is set to your project name specified in Cargo.toml)

1.3 Now you can copy `./out` to `./web/out` e.g. `cp -r ./out ./web` and then

1.4 run the project locally with `npx serve web`

Open localhost on the specified port and see the Rust+Bevy wasm web project locally
42 changes: 33 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ fn main() {
DefaultPlugins.set(RenderPlugin {
render_creation: RenderCreation::Automatic(
WgpuSettings {
// WARN this is a native only feature. It will not work with webgl or webgpu
features:
WgpuFeatures::POLYGON_MODE_LINE,
// // WARN this is a native only feature. It will not work with webgl or webgpu
// features:
// WgpuFeatures::POLYGON_MODE_LINE,
..default()
},
),
Expand Down Expand Up @@ -325,24 +325,48 @@ struct ShipCam;

fn control_ship(
input: Res<ButtonInput<KeyCode>>,
touches: Res<Touches>,
mut ships: Query<&mut Transform, With<Ship>>,
) {
let mut direction = Vec2::new(0., 0.);
let mut direction = Vec2::ZERO;

// Keyboard support
if input.pressed(KeyCode::KeyW) {
direction.y += 1.;
}
if input.pressed(KeyCode::KeyS) {
direction.y -= 1.;
}
if input.pressed(KeyCode::KeyA) {
direction.x += 1.;
direction.x += 5.;
}
if input.pressed(KeyCode::KeyD) {
direction.x -= 1.;
direction.x -= 5.;
}

// Touch support
let mut touch_data = Vec::new();
for touch in touches.iter() {
touch_data.push((touch.id(), touch.position(), touch.previous_position()));
}

if touch_data.len() == 2 {
let (_, first_pos, first_prev_pos) = touch_data[0];
let (_, second_pos, second_prev_pos) = touch_data[1];

let current_midpoint = (first_pos + second_pos) / 2.0;
let prev_midpoint = (first_prev_pos + second_prev_pos) / 2.0;
let drag_vector = current_midpoint - prev_midpoint;

// Use drag vector to determine ship direction
direction.x -= drag_vector.x;
direction.y += drag_vector.y;
}
for mut ship in &mut ships {
ship.translation.x += direction.x * 1.;
ship.translation.z += direction.y * 5.;

// Update ship position
for mut ship in ships.iter_mut() {
ship.translation.x += direction.x;
ship.translation.z += direction.y;
}
}

Expand Down
106 changes: 106 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<html>
<head>
<meta charset="UTF-8" />
<style>
body {
margin: 0;
background: darkgrey;
background-size: 400% 400%;
animation: gradient 15s ease infinite;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
position: absolute;
z-index : -999;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}

@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="loader"></div>
<script>
// the following function keeps track of all AudioContexts and resumes them on the first user
// interaction with the page. If the function is called and all contexts are already running,
// it will remove itself from all event listeners.
(function () {
// An array of all contexts to resume on the page
const audioContextList = [];

// An array of various user interaction events we should listen for
const userInputEventNames = [
"click",
"contextmenu",
"auxclick",
"dblclick",
"mousedown",
"mouseup",
"pointerup",
"touchend",
"keydown",
"keyup",
];

// A proxy object to intercept AudioContexts and
// add them to the array for tracking and resuming later
self.AudioContext = new Proxy(self.AudioContext, {
construct(target, args) {
const result = new target(...args);
audioContextList.push(result);
return result;
},
});

// To resume all AudioContexts being tracked
function resumeAllContexts(_event) {
let count = 0;

audioContextList.forEach((context) => {
if (context.state !== "running") {
context.resume();
} else {
count++;
}
});

// If all the AudioContexts have now resumed then we unbind all
// the event listeners from the page to prevent unnecessary resume attempts
// Checking count > 0 ensures that the user interaction happens AFTER the game started up
if (count > 0 && count === audioContextList.length) {
userInputEventNames.forEach((eventName) => {
document.removeEventListener(eventName, resumeAllContexts);
});
}
}

// We bind the resume function for each user interaction
// event on the page
userInputEventNames.forEach((eventName) => {
document.addEventListener(eventName, resumeAllContexts);
});
})();
</script>
<script type="module">
import init from '/out/low-poly-terrain-bevy.js'
init();
</script>
</body>
</html>
53 changes: 53 additions & 0 deletions web/out/low-poly-terrain-bevy.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* tslint:disable */
/* eslint-disable */

export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;

export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly main: (a: number, b: number) => number;
readonly wgpu_render_bundle_set_pipeline: (a: number, b: number) => void;
readonly wgpu_render_bundle_set_bind_group: (a: number, b: number, c: number, d: number, e: number) => void;
readonly wgpu_render_bundle_set_vertex_buffer: (a: number, b: number, c: number, d: number, e: number) => void;
readonly wgpu_render_bundle_set_push_constants: (a: number, b: number, c: number, d: number, e: number) => void;
readonly wgpu_render_bundle_draw: (a: number, b: number, c: number, d: number, e: number) => void;
readonly wgpu_render_bundle_draw_indexed: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly wgpu_render_bundle_draw_indirect: (a: number, b: number, c: number) => void;
readonly wgpu_render_bundle_draw_indexed_indirect: (a: number, b: number, c: number) => void;
readonly wgpu_render_bundle_set_index_buffer: (a: number, b: number, c: number, d: number, e: number) => void;
readonly wgpu_render_bundle_pop_debug_group: (a: number) => void;
readonly wgpu_render_bundle_insert_debug_marker: (a: number, b: number) => void;
readonly wgpu_render_bundle_push_debug_group: (a: number, b: number) => void;
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export_2: WebAssembly.Table;
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5e198ee8d2aa08c5: (a: number, b: number, c: number) => void;
readonly _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h34c23ac36ecd8694: (a: number, b: number) => void;
readonly _dyn_core__ops__function__FnMut__A_B___Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hae1ad34e1b360a64: (a: number, b: number, c: number, d: number) => void;
readonly _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb2e7268479ca323f: (a: number, b: number) => void;
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6fef265b6094c7ec: (a: number, b: number, c: number) => void;
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
readonly __wbindgen_exn_store: (a: number) => void;
readonly __wbindgen_start: () => void;
}

export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
*
* @returns {InitOutput}
*/
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;

/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
Loading