├── .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 |
--------------------------------------------------------------------------------