迁移指南: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_stateState::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

系统排序 #

并行系统执行器已重新设计。具有隐式排序的系统可能不再按相同的顺序运行。有关新行为的更多详细信息,请参见 发行说明