Managing Scripts
Scripts are Bevy assets (ScriptAsset), loaded from the assets directory like any other asset. A loaded script does nothing until it is attached, which evaluates it:
- parsing the script body,
- creating or updating the script’s context, the environment holding its globals.
Script attachments
A ScriptAttachment says where a script “lives”, and is the identity scripts are addressed by:
ScriptAttachment::EntityScript(entity, handle): attached to an entity through itsScriptComponent,ScriptAttachment::StaticScript(handle): attached to nothing, activated through theAttachScriptcommand.
Inserting or removing a ScriptComponent sends ScriptLifecycleMessages (Attached / Detached), which the script pipeline processes. Static scripts are attached and detached with the AttachScript and DetachScript commands.
Loading
Load scripts with the AssetServer, or create them in memory:
let handle = asset_server.load::<ScriptAsset>("my_script.lua");
let mut script = ScriptAsset::from("x = 0".to_string());
script.language = Language::Lua;
let handle = script_assets.add(script);
Neither runs any code yet.
Note
Scripts are identified by their handle, not their path. The path is only used in logs and error messages, so scripts created in memory show up by asset id instead.
Attaching scripts to entities
Add a ScriptComponent to an entity:
fn load_script(asset_server: Res<AssetServer>, mut commands: Commands) {
let handle = asset_server.load::<ScriptAsset>("my_script.lua");
commands.spawn(ScriptComponent::new(vec![handle]));
}
In the script, import_namespace("bms").current_entity() returns the entity the script is attached to.
Static scripts
Scripts which don’t belong to an entity are attached as static scripts:
fn add_static_script(asset_server: Res<AssetServer>, mut commands: Commands) {
let handle = asset_server.load("scripts/my_script.lua");
commands.queue(AttachScript::<LuaScriptingPlugin>::new(
ScriptAttachment::StaticScript(handle),
));
}
fn remove_static_script(handle: Local<Handle<ScriptAsset>>, mut commands: Commands) {
commands.queue(DetachScript::<LuaScriptingPlugin>::new(
ScriptAttachment::StaticScript(handle.clone()),
));
}
The commands run the script pipeline until the attachment is processed. To avoid frame time spikes, write ScriptLifecycleMessages instead, and let the pipeline pick them up as normal.
Unloading
BMS keeps a handle to each attached script, so dropping your own handles does not unload it. To unload a script, and run its on_script_unloaded callback, remove it from its ScriptComponent or detach it with DetachScript. Once no handles are left, Bevy drops the asset.
A context is removed together with the last script in it.
Hot reloading
Enable Bevy’s file_watcher feature, and changes to script assets are picked up and the scripts reloaded. Use on_script_unloaded and on_script_reloaded to keep state across reloads.
File extensions
Each language plugin maps file extensions to its language, i.e. lua and luau for Lua. To add more:
app.add_plugins(LuaScriptingPlugin::default().add_supported_extension("pua"));
Next, run your scripts.