bevy_scriptum/book/src/workflow/live_reload.md
Jaroslaw Konik d505fb33aa
Some checks failed
Rust / build (pull_request) Has been cancelled
book testing
2025-05-26 22:45:50 +02:00

148 lines
3.7 KiB
Markdown

# Live-reload
## Bevy included support
To enable live reload it should be enough to enable `file-watcher` feature
within bevy dependency in `Cargo.toml`
```toml
bevy = { version = "0.16", features = ["file_watcher"] }
```
## Init-teardown pattern
It is useful to structure your application in a way that would allow making changes to
the scripting code without restarting the application.
A useful pattern is to hava three functions "init", "update" and "teardown".
- "init" function will take care of starting the application(spawning the player, the level etc)
- "update" function will run the main application logic
- "teardown" function will despawn all the entities so application starts at fresh state.
This pattern is very easy to implement in bevy_scriptum. All you need is to define all needed functions
in script:
```lua
player = {
entity = nil
}
-- spawning all needed entities
local function init()
player.entity = spawn_player()
end
-- application logic here, should be called in a bevy system using call_fn
local function update()
(...)
end
-- despawning entities and possible other cleanup logic needed
local function teardown()
despawn(player.entity)
end
-- call init to start the application, this will be called on each file-watcher script
-- reload
init()
```
The function calls can be implemented on Rust side the following way:
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
use bevy_scriptum::runtimes::lua::BevyVec3;
fn init(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<LuaScript>::new(
assets_server.load("scripts/game.lua"),
));
}
fn update(
mut scripted_entities: Query<(Entity, &mut LuaScriptData)>,
scripting_runtime: ResMut<LuaRuntime>,
) {
for (entity, mut script_data) in &mut scripted_entities {
scripting_runtime
.call_fn("update", &mut script_data, entity, ())
.unwrap();
}
}
fn teardown(
mut ev_asset: EventReader<AssetEvent<LuaScript>>,
scripting_runtime: ResMut<LuaRuntime>,
mut scripted_entities: Query<(Entity, &mut LuaScriptData)>,
) {
for event in ev_asset.read() {
if let AssetEvent::Modified { .. } = event {
for (entity, mut script_data) in &mut scripted_entities {
scripting_runtime
.call_fn("teardown", &mut script_data, entity, ())
.unwrap();
}
}
}
}
fn main() {}
```
And to tie this all together we do the following:
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<LuaRuntime>(|builder| {
builder
.add_function(String::from("spawn_player"), spawn_player)
.add_function(String::from("despawn"), despawn);
})
.add_systems(Startup, init)
.add_systems(Update, (update, teardown))
.run();
}
fn init() {} // Implemented elsewhere
fn update() {} // Implemented elsewhere
fn despawn() {} // Implemented elsewhere
fn teardown() {} // Implemented elsewhere
fn spawn_player() {} // Implemented elsewhere
```
`despawn` can be implemented as:
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
fn despawn(In((entity,)): In<(BevyEntity,)>, mut commands: Commands) {
commands.entity(entity.0).despawn();
}
fn main() {} // Implemented elsewhere
```
Implementation of `spawn_player` has been left out as an exercise for the reader.