Skip to content
Merged
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
105 changes: 92 additions & 13 deletions wasm/game.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,90 @@
const canvas = document.getElementById('game-canvas');
const context = canvas.getContext('2d');
const context =
canvas.getContext('2d', { alpha: false, desynchronized: true }) ||
canvas.getContext('2d');
const fpsDisplay = document.getElementById('fps-counter');
let wasmExports = null;
let lastTime = null;
let fpsFrameCount = 0;
let fpsLastTimestamp = 0;
let ballStatePtr = 0;
let ballStateView = null;
const MAX_PIXEL_RATIO = 1.5;

function getBallStateView() {
if (!wasmExports || !wasmExports.memory) {
return null;
}

const memoryBuffer = wasmExports.memory.buffer;
if (!ballStatePtr && typeof wasmExports.get_ball_state_ptr === 'function') {
ballStatePtr = wasmExports.get_ball_state_ptr();
}

if (!ballStatePtr) {
return null;
}

if (
!ballStateView ||
ballStateView.byteOffset !== ballStatePtr ||
ballStateView.buffer !== memoryBuffer
) {
ballStateView = new Float32Array(memoryBuffer, ballStatePtr, 3);
}

return ballStateView;
}

function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const deviceRatio = window.devicePixelRatio || 1;
const pixelRatio = Math.min(deviceRatio, MAX_PIXEL_RATIO);
const displayWidth = Math.floor(window.innerWidth * pixelRatio);
const displayHeight = Math.floor(window.innerHeight * pixelRatio);

canvas.width = displayWidth;
canvas.height = displayHeight;
canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;

if (context && typeof context.resetTransform === 'function') {
context.resetTransform();
}

ballStateView = null;

if (wasmExports) {
wasmExports.set_canvas_size(canvas.width, canvas.height);
}
}

