web-sys
.node.js
with npm
tools installed (https://nodejs.org/)wasm-pack
toolset installed (https://rustwasm.github.io/wasm-pack/installer/)Create Rust + WASM project with
bash
npm init rust-webpack <path>
where path
is path to empty folder where your project will be created by this
command.
Then add this record into your /crate/Cargo.toml
file:
toml
[dependencies]
oxygengine = { version = "0.3", features = ["web-composite-game"] }
where web-composite-game
means that you want to use those
modules of Oxygen game engine, that gives you all features needed to easly make
an HTML5 web game with composite renderer.
You may also select which exact features you need, excluding those which you're
not gonna use. For example: web-composite-game
feature by default enables
these features: composite-renderer
, input
, network
. So if you just want to
make a movie-like animation then you don't need any input or networking, so you
will want to add this record instead:
toml
[dependencies.oxygengine]
version = "0.3"
features = [
"web",
"composite-renderer",
"oxygengine-composite-renderer-backend-web"
]
which means you want to use composite renderer with web backend and produce app
for web target.
bash
npm start
/dist
folder):
bash
npm run build
bash
cd /crate
cargo build --all --target wasm32-unknown-unknown
/index.html
:
```html
where
screen` canvas is our target fullpage game screen where game will be
rendered onto.
/crate/src/lib.rs
:
```rust
extern crate oxygengine;
use oxygengine::prelude::; use wasm_bindgen::prelude::;
static ALLOC: weealloc::WeeAlloc = weealloc::WeeAlloc::INIT;
// component that tags entity as moved with keyboard.
pub struct KeyboardMovementTag;
impl Component for KeyboardMovementTag {
// tag components are empty so they use NullStorage
.
type Storage = NullStorage
// component that tells the speed of entity.
pub struct Speed(pub Scalar);
impl Component for Speed {
// not all entities has speed so we use VecStorage
.
type Storage = VecStorage
// system that moves tagged entities. pub struct KeyboardMovementSystem;
impl<'s> System<'s> for KeyboardMovementSystem { type SystemData = ( // we will read input. Read<'s, InputController>, // we will read delta time from app lifecycle. ReadExpect<'s, AppLifeCycle>, // we will read speed components. ReadStorage<'s, Speed>, // we will filter by tag. ReadStorage<'s, KeyboardMovementTag>, // we will write to transforms. WriteStorage<'s, CompositeTransform>, );
fn run(
&mut self,
(input, lifecycle, speed, keyboard_movement, mut transforms): Self::SystemData,
) {
let dt = lifecycle.delta_time_seconds() as Scalar;
let hor = -input.axis_or_default("move-left") + input.axis_or_default("move-right");
let ver = -input.axis_or_default("move-up") + input.axis_or_default("move-down");
let offset = Vec2::new(hor, ver);
for (_, speed, transform) in (&keyboard_movement, &speed, &mut transforms).join() {
transform.set_translation(transform.get_translation() + offset * speed.0 * dt);
}
}
}
pub struct GameState;
impl State for GameState { fn onenter(&mut self, world: &mut World) { // create entity with camera to view scene. world .createentity() .with(CompositeCamera::new(CompositeScalingMode::CenterAspect)) .with(CompositeTransform::scale(400.0.into())) .build();
// create player entity.
let player = world
.create_entity()
.with(CompositeRenderable(
Rectangle {
color: Color::red(),
rect: [-50.0, -50.0, 100.0, 100.0].into(),
}
.into(),
))
.with(CompositeTransform::default())
.with(KeyboardMovementTag)
.with(Speed(100.0))
.build();
// create eye attached to player.
world
.create_entity()
.with(CompositeRenderable(
Rectangle {
color: Color::yellow(),
rect: [-10.0, -10.0, 20.0, 20.0].into(),
}
.into(),
))
.with(CompositeTransform::translation((-20.0).into()))
.with(Parent(player))
.build();
// create hint text.
world
.create_entity()
.with(CompositeRenderable(
Text {
color: Color::white(),
font: "Verdana".into(),
align: TextAlign::Center,
text: "Use WSAD to move".into(),
position: 0.0.into(),
size: 24.0,
}
.into(),
))
.with(CompositeTransform::translation([0.0, 100.0].into()))
.with(CompositeRenderDepth(-1.0))
.build();
}
}
pub fn run() -> Result<(), JsValue> { setpanichook();
// Application build phase - install all systems and resources and setup them.
let app = App::build()
// install core module assets managment.
.with_bundle(
oxygengine::core::assets::bundle_installer,
(WebFetchEngine::default(), |assets| {
// register assets protocols from composite renderer module.
oxygengine::composite_renderer::protocols_installer(assets);
}),
)
// install input managment.
.with_bundle(oxygengine::input::bundle_installer, |input| {
// register input devices.
input.register(WebKeyboardInputDevice::new(get_event_target_document()));
// input.register(WebMouseInputDevice::new(get_event_target_by_id("screen")));
// map input axes and triggers to devices.
input.map_axis("move-up", "keyboard", "KeyW");
input.map_axis("move-down", "keyboard", "KeyS");
input.map_axis("move-left", "keyboard", "KeyA");
input.map_axis("move-right", "keyboard", "KeyD");
// input.map_axis("mouse-x", "mouse", "x");
// input.map_axis("mouse-y", "mouse", "y");
// input.map_trigger("mouse-left", "mouse", "left");
})
// install composite renderer.
.with_bundle(
oxygengine::composite_renderer::bundle_installer,
WebCompositeRenderer::with_state(
get_canvas_by_id("screen"), // canvas target.
RenderState::new(Some(Color::black())),
),
)
.with_system(KeyboardMovementSystem, "keyboard_movement", &[])
.build(GameState, WebAppTimer::default());
// Application run phase - spawn runner that ticks our app.
AppRunner::new(app).run::<WebAppRunner, _>()?;
Ok(())
}
fn setpanichook() { #[cfg(feature = "consoleerrorpanichook")] consoleerrorpanichook::set_once(); } ```