迁移指南:0.4 到 0.5
"commands: &mut Commands" SystemParam 现在是 "mut commands: Commands" #
// 0.4
fn foo(commands: &mut Commands) {
}
// 0.5
fn foo(mut commands: Commands) {
}
在 0.5 中使用旧的 commands: &mut Commands
语法的系统在调用 foo.system()
时将无法编译。
此更改是因为 Commands
现在持有内部 World
引用以启用安全实体分配。
注意:内部 World
引用需要两个生命周期参数才能将 Commands 传递到非系统函数中:commands: &'a mut Commands<'b>
命令 API #
Commands
API 已完全重构,以与 World
API 保持一致。
// 0.4
commands
.spawn(SomeBundle)
.with(SomeComponent)
.spawn(SomeBundle); // this sort of chaining is no longer possible
let entity = commands.spawn(SomeBundle).current_entity().unwrap();
commands.despawn(entity);
// 0.5
commands
.spawn()
.insert_bundle(SomeBundle)
.insert(Component);
let entity = commands.spawn().insert_bundle(SomeBundle).id();
commands.entity(entity).despawn();
commands.spawn()
不再接受任何参数。要生成捆绑包,请使用 commands.spawn_bundle(bundle)
。
类似地,而不是使用 with(some_component)
生成具有多个组件的对象,你现在必须使用 insert(some_component)
// 0.4
commands.spawn(some_bundle)
.with(some_component);
// 0.5
commands.spawn_bundle(some_bundle)
.insert(some_component);
// or...
commands.spawn()
.insert_bundle(some_bundle)
.insert(some_component);
删除和添加实体上的组件也已更改
// 0.4
commands.insert_one(some_entity, SomeComponent);
commands.remove_one::<SomeComponent>(some_entity);
// 0.5
commands.entity(some_entity).insert(SomeComponent);
commands.entity(some_entity).remove::<SomeComponent>();
计时器现在使用 Duration #
// 0.4
if timer.tick(time.delta_seconds()).finished() { /* do stuff */ }
timer.elapsed() // returns an `f32`
// 0.5
if timer.tick(time.delta()).finished() { /* do stuff */ }
timer.elapsed() // returns a `Duration`
Timer
的大多数方法现在使用 Duration
而不是 f32
。
此更改使计时器能够具有始终如一的高精度。为了方便起见,还有一个 elapsed_secs
方法,该方法返回 f32
。否则,当你需要 f32
时,请在 Duration
上使用 as_secs_f32()
方法进行转换。
简化的事件 #
// 0.4
fn event_reader_system(
mut my_event_reader: Local<EventReader<MyEvent>>,
my_events: Res<Events<MyEvent>>,
) {
for my_event in my_event_reader.iter(&my_events) {
// do things with your event
}
}
// 0.5
fn event_reader_system(mut my_event_reader: EventReader<MyEvent>) {
for my_event in my_event_reader.iter() {
// do things with your event
}
}
你不再需要两个系统参数来读取你的事件。一个 EventReader
就足够了。
按照上面使用 EventReader
读取事件的示例,你现在可以使用 EventWriter
创建新的事件。
// 0.4
fn event_writer_system(
mut my_events: ResMut<Events<MyEvent>>,
) {
my_events.send(MyEvent);
}
// 0.5
fn event_writer_system(
mut my_events: EventWriter<MyEvent>
) {
my_events.send(MyEvent);
}
AppBuilder::add_resource 现在称为 AppBuilder::insert_resource #
这是一个小更改,使 AppBuilder
上的函数名称与 Commands
API 保持一致。
TextBundle #
此捆绑包已重新设计,允许在单个捆绑包中包含多个不同样式的文本部分。已添加 Text::with_section
以简化你只对一个文本部分感兴趣的常见情况。
// 0.4
TextBundle {
text: Text {
value: "hello!".to_string(),
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
style: TextStyle {
font_size: 60.0,
color: Color::WHITE,
..Default::default()
},
},
..Default::default()
}
// 0.5
TextBundle {
text: Text::with_section(
"hello!",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 60.0,
color: Color::WHITE,
},
TextAlignment::default()
),
..Default::default()
}
加载 GLTF 场景时现在必须指定场景 #
以前,你可以仅使用路径加载 GLTF 场景资产。现在,你必须包含一个片段,指定要加载的场景。如果你在文件中只有一个场景,则为 #Scene0
。
// 0.4
asset_server.load("models/foo.glb");
// 0.5
asset_server.load("models/foo.glb#Scene0");
状态 #
现在使用 AppBuilder::add_state
注册状态,该方法会创建 State
资源并注册一个“驱动”系统,该系统取代了 StateStage
。状态使用 SystemSet
注册。
重要提示:如果你停止注册 StateStage
但未注册驱动程序(使用 add_state
或 State::get_driver
),Bevy 0.5 将进入无限循环,导致你的应用程序“锁定”。
// 0.4
app.insert_resource(State::new(MyState::InitState))
.add_stage_after(
bevy::app::stage::UPDATE,
MY_STATE_STAGE_NAME,
bevy::ecs::StateStage::<MyState>::default(),
)
.on_state_enter(
MY_STATE_STAGE_NAME,
MyState::InitState,
enter_init_state.system())
.on_state_update(
MY_STATE_STAGE_NAME,
MyState::InitState,
update_init_state.system())
.on_state_exit(
MY_STATE_STAGE_NAME,
MyState::InitState,
exit_init_state.system());
// 0.5
app.add_state(MyState::InitState)
.add_system_set(SystemSet::on_enter(MyState::InitState)
.with_system(enter_init_state.system()))
.add_system_set(SystemSet::on_update(MyState::InitState)
.with_system(update_init_state.system()))
.add_system_set(SystemSet::on_exit(MyState::InitState)
.with_system(exit_init_state.system()));
仍然可以使用 State::get_driver
手动注册驱动程序,但这通常不需要。
已移除 ChangedRes #
此更改是为了允许更灵活,并且与组件的更改检测行为更加一致。
// 0.4
fn some_system(
res: ChangedRes<SomeResource>
) {
// this system only runs if SomeResource has changed
}
// 0.5
fn some_system(
res: Res<SomeResource> // or ResMut
) {
// this system always runs
if !res.is_changed() { // or .is_added()
return;
}
}
相机 #
Camera3dBundle
现在称为 PerspectiveCameraBundle
,而 Camera2dBundle
现在称为 OrthographicCameraBundle
。
OrthographicCameraBundle
不实现 Default
,因此,要在生成时更改其变换,同时保持其他所有内容不变,请考虑以下操作
let mut camera = OrthographicCameraBundle::new_2d();
camera.transform = Transform::from_translation(Vec3::new(0.0, 0.0, 5.0));
commands.spawn_bundle(camera);
渲染 API 更改 #
RasterizationStateDescriptor
不再存在。其大部分功能已移至 PipelineDescriptor
上的其他字段。例如,cull_mode
现在位于 primitive: PrimitiveState
字段中。
由于 RenderResources
的限制以及新的 Byteable
要求,类型为 Vec<Color>
的缓冲区无法直接上传到 GPU。考虑改用 Vec<Vec4>
,并使用 as_rgba_f32()
和 .into()
插入颜色
#[derive(RenderResources, Default, TypeUuid)]
struct SomeShader {
#[render_resources(buffer)]
pub colors: Vec<Vec4>
}
fn add_some_color(shader: SomeShader, color: Color) {
shader.colors.push(color.as_rgba_f32().into());
}
着色器现在应该使用 CameraViewProj
#
ViewProj
矩阵现在通过名称 CameraViewProj
设置,而不是 Camera
。如果你不更新此矩阵,bevy 将会静默失败,你将无法看到任何内容!
// 0.4
layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
}
// 0.5
layout(set = 0, binding = 0) uniform CameraViewProj {
mat4 ViewProj;
}
诊断 #
PrintDiagnosticsPlugin
现在是 LogDiagnosticsPlugin
。
系统排序 #
并行系统执行器已重新设计。具有隐式排序的系统可能不再按相同的顺序运行。有关新行为的更多详细信息,请参见 发行说明。