10个版本 (5个破坏性更新)
0.6.0 | 2024年4月4日 |
---|---|
0.5.0 | 2024年3月6日 |
0.4.0 | 2024年3月6日 |
0.3.0 | 2023年5月8日 |
0.1.1 | 2022年8月3日 |
#291 in 游戏开发
每月 70次下载
355KB
9K SLoC
Bevy 脚本
尽管 Bevy 不会直接支持脚本,但是正在努力将其整合。这个工具包代表了在 Bevy 现有框架中启用脚本的初步尝试。需要注意的是,这还是一个正在进行中的工作,还没有优化或完成。随着 Bevy 的发展,这个 API 预计将会有重大变化。
要详细了解这个工具包的工作方式,请参阅 architecture.md
为什么使用脚本?
- 无需重新编译整个工具包即可刷新游戏机制
- 将游戏逻辑封装在脚本中,为模组制作者轻松创建自定义内容铺平了道路
- 使用更简单的语言在脚本中实现游戏逻辑/UI,使您的团队的非程序员也能参与开发
功能
- 热重载脚本
- Lua、Teal、Rhai 和 Rune 集成
- 为 Lua 自动生成 Bevy 绑定
- CLI rustc 扩展,用于生成自己的 Lua 绑定
- 基于事件的钩子(例如
on_update
) - 灵活的事件调度(例如根据事件处理事件的处理阶段)
- 每个实体可以有多个脚本
- 一个实体可以有相同脚本的多个实例
- 广泛的回调参数类型支持
- 用于生成脚本原生文档的实用程序
- 通过
require
加载外部 lua 库(由于潜在的不安全性,启用unsafe_lua_modules
cargo 功能)
支持
对语言的支持分为三个级别
ScriptHost
实现,在此语言中可以加载、调度和运行脚本,支持自定义APIProvider
- 实现了 Bevy API 提供者,它使可以访问
entity
、world
等,并提供对至少基本操作的支持,如get_component
、add_component
、spawn
等 - 存在用于生成代理包装结构的宏,可用于自定义类型,并能够添加脚本侧功能
- 为原生 Bevy 结构自动生成宏实例化
目前支持的语言如下
语言 | 支持级别 | 文档生成 |
---|---|---|
Lua | 4 | 是 |
Rhai | 2 | 否 |
Rune | 1 | 否 |
用法
安装
要安装
- 将此crate添加到您的Cargo.toml文件中的依赖项
- 该crate仍在开发中,因此我建议将版本锁定到git提交
- 将ScriptingPlugin添加到您的应用中
- 添加您计划使用的ScriptHosts(
add_script_host
,add_script_host_to_set
)- 确保将其附加到在可能生成、修改/创建/删除脚本组件的系统之后运行的系统集
- 添加脚本处理程序以捕获您期望的优先级范围内的事件(
add_script_handler_to_set
,add_script_handler
) - 添加与您的脚本宿主对应的生成ScriptEvents的系统
- 添加向实体添加ScriptCollection组件并将它们填充脚本的系统
以下是一个示例
fn main() -> std::io::Result<()> {
let mut app = App::new();
app.add_plugins(ScriptingPlugin)
.add_plugins(DefaultPlugins)
// pick and register only the hosts you want to use
// use any system set AFTER any systems which add/remove/modify script components
// in order for your script updates to propagate in a single frame
.add_script_host::<RhaiScriptHost<MyRhaiArgStruct>>(PostUpdate)
.add_script_host::<LuaScriptHost<MyLuaArgStruct>>(PostUpdate)
// the handlers should be ran after any systems which produce script events.
// The PostUpdate set is okay only if your API doesn't require the core Bevy systems' commands
// to run beforehand.
// Note, this setup assumes a single script handler system set with all events having identical
// priority of zero (see examples for more complex scenarios)
.add_script_handler::<LuaScriptHost<MyLuaArg>, 0, 0>(
CoreSet::PostUpdate,
)
.add_script_handler::<RhaiScriptHost<RhaiEventArgs>, 0, 0>(
CoreSet::PostUpdate,
)
// generate events for scripts to pickup
.add_system(trigger_on_update_lua)
.add_system(trigger_on_update_rhai)
// attach script components to entities
.add_startup_system(load_a_script);
app.run();
Ok(())
}
触发脚本回调
脚本通过分发ScriptEvents
被激活。此crate使用自定义优先级事件写入器和读取器,这意味着事件带有相关的优先级。这个优先级与您的事件管道结合,影响事件处理的顺序。优先级为0被认为是最高。
此机制可用于构建类似于Unity或其他游戏引擎中找到的游戏循环。
以下是一个示例事件分发系统
use bevy::prelude::*;
use bevy_mod_scripting::prelude::*;
// event callback generator for lua
#[cfg(feature = "lua")]
pub fn trigger_on_update_lua(mut w: PriorityEventWriter<LuaEvent<()>>) {
let event = LuaEvent::<()> {
hook_name: "on_update".to_string(),
args: (),
recipients: Recipients::All
};
w.send(event,0);
}
添加脚本
脚本由以下组成
- 对其代码文件的引用,表示为资产句柄
- 名称,通常是相对于资产文件夹的路径
脚本通过bevy_mod_scripting::ScriptCollection
组件与实体关联,如下所示
use std::sync::Mutex;
use bevy::prelude::*;
use bevy_mod_scripting::prelude::*;
// An example of a startup system which loads the lua script "console_integration.lua"
// placed in "assets/scripts/" and attaches it to a new entity
#[cfg(feature = "lua")]
pub fn load_a_script(
server: Res<AssetServer>,
mut commands: Commands,
) {
// this handle is kept by the script so it will not be unloaded
let path = "scripts/console_integration.lua".to_string();
let handle = server.load::<LuaFile>(&path);
commands.spawn(()).insert(ScriptCollection::<LuaFile> {
scripts: vec![Script::<LuaFile>::new(
path, handle,
)],
});
}
定义API
要使API可供脚本使用,您需要实现APIProvider
特质。这可以通过App
的add_api_provider
方法进行注册。与插件类似,APIProviders
功能相同
use ::std::sync::Mutex;
use bevy_mod_scripting::prelude::*;
#[cfg(feature = "lua")]
#[derive(Default)]
pub struct LuaAPI;
#[cfg(feature = "lua")]
impl APIProvider for LuaAPI {
type APITarget = Mutex<Lua>;
type DocTarget = LuaDocFragment;
type ScriptContext = Mutex<Lua>;
fn attach_api(&mut self, ctx: &mut Self::APITarget) -> Result<(),ScriptError> {
// ... access the lua context here when the script loads
Ok(())
}
}
按如下方式注册您的API提供者
app.add_plugins(DefaultPlugins)
.add_plugins(ScriptingPlugin)
.add_script_host::<LuaScriptHost<MyLuaArg>>(PostUpdate)
.add_api_provider::<LuaScriptHost<MyLuaArg>>(Box::new(LuaAPI))
//...
APIProvider
接口还包括setup_script
和get_doc_fragment
方法。默认情况下,这些方法不执行任何操作。但是,它们可以用于特定目的。例如,可以使用get_doc_fragment
来生成文档(参见图例),并且setup_script
可以确保每个脚本的一次性设置,例如设置Lua包路径。
文档生成
通过App
的update_documentation
构建器特质方法在运行时公开文档功能
use bevy::prelude::*;
use bevy_mod_scripting::prelude::*;
#[cfg(feature = "lua")]
fn main() -> std::io::Result<()> {
let mut app = App::new();
app.add_plugins(DefaultPlugins)
.add_plugins(ScriptingPlugin)
.add_script_host::<LuaScriptHost<()>>(PostUpdate)
// Note: This is a noop in optimized builds unless the `doc_always` feature is enabled!
// this will pickup any API providers added *BEFOREHAND* like this one
.add_api_provider::<LuaScriptHost<()>>(Box::new(LuaBevyAPIProvider))
.add_api_provider::<LuaScriptHost<()>>(Box::new(LuaCoreBevyAPIProvider))
.update_documentation::<LuaScriptHost<()>>()
.add_script_handler::<LuaScriptHost<()>, 0, 0>(PostUpdate);
Ok(())
}
Lua
tealr
是mlua
crate的包装,提供了Lua文档生成的机制。它可以生成Lua的静态类型d.tl
文件,通过teal
项目,但使用teal
不是生成文档所必需的。
请参阅此示例进行演示。
此crate的Bevy API文档在每个版本发布时自动生成,可以在这里和这里找到。您可能需要调整自动生成的assets/doc/tealr_doc_gen_config.json
文件中的page_root
,将其设置为路径,例如assets/doc/YourAPI
。
Teal - Lua静态类型
青绿色是向您的bevy游戏引入lua的推荐方式。然而,由于它对您的资源结构(script
和scripts/build
,位于assets
下的文件夹)有很强的观点,并且还需要安装lua
+ teal
+ tealr_doc_gen
(使用以下命令安装:cargo install --git https://github.com/lenscas/tealr_doc_gen --rev 91afd4a528e7f5b746ac3a6b299c422b42c05db6
)来解锁此功能,因此该功能被锁定在teal
cargo功能之后。(请参阅https://github.com/teal-language/tl和tealr
)。
一旦启用,.tl
文件可以作为lua脚本加载,除了.lua
文件外,还可以在运行时编译。支持完整的热重载。当您准备好发布游戏时,只需从assets/scripts
目录运行tl build
来编译您的teal文件。这将在assets/scripts/build
下生成.lua
文件。您可以使用bevy_mod_scripting::lua_path
宏来管理加载脚本。
如果启用了teal
并且您已将update_documentation
步骤添加到您的应用程序中,每次在开发过程中运行/构建您的应用程序时,都会生成/同步以下内容:- 一个包含您公开的lua API文档的scripts/doc
目录;- 一个包含您的lua IDE的.d.tl
文件的scripts/types
目录;- 如果尚不存在,将生成一个scripts/tlconfig.lua
文件;- 带有.tl
扩展的任何脚本都将编译为lua代码并进行类型检查。在优化后的发布构建中,不会发生这些操作(没有debug_asserts)。
推荐的工作流程是使用vscode和官方的teal扩展,并在工作空间的根目录中添加一个额外的tlconfig.lua
文件,内容如下:
return {
include_dir = {
"path_to_your_lib/",
}
}
配置
SCRIPT_DOC_DIR
- 文档在assets/scripts/docs
生成,或如果设置了此环境变量,则生成到该路径。
示例
要查看此库的更复杂的应用,请查看示例
依赖关系
~39–79MB
~1.5M SLoC