231 lines
6.9 KiB
Markdown
231 lines
6.9 KiB
Markdown
# bevy_scriptum 📜
|
|
|
|

|
|
|
|
bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language.
|
|
|
|
### Supported scripting languages/runtimes
|
|
|
|
| language/runtime | cargo feature | documentation chapter |
|
|
| ---------------- | ------------- | --------------------------------------------------------------- |
|
|
| 🌙 LuaJIT | `lua` | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) |
|
|
| 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) |
|
|
| 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) |
|
|
|
|
Documentation book is available [here](https://jarkonik.github.io/bevy_scriptum/) 📖
|
|
|
|
Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑💻
|
|
|
|
bevy_scriptum's main advantages include:
|
|
|
|
- low-boilerplate
|
|
- easy to use
|
|
- asynchronicity with a promise-based API
|
|
- flexibility
|
|
- hot-reloading
|
|
|
|
Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile it.
|
|
|
|
All you need to do is register callbacks on your Bevy app like this:
|
|
|
|
```rust
|
|
use bevy::prelude::*;
|
|
use bevy_scriptum::prelude::*;
|
|
use bevy_scriptum::runtimes::lua::prelude::*;
|
|
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_scripting::<LuaRuntime>(|runtime| {
|
|
runtime.add_function(String::from("hello_bevy"), || {
|
|
println!("hello bevy, called from script");
|
|
});
|
|
})
|
|
.run();
|
|
```
|
|
|
|
And you can call them in your scripts like this:
|
|
|
|
```lua
|
|
hello_bevy()
|
|
```
|
|
|
|
Every callback function that you expose to the scripting language is also a Bevy system, so you can easily query and mutate ECS components and resources just like you would in a regular Bevy system:
|
|
|
|
```rust
|
|
use bevy::prelude::*;
|
|
use bevy_scriptum::prelude::*;
|
|
use bevy_scriptum::runtimes::lua::prelude::*;
|
|
|
|
#[derive(Component)]
|
|
struct Player;
|
|
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_scripting::<LuaRuntime>(|runtime| {
|
|
runtime.add_function(
|
|
String::from("print_player_names"),
|
|
|players: Query<&Name, With<Player>>| {
|
|
for player in &players {
|
|
println!("player name: {}", player);
|
|
}
|
|
},
|
|
);
|
|
})
|
|
.run();
|
|
```
|
|
|
|
You can also pass arguments to your callback functions, just like you would in a regular Bevy system - using `In` structs with tuples:
|
|
|
|
```rust
|
|
use bevy::prelude::*;
|
|
use bevy_scriptum::prelude::*;
|
|
use bevy_scriptum::runtimes::lua::prelude::*;
|
|
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_scripting::<LuaRuntime>(|runtime| {
|
|
runtime.add_function(
|
|
String::from("fun_with_string_param"),
|
|
|In((x,)): In<(String,)>| {
|
|
println!("called with string: '{}'", x);
|
|
},
|
|
);
|
|
})
|
|
.run();
|
|
```
|
|
|
|
which you can then call in your script like this:
|
|
|
|
```lua
|
|
fun_with_string_param("Hello world!")
|
|
```
|
|
|
|
### Usage
|
|
|
|
Add the following to your `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
bevy_scriptum = { version = "0.9", features = ["lua"] }
|
|
```
|
|
|
|
or execute `cargo add bevy_scriptum --features lua` from your project directory.
|
|
|
|
You can now start exposing functions to the scripting language. For example, you can expose a function that prints a message to the console:
|
|
|
|
```rust
|
|
use bevy::prelude::*;
|
|
use bevy_scriptum::prelude::*;
|
|
use bevy_scriptum::runtimes::lua::prelude::*;
|
|
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_scripting::<LuaRuntime>(|runtime| {
|
|
runtime.add_function(
|
|
String::from("my_print"),
|
|
|In((x,)): In<(String,)>| {
|
|
println!("my_print: '{}'", x);
|
|
},
|
|
);
|
|
})
|
|
.run();
|
|
```
|
|
|
|
Then you can create a script file in `assets` directory called `script.lua` that calls this function:
|
|
|
|
```lua
|
|
my_print("Hello world!")
|
|
```
|
|
|
|
And spawn an entity with attached `Script` component with a handle to a script source file:
|
|
|
|
```rust
|
|
use bevy::prelude::*;
|
|
use bevy_scriptum::prelude::*;
|
|
use bevy_scriptum::runtimes::lua::prelude::*;
|
|
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_scripting::<LuaRuntime>(|runtime| {
|
|
runtime.add_function(
|
|
String::from("my_print"),
|
|
|In((x,)): In<(String,)>| {
|
|
println!("my_print: '{}'", x);
|
|
},
|
|
);
|
|
})
|
|
.add_systems(Startup,|mut commands: Commands, asset_server: Res<AssetServer>| {
|
|
commands.spawn(Script::<LuaScript>::new(asset_server.load("script.lua")));
|
|
})
|
|
.run();
|
|
```
|
|
|
|
You should then see `my_print: 'Hello world!'` printed in your console.
|
|
|
|
### Provided examples
|
|
|
|
You can also try running provided examples by cloning this repository and running `cargo run --example <example_name>_<language_name>`. For example:
|
|
|
|
```bash
|
|
cargo run --example hello_world_lua
|
|
```
|
|
|
|
The examples live in `examples` directory and their corresponding scripts live in `assets/examples` directory within the repository.
|
|
|
|
### Bevy compatibility
|
|
|
|
| bevy version | bevy_scriptum version |
|
|
| ------------ | --------------------- |
|
|
| 0.16 | 0.8-0.9 |
|
|
| 0.15 | 0.7 |
|
|
| 0.14 | 0.6 |
|
|
| 0.13 | 0.4-0.5 |
|
|
| 0.12 | 0.3 |
|
|
| 0.11 | 0.2 |
|
|
| 0.10 | 0.1 |
|
|
|
|
### Promises - getting return values from scripts
|
|
|
|
Every function called from script returns a promise that you can call `:and_then` with a callback function on. This callback function will be called when the promise is resolved, and will be passed the return value of the function called from script. For example:
|
|
|
|
```lua
|
|
get_player_name():and_then(function(name)
|
|
print(name)
|
|
end)
|
|
```
|
|
|
|
which will print out `John` when used with following exposed function:
|
|
|
|
```rust
|
|
use bevy::prelude::*;
|
|
use bevy_scriptum::prelude::*;
|
|
use bevy_scriptum::runtimes::lua::prelude::*;
|
|
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_scripting::<LuaRuntime>(|runtime| {
|
|
runtime.add_function(String::from("get_player_name"), || String::from("John"));
|
|
});
|
|
```
|
|
|
|
## Access entity from script
|
|
|
|
A variable called `entity` is automatically available to all scripts - it represents bevy entity that the `Script` component is attached to.
|
|
It exposes `index` property that returns bevy entity index.
|
|
It is useful for accessing entity's components from scripts.
|
|
It can be used in the following way:
|
|
|
|
```lua
|
|
print("Current entity index: " .. entity.index)
|
|
```
|
|
|
|
`entity` variable is currently not available within promise callbacks.
|
|
|
|
### Contributing
|
|
|
|
Contributions are welcome! Feel free to open an issue or submit a pull request.
|
|
|
|
### License
|
|
|
|
bevy_scriptum is licensed under either of the following, at your option:
|
|
Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
|