https://github.com/jakobhellermann/bevy_mod_js_scripting
https://github.com/jakobhellermann/bevy_mod_js_scripting
Last synced: about 1 year ago
JSON representation
- Host: GitHub
- URL: https://github.com/jakobhellermann/bevy_mod_js_scripting
- Owner: jakobhellermann
- License: other
- Created: 2022-05-27T12:41:42.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2023-01-01T10:04:54.000Z (over 3 years ago)
- Last Synced: 2025-04-12T23:54:25.303Z (about 1 year ago)
- Language: Rust
- Size: 485 KB
- Stars: 86
- Watchers: 4
- Forks: 6
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# bevy_mod_js_scripting
`bevy_mod_js_scripting` is an experimental scripting integration crate for writing javascript/typescript scripts and running them
in `bevy` with full access to the ECS values like components and resources.
## Example
```ts
// type safe access to resources and values
type Scoreboard = { score: number };
const Scoreboard: BevyType = { typeName: "breakout::Scoreboard" };
// script-local variables can be used for easy cross-frame state
let i = 0;
function run() {
// increment score every 60 frames
if(i % 60 == 0) {
let score = world.resource(Scoreboard)!;
score.score += 1;
// logging works via `trace`, `debug`, `info`, `warn`, `error`
info(score.score);
}
// query components
for (const item of world.query(Transform, Aabb)) {
let [transform, aabb] = item.components;
info("Translation:", transform.translation.toString());
info("AABB Center:", aabb.center.toString());
// call methods on value references (requires app code setup, see headless.rs)
let normalized = transform.scale.normalize();
}
}
export default {
// execute the `run` function in the update stage
update: run,
}
```
More examples can be found in the [examples](./examples/) folder.
Also check out the [punchy wiki page](https://github.com/fishfolks/punchy/wiki/Scripting) on scripting, which uses `bevy_mod_js_scripting`.
## Current Status
Currently supported operations are
- resource access (`world.resource(Time)`)
- world information (`world.components`, `world.resources`, `world.entities`)
- queries (`world.query(Ball, Velocity).map(({ entity, components }) => components[1])`)
- component insertion (`world.insert(value)`)
- dealing with ecs value references (`Value.create`, `Value.patch`)
## Design decisions
Types
In `bevy_ecs`, the common methods for dealing with ECS values all take a type parameter, like
```rs
world.resource::(); // or
world.query<(Entity, &Component)>();
```
Ideally we would be able to write
```ts
let time = world.resource
Instead, what we need to do is write `type` definition with an associated variable of type `BevyType`, which contains the referenced type's type name.
```ts
type Transform = {
translation: Vec3,
rotation: Quat,
scale: Vec3,
};
const Transform: BevyType = {
typeName: "bevy_transform::components::global_transform::GlobalTransform"
};
// `world.resource` is typed so that typescript can infer `transform` to be of type `Transform | undefined`
let transform = world.resource(Transform);
```
Similarly, queries list their types like
```ts
for (item of world.query(Ball, Velocity)) {
info(item.entity);
let [_ball, velocity] = item.components;
// velocity is properly typed
}
```
Currently, there is a pregenerated list of bevy types in [./types/bevy_types.ts](./types/bevy_types.ts), and you can also just define your own ones.
In the future we may include a utility for automatically generating the typescript definitions for your game in a `build.rs` script, so that you don't need to manually write or re-generate them.
Javascript Values & ECS Value references
When you call `world.resource` (or any other method returning references to ECS values), what you get is not just a simple javascript object corresponding to the rust value, but instead a `Proxy` which defers all accesses/modifications to the actual value inside the bevy world.
Only leaf values, like `transform.translation.x`, which can be natively represented as a javascript primitive, are automatically converted to/from the rust representation on gets and sets.
This means that
```ts
let transform = world.resource(Transform);
let translation = transform.translation;
// typeof translation.x == "number"
translation.x = 3.0;
```
If you want to create a new value reference, for example for inserting a new resource, the current APIs to do that are `Value.create` and `Value.patch`.
```ts
let transform = Value.create(Transform);
let vec3 = Value.create(Vec3, { x: 0.0, y: 1.0, z: 2.0 });
transform.translation = vec3;
world.insertResource(Transform, transform);
```
Expect to see changes in this area as we figure out the best way to deal with the interaction of javascript objects and value references.
## Web support
`bevy_mod_js_scripting` can run in the browser using its native javascript execution environment.
To try it out, download and configure [wasm-server-runner](https://github.com/jakobhellermann/wasm-server-runner) and run
```sh
cargo run --example breakout --target wasm32-unknown-unknown
```