CONNECTING...

Documentation

Everything you need to build games for Emberware

Developer Book

The complete Emberware developer documentation, hosted as an mdBook.

Open Full Documentation

Common Patterns

Minimal Game

#![no_std]
#![no_main]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_: &PanicInfo) -> ! {
    core::arch::wasm32::unreachable()
}

#[link(wasm_import_module = "env")]
extern "C" {
    fn draw_rect(x: f32, y: f32, w: f32, h: f32, c: u32);
}

#[no_mangle]
pub extern "C" fn init() {}

#[no_mangle]
pub extern "C" fn update() {}

#[no_mangle]
pub extern "C" fn render() {
    unsafe { draw_rect(100.0, 100.0, 50.0, 50.0, 0xFF6B6BFF); }
}
#include 

/* FFI imports */
EWZX_IMPORT void draw_rect(float x, float y, float w, float h, uint32_t c);

/* Game lifecycle */
EWZX_EXPORT void init(void) {}

EWZX_EXPORT void update(void) {}

EWZX_EXPORT void render(void) {
    draw_rect(100.0f, 100.0f, 50.0f, 50.0f, 0xFF6B6BFF);
}
// FFI imports
extern fn draw_rect(x: f32, y: f32, w: f32, h: f32, c: u32) void;

// Game lifecycle
export fn init() void {}

export fn update() void {}

export fn render() void {
    draw_rect(100.0, 100.0, 50.0, 50.0, 0xFF6B6BFF);
}

Rollback-Safe State

// All game state in static variables
static mut PLAYER_X: f32 = 100.0;
static mut PLAYER_Y: f32 = 200.0;
static mut SCORE: u32 = 0;

#[no_mangle]
pub extern "C" fn update() {
    unsafe {
        // Use synchronized random()
        let spawn_x = (random() % 320) as f32;

        // Deterministic updates
        PLAYER_X += left_stick_x(0) * 5.0;
    }
}

// Multiplayer just works!
/* All game state in static variables */
static float player_x = 100.0f;
static float player_y = 200.0f;
static uint32_t score = 0;

EWZX_EXPORT void update(void) {
    /* Use synchronized random_u32() */
    float spawn_x = (float)(random_u32() % 320);

    /* Deterministic updates */
    player_x += left_stick_x(0) * 5.0f;
}

/* Multiplayer just works! */
// All game state in module-level variables
var player_x: f32 = 100.0;
var player_y: f32 = 200.0;
var score: u32 = 0;

export fn update() void {
    // Use synchronized random_u32()
    const spawn_x: f32 = @floatFromInt(random_u32() % 320);

    // Deterministic updates
    player_x += left_stick_x(0) * 5.0;
}

// Multiplayer just works!

Asset Loading

static PLAYER_MESH: &[u8] = include_bytes!("assets/player.ewzmesh");
static GRASS_TEX: &[u8] = include_bytes!("assets/grass.ewztex");

fn init() {
    let player = load_zmesh(
        PLAYER_MESH.as_ptr() as u32,
        PLAYER_MESH.len() as u32
    );
    let grass = load_ztex(
        GRASS_TEX.as_ptr() as u32,
        GRASS_TEX.len() as u32
    );
}
/* Embed assets at compile time */
extern const unsigned char player_ewzmesh_data[];
extern const unsigned int player_ewzmesh_size;
extern const unsigned char grass_ewztex_data[];
extern const unsigned int grass_ewztex_size;

EWZX_EXPORT void init(void) {
    uint32_t player = load_zmesh(
        (uint32_t)player_ewzmesh_data,
        player_ewzmesh_size
    );
    uint32_t grass = load_ztex(
        (uint32_t)grass_ewztex_data,
        grass_ewztex_size
    );
}
const player_mesh = @embedFile("assets/player.ewzmesh");
const grass_tex = @embedFile("assets/grass.ewztex");

export fn init() void {
    const player = load_zmesh(
        @intFromPtr(player_mesh.ptr),
        player_mesh.len
    );
    const grass = load_ztex(
        @intFromPtr(grass_tex.ptr),
        grass_tex.len
    );
}

Input Handling

fn update() {
    // Button press (one-time trigger)
    if button_pressed(0, BUTTON_A) != 0 {
        player.jump();
    }

    // Analog stick with deadzone
    let stick_x = left_stick_x(0);
    let deadzone = 0.15;
    if stick_x.abs() > deadzone {
        player.x += stick_x * MOVE_SPEED * delta_time();
    }
}
EWZX_EXPORT void update(void) {
    /* Button press (one-time trigger) */
    if (button_pressed(0, EWZX_BUTTON_A)) {
        player_jump();
    }

    /* Analog stick with deadzone */
    float stick_x = left_stick_x(0);
    float deadzone = 0.15f;
    if (ewzx_absf(stick_x) > deadzone) {
        player_x += stick_x * MOVE_SPEED * delta_time();
    }
}
export fn update() void {
    // Button press (one-time trigger)
    if (button_pressed(0, Button.a) != 0) {
        player_jump();
    }

    // Analog stick with deadzone
    const stick_x = left_stick_x(0);
    const deadzone = 0.15;
    if (@abs(stick_x) > deadzone) {
        player_x += stick_x * MOVE_SPEED * delta_time();
    }
}