twors 0.3.0

A simple Rust 2D game engine based on canvas and WASM
Documentation
# TwoRS

![development](https://img.shields.io/badge/maintenance%20status-actively%20developed-brightgreen)
![crates.io](https://img.shields.io/crates/v/twors)

Easily render 2D graphics in a canvas using WASM - entirely powered by Rust!!! 🦀

## 😎 Why Rust/WASM?

[WASM's use case](https://webassembly.org/docs/use-cases/) is either the reuse of code written in another language or to offload
heavy computations to the near-native execution speeds of WASM.

However this is not free - traversing the WASM boundary means going trough [some glue code](https://rustwasm.github.io/wasm-bindgen/contributing/design/index.html)
and copying data from JS to the [WASM module memory](https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Concepts)
and back. In the case of strings it's even more expensive - because
[JS strings are UTF-16](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#utf-16_characters_unicode_code_points_and_grapheme_clusters)
and [Rust strings use UTF-8](https://doc.rust-lang.org/std/string/struct.String.html) any string
passtrough needs to go trough a copy
[AND a re-encode](https://rustwasm.github.io/wasm-bindgen/reference/types/str.html?highlight=utf-16#utf-16-vs-utf-8).

`twors` doesn't have any complicated physics to offload to WASM at this point in time - neither
is it making use of some advanced pre-existing Rust library. So why Rust then?

⭐ *Simply because Rust is the best!!!* ⭐

## ⚡ Quick start

- To quickly play with `twors` you can make use of the `examples/playground`
  crate in this repo - it contains code for moving a rectangle via the WASD keys.

```bash
git clone https://github.com/vimlucid/twors
cd twors

cargo install cargo-make
cargo make watch # this assumes you have the `cp` command - e.g. it won't work in Windows CMD

# edit the source code in examples/playground and manually refresh http://localhost:8080
```

- To add `twors` to your own crate - check out the [Installation and build]#%EF%B8%8F-installation-and-build guide

## ✨ Features

✅ `TwoRS`'s goal is to be simple. It aims to provide:

- [x] An adaptive canvas resolution (on canvas resize)
- [x] A main loop with precalculated [delta time]https://en.wikipedia.org/wiki/Delta_timing
- [x] Keyboard/mouse input handling
- [ ] A convenient component system for code organization
  - [x] Basic component support with `on_init` and `on_update` lifecycle methods
  - [ ] Transform
    - [x] Translation & scale
    - [ ] Rotation
    - [ ] Inheritance (so a component moves with its parent)
  - [x] Dynamic component instantiation and removal
  - [ ] Interacting with components from other components via `get_component_by_id` 
- [ ] Rectangle-based 2D collision detection
- [ ] Utilities
  - [ ] Linear/smooth interpolation (for animations over time)
  - [ ] Bezier curves
  - [ ] Factories for various shapes & styling
    - [x] Square & rectangle
    - [ ] ...
- [ ] Benchmark

❌ `TwoRS` does not **currently** have (and aim) to provide:

- a physics engine
- an algorithm library (e.g. pathfinding)
- a sound library
- 🎨 **\*a UI library** (check the note below)

🎨 **\* a UI library**
- If your project requirements allow it you can implement your UI in Rust via framework like
  [Dioxus]https://dioxuslabs.com/, [Leptos]https://leptos.dev/,
  [Sycamore]https://github.com/sycamore-rs/sycamore and [Yew]https://yew.rs/ to name a few.
  - These, however, are the Rust analog to JS frameworks like React - they **will not allow you
    to create a UI that's rendered directly in the canvas**. If the UI can live in the DOM then
    they are a great choice!
- Still - UI is an important part of any application and not having support for canvas UI might
  be a dealbreaker. I might open source another library (based on this one) for simple canvas UI
  support after I am done with the initial feature set of `TwoRS`


## 🛠️ Installation and build

Compiling a WASM library is a bit more involved than simply executing `cargo run`, but
it's pretty straightforward if you know the steps.

We'll basically:
- set up our crate for WASM compilation
- add the bare minimum code to run the `twors` engine
- add an `index.html` with some JavaScript (just enough to run our WASM library)
- serve all of this goodness via an HTTP server

Once we do this **all of the remaining code can be written entirely in Rust!** \
You are of course free to mix and match as you like.

### **Step 1** - Create a `lib` crate

In order to compile Rust into WASM via [wasm-pack](https://github.com/rustwasm/wasm-pack)
(an amazing WASM compilation helper) it's necessary to have a `lib` crate first

```bash
cargo new twors-demo --lib
```

### **Step 2** - Install `twors`, `wasm_bindgen` and `console_log`

- A **browser-capable** logging backend crate like
  [console_log]https://crates.io/crates/console_log is needed to enable
  logging in the browser console
    - You will also need a logging frontend crate like [log]https://crates.io/crates/log to
      be able to print messages yourself.
- [wasm_bindgen]https://crates.io/crates/wasm-bindgen will generate the necessary JS/Rust
  glue to enhance what can go trough the WASM/JS boundary.
  - Without it our Rust code that's
    called from JS (and vice-versa) would be able to only take numbers as arguments and return
    numbers as a result. With this crate we
    [enable a lot more types]https://rustwasm.github.io/wasm-bindgen/reference/types.html
    in our JS/WASM boundary API.

```bash
cargo add twors
cargo add wasm_bindgen
cargo add console_log
```

### **Step 3** - Create and run the `twors` engine.

- For now we won't add any components, so we'll see a blank canvas when
  we open our application in the browser.

```rust
use twors::{Engine, Result};
use wasm_bindgen::prelude::wasm_bindgen;
use std::collections::HashMap;
use console_log;

// The "wasm_bindgen" attribute will generate glue both on the JS and on the WASM sides.
// Passing Rust types like `&str` from JS is thanks to the magic of `wasm_bindgen`.
#[wasm_bindgen]
pub fn entry(canvas_id: &str) -> Result<()> {
    console_log::init().unwrap(); // Setup logger frontends to use our browser-capable logger.

    // Pass a list of components to render on the canvas.
    // We'll add an index.html file with said canvas later.
    let engine = Engine::new(canvas_id, HashMap::default())?;
    engine.run()?;

    Ok(())
}
```

### **Step 4** - Change the crate type to a [dynamic library intended to be loaded from another language]https://doc.rust-lang.org/reference/linkage.html

Add a `lib` section to your `Cargo.toml` file (if you don't already have one) and set
the `crate-type` property to `cdylib`.

The `wasm32-unknown-unknown` compile target (what `wasm-pack` makes the Rust compiler use) will
detect this and produce a WASM library.

```text
[lib]
crate-type = ["cdylib"]
```

### **Step 5** - Install `wasm-pack` and build your WASM library

The following `wasm-pack --build` command will produce a `pkg` folder in your crate's root -
this folder will contain the WASM library as well as the JS part of the glue code that's
needed for the WASM-JS communication.

```bash
cargo install wasm-pack
wasm-pack build --target web
```

### **Step 6** - Copy `examples/assets/index.html` to the `pkg` folder in your crate's root

We have compiled our WASM library - now we need to call it from JS. The example `index.html`
will:
- create a full screen canvas
- run our Rust `entry` method (that we created earlier) by passing it the canvas ID.

```bash
# navigate to your crate root and execute the following
cp ./examples/assets/index.html ./pkg
```

> **NOTE:** If you don't have the `cp` command (e.g. if you are using Windows CMD) you can always copy
> the `index.html` manually.

### **Step 7** - Serve the `pkg` directory at the crate root

You will need to serve the WASM library with the `application/wasm` MIME type or the browser
will refuse to run it. A great server that does this automatically is
[miniserve](https://github.com/svenstaro/miniserve)

```bash
cargo intall miniserve
miniserve ./pkg --index index.html
```

## 🧱 Fundamentals

- TODO

## 💻 Development

Experiments and manual testing during development can be done in the `examples/playground` crate.

```bash
# convenience scripts - see "Makefile.toml" for full list of commands
cargo install cargo-make

# run local pre-commit checks - will be run on "build" automatically
cargo make install-git-hooks

cargo make build # build the "playground" crate as a WASM module
cargo make serve # like "build", but will also start a HTTP server
cargo make watch # like "serve", but will restart the server on changes

# other commands
cargo make test
cargo make clean
cargo make format
cargo make licenses # update licenses.html (run after dependency addition/removal)
```

### Notes

- Make sure to use  `wasm_assert!` instead of `assert!` in non-test code to see error messages
  in the browser console.