function drawFrame() {
if (!wasmExports) {
if (!wasmExports || !context) {
return;
}

context.clearRect(0, 0, canvas.width, canvas.height);
const state = getBallStateView();
let x;
let y;
let radius;

if (state) {
x = state[0];
y = state[1];
radius = state[2];
} else {
if (typeof wasmExports.get_ball_radius !== 'function') {
return;
}

radius = wasmExports.get_ball_radius();
x = wasmExports.get_ball_x();
y = wasmExports.get_ball_y();
}

context.fillStyle = '#0d47a1';
context.fillRect(0, 0, canvas.width, canvas.height);

const radius = wasmExports.get_ball_radius();
const x = wasmExports.get_ball_x();
const y = wasmExports.get_ball_y();

context.fillStyle = '#ffca28';
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2);
Expand All @@ -40,10 +97,24 @@ function updateAndRender(timestamp) {
fpsLastTimestamp = timestamp;
}

const deltaSeconds = (timestamp - lastTime) / 1000;
const deltaSecondsRaw = (timestamp - lastTime) / 1000;
const deltaSeconds = Math.min(deltaSecondsRaw, 0.25);
lastTime = timestamp;

wasmExports.update(deltaSeconds);
if (wasmExports) {
let ptr = 0;
if (typeof wasmExports.update_and_get_state === 'function') {
ptr = wasmExports.update_and_get_state(deltaSeconds);
} else if (typeof wasmExports.update === 'function') {
wasmExports.update(deltaSeconds);
}

if (ptr && ptr !== ballStatePtr) {
ballStatePtr = ptr;
ballStateView = null;
}
}

drawFrame();

fpsFrameCount += 1;
Expand All @@ -67,10 +138,18 @@ async function start() {
wasmExports = instance.exports;

resizeCanvas();
wasmExports.reset_ball();

if (typeof wasmExports.get_ball_state_ptr === 'function') {
ballStatePtr = wasmExports.get_ball_state_ptr();
}

if (typeof wasmExports.reset_ball === 'function') {
wasmExports.reset_ball();
}

drawFrame();
requestAnimationFrame(updateAndRender);
}

window.addEventListener('resize', resizeCanvas);
window.addEventListener('resize', resizeCanvas, { passive: true });
start();
101 changes: 65 additions & 36 deletions wasm/hello.c
Original file line number Diff line number Diff line change
@@ -1,25 +1,62 @@
#include <emscripten/emscripten.h>
#include <stdint.h>

typedef struct BallState {
float x;
float y;
float radius;
} BallState;

static float canvas_width = 640.0f;
static float canvas_height = 480.0f;
static float ball_x = 320.0f;
static float ball_y = 240.0f;
static float ball_radius = 16.0f;
static BallState ball = {320.0f, 240.0f, 16.0f};
static float velocity_x = 180.0f;
static float velocity_y = 140.0f;

static void clamp_ball_inside_canvas(void)
{
if (ball_x < ball_radius) {
ball_x = ball_radius;
} else if (ball_x > canvas_width - ball_radius) {
ball_x = canvas_width - ball_radius;
if (ball.x < ball.radius) {
ball.x = ball.radius;
} else {
const float max_x = canvas_width - ball.radius;
if (ball.x > max_x) {
ball.x = max_x;
}
}

if (ball.y < ball.radius) {
ball.y = ball.radius;
} else {
const float max_y = canvas_height - ball.radius;
if (ball.y > max_y) {
ball.y = max_y;
}
}
}

static void simulate_ball(float delta_seconds)
{
if (delta_seconds <= 0.0f) {
return;
}

ball.x += velocity_x * delta_seconds;
ball.y += velocity_y * delta_seconds;

if (ball.x - ball.radius < 0.0f) {
ball.x = ball.radius;
velocity_x = -velocity_x;
} else if (ball.x + ball.radius > canvas_width) {
ball.x = canvas_width - ball.radius;
velocity_x = -velocity_x;
}

if (ball_y < ball_radius) {
ball_y = ball_radius;
} else if (ball_y > canvas_height - ball_radius) {
ball_y = canvas_height - ball_radius;
if (ball.y - ball.radius < 0.0f) {
ball.y = ball.radius;
velocity_y = -velocity_y;
} else if (ball.y + ball.radius > canvas_height) {
ball.y = canvas_height - ball.radius;
velocity_y = -velocity_y;
}
}

Expand All @@ -40,8 +77,8 @@ void set_canvas_size(int width, int height)
EMSCRIPTEN_KEEPALIVE
void reset_ball(void)
{
ball_x = canvas_width * 0.5f;
ball_y = canvas_height * 0.5f;
ball.x = canvas_width * 0.5f;
ball.y = canvas_height * 0.5f;
velocity_x = 180.0f;
velocity_y = 140.0f;
clamp_ball_inside_canvas();
Expand All @@ -50,44 +87,36 @@ void reset_ball(void)
EMSCRIPTEN_KEEPALIVE
void update(float delta_seconds)
{
if (delta_seconds <= 0.0f) {
return;
}

ball_x += velocity_x * delta_seconds;
ball_y += velocity_y * delta_seconds;
simulate_ball(delta_seconds);
}

if (ball_x - ball_radius < 0.0f) {
ball_x = ball_radius;
velocity_x = -velocity_x;
} else if (ball_x + ball_radius > canvas_width) {
ball_x = canvas_width - ball_radius;
velocity_x = -velocity_x;
}
EMSCRIPTEN_KEEPALIVE
uintptr_t update_and_get_state(float delta_seconds)
{
simulate_ball(delta_seconds);
return (uintptr_t)&ball;
}

if (ball_y - ball_radius < 0.0f) {
ball_y = ball_radius;
velocity_y = -velocity_y;
} else if (ball_y + ball_radius > canvas_height) {
ball_y = canvas_height - ball_radius;
velocity_y = -velocity_y;
}
EMSCRIPTEN_KEEPALIVE
uintptr_t get_ball_state_ptr(void)
{
return (uintptr_t)&ball;
}

EMSCRIPTEN_KEEPALIVE
float get_ball_x(void)
{
return ball_x;
return ball.x;
}

EMSCRIPTEN_KEEPALIVE
float get_ball_y(void)
{
return ball_y;
return ball.y;
}

EMSCRIPTEN_KEEPALIVE
float get_ball_radius(void)
{
return ball_radius;
return ball.radius;
}