Gaming & Canvas dengan WebAssembly
Game development adalah salah satu motivasi awal WebAssembly. Dengan Wasm, kamu bisa membuat game yang berjalan di browser dengan performa mendekati native — tanpa plugin.
Game Loop Pattern
// JavaScript: requestAnimationFrame + Wasm game logic
import init, { GameState } from "./pkg/game.js";
await init();
const game = GameState.new(800, 600);
let lastTime = 0;
function gameLoop(timestamp) {
const dt = (timestamp - lastTime) / 1000; // delta time in seconds
lastTime = timestamp;
// Update game state (Wasm — cepat)
game.update(dt);
// Render (JS Canvas API atau Wasm direct pixel)
game.render();
requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
Rust: Game State & Physics
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct GameState {
width: f64,
height: f64,
player_x: f64,
player_y: f64,
velocity_x: f64,
velocity_y: f64,
enemies: Vec<Entity>,
score: u32,
}
struct Entity {
x: f64, y: f64,
vx: f64, vy: f64,
radius: f64,
active: bool,
}
#[wasm_bindgen]
impl GameState {
pub fn new(width: f64, height: f64) -> Self {
Self {
width, height,
player_x: width / 2.0,
player_y: height / 2.0,
velocity_x: 0.0,
velocity_y: 0.0,
enemies: Vec::new(),
score: 0,
}
}
pub fn update(&mut self, dt: f64) {
// Physics: update posisi
self.player_x += self.velocity_x * dt;
self.player_y += self.velocity_y * dt;
// Boundary check
self.player_x = self.player_x.clamp(0.0, self.width);
self.player_y = self.player_y.clamp(0.0, self.height);
// Update enemies
for enemy in &mut self.enemies {
if !enemy.active { continue; }
enemy.x += enemy.vx * dt;
enemy.y += enemy.vy * dt;
// Collision detection (circle vs circle)
let dx = enemy.x - self.player_x;
let dy = enemy.y - self.player_y;
let dist = (dx * dx + dy * dy).sqrt();
if dist < enemy.radius + 10.0 {
enemy.active = false;
self.score += 10;
}
}
}
pub fn set_input(&mut self, key: &str) {
match key {
"ArrowUp" => self.velocity_y = -200.0,
"ArrowDown" => self.velocity_y = 200.0,
"ArrowLeft" => self.velocity_x = -200.0,
"ArrowRight" => self.velocity_x = 200.0,
_ => {}
}
}
}
Canvas Rendering
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
#[wasm_bindgen]
impl GameState {
pub fn render(&self) {
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document
.get_element_by_id("game")
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap();
let ctx = canvas
.get_context("2d").unwrap().unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();
// Clear
ctx.clear_rect(0.0, 0.0, self.width, self.height);
// Draw player
ctx.set_fill_style_str("#00ff88");
ctx.begin_path();
ctx.arc(self.player_x, self.player_y, 10.0,
0.0, std::f64::consts::PI * 2.0).unwrap();
ctx.fill();
// Draw score
ctx.set_fill_style_str("#ffffff");
ctx.set_font("16px monospace");
ctx.fill_text(
&format!("Score: {}", self.score), 10.0, 25.0
).unwrap();
}
}
Input Handling (JavaScript Side)
document.addEventListener("keydown", (e) => {
game.set_input(e.key);
});
document.addEventListener("keyup", () => {
game.set_input("stop");
});
Real-World Game Engines + Wasm
- Unity — Export ke WebGL/Wasm untuk browser games
- Unreal Engine — HTML5 target menggunakan Emscripten → Wasm
- Bevy (Rust) — Native Wasm support, ECS architecture
- Macroquad (Rust) — Simple 2D game framework, compile ke Wasm