React + Rust + Wasm: Game Engine

Neo Quest | Nikhil Gupta

Neo Quest | Nikhil Gupta / November 14, 2022

2 min read

Summary

In this article, we will use Bevy game engine in our React application from our Rust WASM library to draw a 2D Image. We will build on the previous tutorial available here.

Add bevy to the dependencies

First of all, let's add bevy as a dependency:

# Cargo.toml
[package]
name = "rust-wasm-lib"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

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

[dependencies]
js-sys = "0.3.60"
wasm-bindgen = "0.2.83"
photon-rs = "0.3.1"
bevy = "0.9.0"

[dependencies.web-sys]
version = "0.3.4"
features = [
  'Document',
  'Element',
  'HtmlElement',
  'Node',
  'Window',
  'CanvasRenderingContext2d',
  'HtmlCanvasElement',
  'WebGlBuffer',
  'WebGlVertexArrayObject',
  'WebGl2RenderingContext',
  'WebGlProgram',
  'WebGlShader'
]

Run a Bevy App using Rust

Last time, we exposed a function to alter an image using photon APIs. Now, let's modify our lib.rs to expose another function that will create a simple Bevy app.

Import Bevy

...
use bevy::prelude::*;
...

Create a Bevy App

#[wasm_bindgen]
pub fn run_bevy_app() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup) # Defined below
        .run();
}

Setup commands for camera and 2D image

pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/icon.png"),
        ..default()
    });
}

Build the new wasm library

Let's run wasm-pack again to build the updated library

wasm-pack build --target web

Setup the image path correctly

In the setup command, we passed a path to the image as branding/icon.png. For bevy to find this image, let's create a folder named assets inside public with the image stored under branding/icon.png.

Call the new function from the demo app

Finally, let's add a button to load the wasm and call run_bevy_app function from our App.ts file like so:

// App.ts

import init, { run_bevy_app } from "rust-wasm-lib";
import './App.css';

function App() {
  const runBevyApp = async () => {
    await init();
    run_bevy_app();
  };
  
  return (
    <div className="App">
      <button onClick={runBevyApp}>Run Bevy App</button>
    </div>
  );
}

export default App;

Now, if you run the updated app and click on Run Bevy App, you should see a canvas with the image in the centre. :)

If you liked this article, subscribe here to get the complete code and updates for the entire collection: Rust & Wasm.