diff --git a/book/src/ruby/builtin_types.md b/book/src/ruby/builtin_types.md new file mode 100644 index 0000000..e376a33 --- /dev/null +++ b/book/src/ruby/builtin_types.md @@ -0,0 +1,87 @@ +# Builtin types + +bevy_scriptum provides following types that can be used in Lua: + +- ```Vec3``` +- ```BevyEntity``` + +## Vec3 + +### Constructor + +`Vec3(x: number, y: number, z: number)` + +### Properties + +- `x: number` +- `y: number` +- `z: number` + + +### Example Lua usage + +``` +my_vec = Vec3(1, 2, 3) +set_translation(entity, my_vec) +``` + +### Example Rust usage + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("set_translation"), set_translation); + }) + .run(); +} + +fn set_translation( + In((entity, translation)): In<(BevyEntity, BevyVec3)>, + mut entities: Query<&mut Transform>, +) { + let mut transform = entities.get_mut(entity.0).unwrap(); + transform.translation = translation.0; +} +``` + +## BevyEntity + +### Constructor + +None - instances can only be acquired by using built-in `entity` global variable. + +### Properties + +- `index: integer` + +### Example Lua usage + +```lua +print(entity.index) +pass_to_rust(entity) +``` + +### Example Rust usage + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("pass_to_rust"), |In((entity,)): In<(BevyEntity,)>| { + println!("pass_to_rust called with entity: {:?}", entity); + }); + }) + .run(); +} +``` diff --git a/book/src/ruby/builtin_variables.md b/book/src/ruby/builtin_variables.md new file mode 100644 index 0000000..558aa39 --- /dev/null +++ b/book/src/ruby/builtin_variables.md @@ -0,0 +1,13 @@ +# Builtin variables + +## entity + +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. diff --git a/book/src/ruby/calling_rust_from_script.md b/book/src/ruby/calling_rust_from_script.md new file mode 100644 index 0000000..3b7321e --- /dev/null +++ b/book/src/ruby/calling_rust_from_script.md @@ -0,0 +1,121 @@ +# Calling Rust from Lua + +To call a rust function from Lua first you need to register a function +within Rust using builder pattern. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + // `runtime` is a builder that you can use to register functions + }) + .run(); +} +``` + +For example to register a function called `my_rust_func` you can do the following: + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("my_rust_func"), || { + println!("my_rust_func has been called"); + }); + }) + .run(); +} +``` + +After you do that the function will be available to Lua code in your spawned scripts. + +```lua +my_rust_func() +``` + +Registered functions can also take parameters. A parameter can be any type +that implements `FromLua`. + +Since a registered callback function is a Bevy system, the parameters are passed +to it as `In` struct with tuple, which has to be the first parameter of the closure. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("func_with_params"), |args: In<(String, i64)>| { + println!("my_rust_func has been called with string {} and i64 {}", args.0.0, args.0.1); + }); + }) + .run(); +} +``` + +To make it look nicer you can destructure the `In` struct. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("func_with_params"), |In((a, b)): In<(String, i64)>| { + println!("my_rust_func has been called with string {} and i64 {}", a, b); + }); + }) + .run(); +} +``` + +The above function can be called from Lua + +```lua +func_with_params("abc", 123) +``` + +## Return value via promise + +Any registered rust function that returns a value will retrurn a promise when +called within a script. By calling `:and_then` on the promise you can register +a callback that will receive the value returned from Rust function. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("returns_value"), || { + 123 + }); + }) + .run(); +} +``` + +```lua +returns_value():and_then(function (value) + print(value) -- 123 +end) +``` diff --git a/book/src/ruby/calling_script_from_rust.md b/book/src/ruby/calling_script_from_rust.md new file mode 100644 index 0000000..b543c2c --- /dev/null +++ b/book/src/ruby/calling_script_from_rust.md @@ -0,0 +1,67 @@ +# Calling Lua from Rust + +To call a function defined in Lua + +```lua +function on_update() +end +``` + +We need to acquire `LuaRuntime` resource within a bevy system. +Then we will be able to call `call_fn` on it, providing the name +of the function to call, `LuaScriptData` that has been automatically +attached to entity after an entity with script attached has been spawned +and its script evaluated, the entity and optionally some arguments. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn call_lua_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, + scripting_runtime: ResMut, +) { + for (entity, mut script_data) in &mut scripted_entities { + // calling function named `on_update` defined in lua script + scripting_runtime + .call_fn("on_update", &mut script_data, entity, ()) + .unwrap(); + } +} + +fn main() {} +``` + +We can also pass some arguments by providing a tuple or `Vec` as the last +`call_fn` argument. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn call_lua_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, + scripting_runtime: ResMut, +) { + for (entity, mut script_data) in &mut scripted_entities { + scripting_runtime + .call_fn("on_update", &mut script_data, entity, (123, String::from("hello"))) + .unwrap(); + } +} + +fn main() {} +``` + +They will be passed to `on_update` Lua function +```lua +function on_update(a, b) + print(a) -- 123 + print(b) -- hello +end +``` + +Any type that implements `IntoLua` can be passed as an argument withing the +tuple in `call_fn`. diff --git a/book/src/ruby/hello_world.md b/book/src/ruby/hello_world.md new file mode 100644 index 0000000..469f551 --- /dev/null +++ b/book/src/ruby/hello_world.md @@ -0,0 +1,66 @@ +# Hello World + +After you are done installing the required crates, you can start developing +your first game or application using bevy_scriptum. + +To start using the library you need to first import some structs and traits +with Rust `use` statements. + +For convenience there is a main "prelude" module provided called +`bevy_scriptum::prelude` and a prelude for each runtime you have enabled as +a create feature. + +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::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|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::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|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| { + commands.spawn(Script::::new(asset_server.load("script.lua"))); + }) + .run(); +} +``` + +You should then see `my_print: 'Hello world!'` printed in your console. diff --git a/book/src/ruby/installation.md b/book/src/ruby/installation.md new file mode 100644 index 0000000..4caaac9 --- /dev/null +++ b/book/src/ruby/installation.md @@ -0,0 +1,12 @@ +# Installation + +Add the following to your `Cargo.toml`: + +```toml +[dependencies] +bevy = "0.16" +bevy_scriptum = { version = "0.8", features = ["lua"] } +``` + +If you need a different version of bevy you need to use a matching bevy_scriptum +version according to the [bevy support matrix](../bevy_support_matrix.md) diff --git a/book/src/ruby/interacting_with_bevy.md b/book/src/ruby/interacting_with_bevy.md new file mode 100644 index 0000000..6e7637d --- /dev/null +++ b/book/src/ruby/interacting_with_bevy.md @@ -0,0 +1,75 @@ +# Interacting with bevy in callbacks + +Every registered function is also just a regular Bevy system. + +That allows you to do anything you would do in a Bevy system. + +You could for example create a callback system function that prints names +of all entities with `Player` component. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +#[derive(Component)] +struct Player; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("print_player_names"), + |players: Query<&Name, With>| { + for player in &players { + println!("player name: {}", player); + } + }, + ); + }) + .run(); +} +``` + +In script: + +```lua +print_player_names() +``` + +You can use functions that interact with Bevy entities and resources and +take arguments at the same time. It could be used for example to mutate a +component. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +#[derive(Component)] +struct Player { + health: i32 +} + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("hurt_player"), + |In((hit_value,)): In<(i32,)>, mut players: Query<&mut Player>| { + let mut player = players.single_mut(); + player.health -= hit_value; + }, + ); + }) + .run(); +} +``` + +And it could be called in script like: + +```lua +hurt_player(5) +``` diff --git a/book/src/ruby/lua.md b/book/src/ruby/lua.md new file mode 100644 index 0000000..891ffd4 --- /dev/null +++ b/book/src/ruby/lua.md @@ -0,0 +1,3 @@ +# Lua + +This chapter demonstrates how to work with bevy_scriptum when using Lua language runtime. diff --git a/book/src/ruby/spawning_scripts.md b/book/src/ruby/spawning_scripts.md new file mode 100644 index 0000000..41a49b4 --- /dev/null +++ b/book/src/ruby/spawning_scripts.md @@ -0,0 +1,40 @@ +# Spawning scripts + +To spawn a Lua script you will need to get a handle to a script asset using +bevy's `AssetServer`. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn my_spawner(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("my_script.lua"), + )); +} + +fn main() {} +``` + +After they scripts have been evaled by bevy_scriptum, the entities that they've +been attached to will get the `Script::` component stripped and instead +```LuaScriptData``` component will be attached. + +So to query scipted entities you could do something like: + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn my_system( + mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, +) { + for (entity, mut script_data) in &mut scripted_entities { + // do something with scripted entities + } +} + +fn main() {} +```