├── .gitignore ├── Makefile ├── README.md ├── index.html ├── roguelike.wasm └── roguelike ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | target 3 | *.lock 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | cd roguelike && cargo +nightly build --release --target wasm32-unknown-unknown 3 | cp roguelike/target/wasm32-unknown-unknown/release/roguelike.wasm . 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | This is a simple experiment to show how to make a simple roguelike interface using Rust and Web Assembly. There's not much to it. 4 | 5 | https://richardanaya.github.io/rust-roguelike/index.html 6 | 7 | Interesting detail: Safe Rust doesn't allow mutable static globals. So you have to create a global mutex that holds a mutable value. That way all your library entrypoint functions sharing that global data are thread safe. Turns out there is a cargo package lazy_static that makes this easy. 8 | 9 | Feel free to leave an issue if you see a more idiomatic way of doing something. 10 | 11 | # How to build 12 | 13 | ```bash 14 | curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly 15 | rustup update nightly 16 | rustup target add wasm32-unknown-unknown --toolchain=nightly 17 | make 18 | ``` 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 12 | 13 |
Use arrow keys to move ↑↓→ ←
14 | 15 | 47 | -------------------------------------------------------------------------------- /roguelike.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardanaya/rust-roguelike/fcf6235d46894c8890baa0f9fbf95f490da7d73a/roguelike.wasm -------------------------------------------------------------------------------- /roguelike/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "roguelike" 3 | version = "0.1.0" 4 | authors = ["Richard "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | lazy_static = "1.0.0" 11 | -------------------------------------------------------------------------------- /roguelike/src/lib.rs: -------------------------------------------------------------------------------- 1 | //using special macros for global state, see below 2 | #[macro_use] 3 | extern crate lazy_static; 4 | use std::sync::Mutex; 5 | 6 | //this represents our external js function 7 | extern { 8 | fn put_character(x: i32,y: i32,char: i32,r: i32,g: i32,b: i32); 9 | } 10 | 11 | const KEY_LEFT: i32 = 37; 12 | const KEY_UP: i32 = 38; 13 | const KEY_RIGHT: i32 = 39; 14 | const KEY_DOWN: i32 = 40; 15 | 16 | struct World { 17 | view_width: i32, 18 | view_height: i32, 19 | player_position:(i32,i32) 20 | } 21 | 22 | //we can't have mutable statics so we need this mutex that holds it 23 | //using a macro from a crate lazy_static 24 | lazy_static! { 25 | static ref WORLD: Mutex = Mutex::new( 26 | World { 27 | view_width: 0, 28 | view_height: 0, 29 | player_position : (1,1) 30 | } 31 | ); 32 | } 33 | 34 | //a simple safe wrapper around calling the JS function 35 | fn draw_character(x: i32,y: i32, char: u8,r: u8,g: u8,b: u8) -> () { 36 | unsafe { 37 | put_character(x,y,char as i32,r as i32,g as i32,b as i32); 38 | } 39 | } 40 | 41 | fn draw_world(world:&World){ 42 | //draw grass 43 | for y in 0..world.view_height { 44 | for x in 0..world.view_width { 45 | draw_character(x,y,46,0,255,0); 46 | } 47 | } 48 | 49 | //draw player 50 | draw_character(world.player_position.0,world.player_position.1,64,255,255,255); 51 | } 52 | 53 | #[no_mangle] 54 | pub fn start(width: i32, height: i32) -> () { 55 | //get the world, this gets unlocked on deallocation 56 | let world = &mut WORLD.lock().unwrap(); 57 | world.view_width = width; 58 | world.view_height = height; 59 | draw_world(world); 60 | } 61 | 62 | #[no_mangle] 63 | pub fn key_down(c: i32) -> () { 64 | //get the world, this gets unlocked on deallocation 65 | let world = &mut WORLD.lock().unwrap(); 66 | let modifier = match c { 67 | KEY_LEFT => (-1,0), 68 | KEY_RIGHT => (1,0), 69 | KEY_DOWN => (0,1), 70 | KEY_UP => (0,-1), 71 | _ => (0,0) 72 | }; 73 | world.player_position = (world.player_position.0+modifier.0,world.player_position.1+modifier.1); 74 | draw_world(world); 75 | } 76 | --------------------------------------------------------------------------------