- 0.11 到 0.12
- 动画播放器 API 更新
- 修复一次性运行器
- 添加对 KHR_materials_emissive_strength 的支持
- Bevy 资产 V2
- 写时复制 AssetPaths
- 删除 anyhow
- 使用包装器资产实现非阻塞 load_untyped
- reflect: TypePath 第 2 部分
- 更符合人体工程学的空间音频
- 简化并行迭代方法
- 修复 WorldQuery::fetch 的安全不变式并简化克隆
- 选择退出多线程特性标志
- 重构 build_schedule 和相关错误
- 添加 system.map(...) 用于转换系统的输出
- 用 HashMap 替换 EntityMap
- 重命名 ManualEventIterator
- 替换 FnOnce 的 EntityCommand 实现
- 将调度名称移入调度
- 重构 EventReader::iter 以便读取
- 用 IntoSystemSetConfigs 替换 IntoSystemSetConfig
- 将 get_component(_unchecked_mut) 从 Query 移到 QueryState
- 修复“tick” 列和 ComponentSparseSet 方法的命名
- 从 set_if_neq 返回布尔值
- 重命名 RemovedComponents::iter/iter_with_id 为 read/read_with_id
- 删除 States::variants 并删除其派生的枚举限制
- 用内部化标签替换所有标签
- 将 configure_schedules 添加到 App 和 Schedules 中,将 ScheduleBuildSettings 应用于所有调度
- 仅在事件系统有实际工作要完成时才运行
- 允许通过 EntityMut 进行不相交的易变世界访问
- 使生成器类型接受并返回 Self
- 更改 AxisSettings 活区默认值
- 重命名 bevy_math::rects 转换方法
- 为清晰起见,将 Bezier 重命名为 CubicBezier
- 为所有三次曲线生成器添加 Cubic 前缀
- 删除 bevy_dylib 特性
- 重构 bevy_reflect 的 path 模块
- 使 ParsedPath 可传递给 GetPath
- 删除 TypeRegistry 重新导出重命名
- 为 ReflectFromPtr 的字段提供 getter
- bevy_reflect: 修复被忽略/跳过的字段顺序
- 在 Camera::physical_viewport_rect 中返回 URect 而不是 (UVec2, UVec2)
- 更新 bevy_window::PresentMode 以镜像 wgpu::PresentMode
- 将 GpuArrayBuffer 用于 MeshUniform
- 减小 MeshUniform 的大小以提高性能
- 重新排序渲染集,重构 bevy_sprite 以利用它
- 将 ComputedVisibility 拆分为两个组件,以便进行准确的更改检测并加速可见性传播
- 清理可见性模块
- 允许其他插件创建渲染器资源
- 将 EntityHashMap<Entity, T> 用于渲染世界实体存储,以获得更好的性能
- PCF 用于方向光/聚光灯阴影
- 将 Material 用于线框
- 延迟渲染器
- pbr 着色器清理
- *_PREPASS 着色器定义清理
- 允许对 StandardMaterial 进行扩展
- 可变 MeshPipeline 视图绑定组布局
- 更新着色器导入
- 绑定组条目
- 检测 dds 纹理的立方体贴图
- 添加 Image 的便捷方法
- 使用“镜面遮挡”术语来一致地熄灭环境光和环境贴图光的菲涅耳
- 修复雾色不准确
- Image Sampler 改进
- StandardMaterial 光透射
- 增加默认法线偏差以避免常见的伪影
- 使方向光级联计算泛化为 CameraProjection
- 将皮肤代码移到单独的模块中
- 将场景生成器系统移到 SpawnScene 调度中
- 删除 Resource 并将 Debug 添加到 TaskPoolOptions
- 全局 TaskPool API 改进
- 统一 FixedTime 和 Time,同时修复几个问题
- 将 ContentSize 的 measure_func 字段的默认值更改为 None。
- 将 UiScale 更改为元组结构
- 清理一些 bevy_text pipeline.rs
- 使 GridPlacement 的字段非零并添加访问器函数。
- 删除 Val 的 try_* 算术方法
- 重命名 Val evaluate 为 resolve 并实现视口变体支持
- TextLayoutInfo::size 应保存文本的绘制大小,而不是缩放值。
- 每个根节点都有一个单独的隐式视口节点 + 使视口节点 Display::Grid
- 将 num_font_atlases 重命名为 len。
- 各种可访问性 API 更新。
- 为 bevy_text 添加更多文档。
- 更新 UI 对齐文档
- 添加选项以切换窗口控制按钮
- 改进 bevy_winit 文档
- 解决 naga/wgpu WGSL instance_index -> GLSL gl_InstanceID 在 WebGL2 上的错误
- 删除 &mut EventReader 的 IntoIterator 实现
- 更新默认 `ClearColor`` 以更好地匹配 Bevy 的品牌
- 视图变换
迁移指南: 0.11 到 0.12
Bevy 严重依赖 Rust 语言和编译器的改进。因此,最低支持的 Rust 版本 (MSRV) 是 Rust 的“最新稳定版本”。
动画播放器 API 更新 #
AnimationPlayer
上的一些方法已更改。
elapsed
已被删除。使用seek_time
。set_elapsed
已被删除。使用seek_to
。stop_repeating
已被删除。使用set_repeat(RepeatAnimation::Never)
。
如果您手动重置动画状态,可以使用新的 replay
方法。
修复一次性运行器 #
app.ready()
已被 app.plugins_state()
替换,后者将返回有关应用程序中插件当前状态的更多详细信息
添加对 KHR_materials_emissive_strength 的支持 #
GLTF 资产加载器现在将在转换为 Bevy 的 StandardMaterial::emissive
时考虑 emissiveStrength
。Blender 将使用此字段导出发射材料。从您的 GLTF 文件中删除该字段,或在资产加载后手动修改您的材料,以匹配 Bevy 在先前版本中加载这些文件的方式。
Bevy 资产 V2 #
迁移自定义资产加载器 #
现有的资产加载器需要一些小改动才能与 Bevy 资产 V2 协同工作。
首先,您需要将资产类型添加为加载器的关联类型。此类型称为 Asset
,表示加载器生成的“默认资产”的类型。
您还需要添加一个 Settings
类型,它表示在您请求资产时可以传递给加载器的选项。如果您的资产没有设置,那么您可以将其设置为单位类型。
pub struct MyAssetLoader;
impl AssetLoader for MyAssetLoader {
type Asset = MyAsset;
type Settings = ();
您还需要对 load
函数进行一些小改动。load 函数现在接受一个 settings
参数,其类型是 Settings
。
fn load<'a>(
&'a self,
reader: &'a mut Reader,
settings: &'a Self::Settings,
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<Self::Asset, anyhow::Error>> {
同样,如果您没有使用设置,那么您可以忽略该参数(以“_”作为前缀)。
此外,第二个参数现在是一个 reader 而不是字节向量。如果您的现有代码期望字节,您可以简单地读取整个流
fn load<'a>(
&'a self,
reader: &'a mut Reader,
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<Self::Asset, anyhow::Error>> {
Box::pin(async move {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
最后,您需要编写返回默认资产的代码。这以前是通过调用 load_context.set_default_asset()
完成的,但在 V2 中,您只需从 load
函数中返回资产即可
fn load<'a>(
&'a self,
reader: &'a mut Reader,
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<Self::Asset, anyhow::Error>> {
Box::pin(async move {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let mut asset: MyAsset =
serde_json::from_slice(&bytes).expect("unable to decode asset");
Ok(asset)
}
要使用新的加载器,请确保注册加载器和资产类型
app.register_asset_loader(MyAssetLoader)
.init_asset::<MyAsset>()
资产热重载 #
特性 filesystem_watcher
已重命名为 file_watcher
。此外,您不再需要在 AssetPlugin
中手动配置 ChangeWatcher
,因为现在在启用特性时会自动配置。
带标签的资产 #
如果您的加载器允许带标签的资产,则有几种不同的方法可以处理它们。最简单的方法是调用 load_context.labeled_asset_scope
// Assume `asset.children` is a HashMap or something.
// Using `drain` here so that we take ownership and don't end up with
// multiple references to the same asset.
asset.children.drain().for_each(|(label, mut item)| {
load_context.labeled_asset_scope(label, |lc| {
// Do any additional processing on the item
// Use 'lc' to load dependencies
item
});
});
您可以使用提供的加载上下文 (lc
) 来加载其他资产。这些资产将自动注册为带标签资产的依赖项。
使用资产 #
对 load
的实际调用没有改变
let handle = server.load("path/to/my/asset.json");
// ...
let data = assets.get(&handle).unwrap();
资产事件 #
资产事件有一些变化。事件不再包含 handle
字段,而是包含一个名为 id
的字段
for ev in ev_template.read() {
match ev {
AssetEvent::Added { id } => {
println!("Asset added");
}
AssetEvent::LoadedWithDependencies { id } => {
println!("Asset loaded");
}
AssetEvent::Modified { id } => {
println!("Asset modified");
}
AssetEvent::Removed { id } => {
println!("Asset removed");
}
}
}
id
可用于访问资产数据、资产的路径或加载状态。资产句柄也包含一个 id
字段,可用于比较是否相等
AssetEvent::Modified { id } => {
for cmp in query.iter() {
if cmp.handle.id() == id {
println!("Found it!");
}
}
}
此外,您可能已经注意到事件集已更改。其中最重要的一个是 LoadedWithDependencies
,它告诉您资产及其所有依赖项已完成加载到内存中。
UntypedHandle
#
HandleUntyped
已重命名为 UntypedHandle
。HandleId
已被 UntypedAssetId
及其类型化等效项 AssetId<T>
替换。
构建非类型化句柄的新方法如下所示
// 0.11
const MESH_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Mesh::TYPE_UUID, 0x1f40128bac02a9b);
// 0.12
const MESH_HANDLE: UntypedHandle =
UntypedHandle::Weak(UntypedAssetId::Uuid { type_id: TypeId::of::<Mesh>(), uuid: Uuid::from_u128(0x1f40128bac02a9b) });
写时复制 AssetPaths #
// 0.11
AssetPath::new("logo.png", None);
// 0.12
AssetPath::from("logo.png");
// 0.11
AssetPath::new("scene.gltf", Some("Mesh0"));
// 0.12
AssetPath::from("scene.gltf").with_label("Mesh0");
AssetPath
现在序列化为 AssetPath("some_path.extension#Label")
而不是 AssetPath { path: "some_path.extension", label: Some("Label) }
删除 anyhow
#
bevy_asset
不再导出anyhow
;将其添加到您自己的项目中(如果需要)。AssetLoader
和AssetSaver
具有关联类型Error
;定义适当的错误类型(例如,使用thiserror
),或使用预制错误类型(例如,anyhow::Error
)。请注意,使用anyhow::Error
是一个直接替换。AssetLoaderError
已被删除;定义一个新的错误类型,或使用替代方案(例如,anyhow::Error
)- 所有第一方
AssetLoader
和AssetSaver
现在返回相关的(且狭窄的)错误类型,而不是单个模糊的类型;匹配特定错误类型,或封装(Box<dyn>
、thiserror
、anyhow
等)
使用包装器资产实现非阻塞 load_untyped #
尽可能使用类型化 API 来直接获取对您资产的句柄。如果您不知道类型或需要出于其他原因使用 load_untyped
,则 Bevy 0.12 引入了额外的间接层。资产服务器将返回对 LoadedUntypedAsset
的句柄,该句柄将在后台加载。一旦加载完成,就可以从 LoadedUntypedAsset
的字段 handle
中检索到对资产文件的非类型化句柄。
reflect: TypePath
第 2 部分 #
- 在所有需要稳定性保证和反射环境中使用
TypePath
来代替std::any::type_name
,可以通过以下 API 来使用:- 如果你拥有具体类型,而非值,请使用
TypePath::type_path
。 - 如果你拥有
dyn Reflect
值,但没有具体类型,请使用DynamicTypePath::reflect_type_path
。 - 如果你要通过注册表使用,或者你想使用
DynamicFoo
的表示类型,请使用TypeInfo::type_path
。
- 如果你拥有具体类型,而非值,请使用
- 从手动
Reflect
实现中删除type_name
。 - 在
TypeInfo
类结构体中使用type_path
和type_path_table
来代替type_name
。 - 使用
get_with_type_path(_mut)
来代替get_with_type_name(_mut)
。
更符合人体工程学的空间音频 #
空间音频现在会自动使用 AudioBundle
的变换以及具有 SpatialListener
组件的实体的变换。
如果你之前手动缩放发射器/监听器位置,你可以使用 AudioPlugin
的 spatial_scale
字段来代替。
// 0.11
commands.spawn(
SpatialAudioBundle {
source: asset_server.load("sounds/Windless Slopes.ogg"),
settings: PlaybackSettings::LOOP,
spatial: SpatialSettings::new(listener_position, gap, emitter_position),
},
);
fn update(
emitter_query: Query<(&Transform, &SpatialAudioSink)>,
listener_query: Query<&Transform, With<Listener>>,
) {
let listener = listener_query.single();
for (transform, sink) in &emitter_query {
sink.set_emitter_position(transform.translation);
sink.set_listener_position(*listener, gap);
}
}
// 0.12
commands.spawn((
SpatialBundle::from_transform(Transform::from_translation(emitter_position)),
AudioBundle {
source: asset_server.load("sounds/Windless Slopes.ogg"),
settings: PlaybackSettings::LOOP.with_spatial(true),
},
));
commands.spawn((
SpatialBundle::from_transform(Transform::from_translation(listener_position)),
SpatialListener::new(gap),
));
简化并行迭代方法 #
方法 QueryParIter::for_each_mut
已经被弃用,不再起作用。请使用 for_each
来代替,它现在支持可变查询。
// 0.11
query.par_iter_mut().for_each_mut(|x| ...);
// 0.12
query.par_iter_mut().for_each(|x| ...);
方法 QueryParIter::for_each
现在会获取 QueryParIter
的所有权,而不是获取共享引用。
// 0.11
let par_iter = my_query.par_iter().batching_strategy(my_batching_strategy);
par_iter.for_each(|x| {
// ...Do stuff with x...
par_iter.for_each(|y| {
// ...Do nested stuff with y...
});
});
// 0.12
my_query.par_iter().batching_strategy(my_batching_strategy).for_each(|x| {
// ...Do stuff with x...
my_query.par_iter().batching_strategy(my_batching_strategy).for_each(|y| {
// ...Do nested stuff with y...
});
});
修复 WorldQuery::fetch
的安全不变式,并简化克隆 #
fetch
不变式
函数 WorldQuery::fetch
添加了以下安全不变式:
如果
update_component_access
包含任何可变访问,那么调用者必须确保在每个entity
/table_row
的每个原型中,fetch
的调用次数不超过一次。如果Self
实现ReadOnlyWorldQuery
,那么可以安全地多次调用它。
此不变式对于健全性始终是必要的,但之前没有文档记录。如果你在任何地方手动调用此函数,你应该检查以确保不违反此不变式。
删除 clone_fetch
函数 WorldQuery::clone_fetch
已被删除。关联类型 WorldQuery::Fetch
现在具有 Clone
绑定。
// 0.11
struct MyFetch<'w> { ... }
unsafe impl WorldQuery for MyQuery {
...
type Fetch<'w> = MyFetch<'w>
unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {
MyFetch {
field1: fetch.field1,
field2: fetch.field2.clone(),
...
}
}
}
// 0.12
#[derive(Clone)]
struct MyFetch<'w> { ... }
unsafe impl WorldQuery for MyQuery {
...
type Fetch<'w> = MyFetch<'w>;
}
选择退出 multi-threaded
特性标记 #
bevy_ecs
和 bevy_tasks
中的 multi-threaded
特性不再默认启用。但是,它仍然是 bevy
伞形板条箱的默认特性。
如果你使用 bevy
但没有使用 default-features
,或者如果你直接依赖 bevy_ecs
或 bevy_tasks
,你很可能希望启用它,以允许系统并行运行。
重构 build_schedule
和相关错误 #
ScheduleBuildError
现在在更多变体中包含字符串。你可能需要调整处理这些变体的代码。
添加 system.map(...)
用于转换系统的输出 #
system_adapter
函数已被弃用:请使用 .map
来代替,它是一个轻量级的 .pipe
替代方案。
// 0.11
my_system.pipe(system_adapter::ignore)
my_system.pipe(system_adapter::unwrap)
my_system.pipe(system_adapter::new(T::from))
// 0.12
my_system.map(std::mem::drop)
my_system.map(Result::unwrap)
my_system.map(T::from)
// 0.11
my_system.pipe(system_adapter::info)
my_system.pipe(system_adapter::dbg)
my_system.pipe(system_adapter::warn)
my_system.pipe(system_adapter::error)
// 0.12
my_system.map(bevy_utils::info)
my_system.map(bevy_utils::dbg)
my_system.map(bevy_utils::warn)
my_system.map(bevy_utils::error)
用 HashMap
替换 EntityMap
#
- 对
EntityMap::world_scope
的调用可以直接用以下内容替换:map.world_scope(&mut world)
->world.world_scope(&mut map)
- 对旧版
EntityMap
方法(例如EntityMap::get
)的调用必须显式包含 de/引用符号:let entity = map.get(parent);
->let &entity = map.get(&parent);
重命名 ManualEventIterator
#
类型 ManualEventIterator
已重命名为 EventIterator
。此外,ManualEventIteratorWithId
已重命名为 EventIteratorWithId
。
替换了 FnOnce
的 EntityCommand
实现 #
1. 新类型 FnOnce
创建一个 EntityCommand
类型,它实现了你之前编写的那个方法。
pub struct ClassicEntityCommand<F>(pub F);
impl<F> EntityCommand for ClassicEntityCommand<F>
where
F: FnOnce(Entity, &mut World) + Send + 'static,
{
fn apply(self, id: Entity, world: &mut World) {
(self.0)(id, world);
}
}
commands.add(ClassicEntityCommand(|id: Entity, world: &mut World| {
/* ... */
}));
2. 从 EntityMut
中提取 (Entity, &mut World)
方法 into_world_mut
可用于从 EntityMut
获取对 World
的访问。
let old = |id: Entity, world: &mut World| {
/* ... */
};
let new = |mut entity: EntityWorldMut| {
let id = entity.id();
let world = entity.into_world_mut();
/* ... */
};
将调度名称移入 Schedule
#
Schedule::new
和 App::add_schedule
// 0.11
let schedule = Schedule::new();
app.add_schedule(MyLabel, schedule);
// 0.12
let schedule = Schedule::new(MyLabel);
app.add_schedule(schedule);
如果你没有将调度插入世界,而是直接使用调度,你可以使用默认构造函数,它会重用默认标签。
// 0.11
let schedule = Schedule::new();
schedule.run(world);
// 0.12
let schedule = Schedule::default();
schedule.run(world);
Schedules::insert
// 0.11
let schedule = Schedule::new();
schedules.insert(MyLabel, schedule);
// 0.12
let schedule = Schedule::new(MyLabel);
schedules.insert(schedule);
World::add_schedule
// 0.11
let schedule = Schedule::new();
world.add_schedule(MyLabel, schedule);
// 0.12
let schedule = Schedule::new(MyLabel);
world.add_schedule(schedule);
重构 EventReader::iter
为 read
#
EventReader::iter
和EventReader::iter_with_id
的现有用法将必须分别更改为EventReader::read
和EventReader::read_with_id
。ManualEventReader::iter
和ManualEventReader::iter_with_id
的现有用法将必须分别更改为ManualEventReader::read
和ManualEventReader::read_with_id
。
用 IntoSystemSetConfigs
替换 IntoSystemSetConfig
#
- 使用
App::configure_sets
来代替App::configure_set
- 使用
Schedule::configure_sets
来代替Schedule::configure_set
将 get_component(_unchecked_mut)
从 Query
移到 QueryState
#
use bevy_ecs::system::QueryComponentError;
-> use bevy_ecs::query::QueryComponentError;
修复 tick
列和 ComponentSparseSet
方法的命名 #
以下方法名称已重命名,从 foo_ticks_bar
到 foo_tick_bar
(ticks
现在是单数,tick
)
ComponentSparseSet::get_added_ticks
→get_added_tick
ComponentSparseSet::get_changed_ticks
→get_changed_tick
Column::get_added_ticks
→get_added_tick
Column::get_changed_ticks
→get_changed_tick
Column::get_added_ticks_unchecked
→get_added_tick_unchecked
Column::get_changed_ticks_unchecked
→get_changed_tick_unchecked
从 set_if_neq
返回布尔值 #
特征方法 DetectChangesMut::set_if_neq
现在会返回一个布尔值,指示值是否已更改。如果你之前手动实现此函数,现在必须在值被覆盖时返回 true
,在值没有被覆盖时返回 false
。
将 RemovedComponents::iter/iter_with_id
重命名为 read/read_with_id
#
fn react_on_removal(mut removed: RemovedComponents<MyComponent>) {
// 0.11
for entity in removed.iter() { /* ... */ }
for (entity, id) in removed.iter_with_id() { /* ... */ }
for entity in &mut removed { /* ... */ }
// 0.12
for entity in removed.read() { /* ... */ }
for (entity, id) in removed.read_with_id() { /* ... */ }
for entity in removed.read() { /* ... */ }
}
删除 States::variants
并移除其派生中的仅枚举限制 #
States::variants
不再存在。如果你依赖此函数,请考虑使用提供枚举迭代器的库。
将所有标签替换为内联标签 #
将
BoxedScheduleLabel
和Box<dyn ScheduleLabel>
替换为InternedScheduleLabel
或Interned<dyn ScheduleLabel>
。将
BoxedSystemSet
和Box<dyn SystemSet>
替换为InternedSystemSet
或Interned<dyn SystemSet>
。将
AppLabelId
替换为InternedAppLabel
或Interned<dyn AppLabel>
。手动实现
ScheduleLabel
、AppLabel
或SystemSet
的类型需要实现dyn_hash
,而不是实现DynHash
as_dyn_eq
将标签按值而不是按引用传递给
World::try_schedule_scope
、World::schedule_scope
、World::try_run_schedule
、World::run_schedule
、Schedules::remove
、Schedules::remove_entry
、Schedules::contains
、Schedules::get
和Schedules::get_mut
。
将 configure_schedules
添加到 App
和 Schedules
,以将 ScheduleBuildSettings
应用于所有调度 #
- 没有重大更改。
- 添加了
Schedule::get_build_settings()
获取器,用于获取调度的ScheduleBuildSettings
。 - 可以替换所有调度的手动配置
// 0.11
for (_, schedule) in app.world.resource_mut::<Schedules>().iter_mut() {
schedule.set_build_settings(build_settings);
}
// 0.l2
app.configure_schedules(build_settings);
仅在事件系统有实际工作要做时才运行它们 #
Events<T>::update_system
已从类型中分离出来,可以在 bevy_ecs::event::event_update_system
中找到。
通过 EntityMut
允许不连续的可变世界访问 #
已删除方法 EntityRef::world
,以修复查询的健全性问题。如果你需要在使用 EntityRef
时访问 &World
,请考虑将世界作为单独的参数传递。
EntityMut
不再能够执行“结构性”世界变异,例如添加或删除组件,或销毁实体。此外,EntityMut::world
、EntityMut::world_mut
、EntityMut::into_world_mut
和 EntityMut::world_scope
已被删除。请使用新添加的类型 EntityWorldMut
来代替,它是一个用于处理 &mut World
的辅助类型。
使生成器类型获取和返回 Self
#
使用 bevy_ecs::DynamicSceneBuilder
和 bevy_ecs::SceneBuilder
时,不要将生成器绑定到变量,而是直接使用它。这些类型上的方法现在会使用 Self
,因此如果你没有立即 build
,你将需要重新绑定生成器。
// 0.11
let mut scene_builder = DynamicSceneBuilder::from_world(&world);
let scene = scene_builder.extract_entity(a).extract_entity(b).build();
// 0.12
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entity(a)
.extract_entity(b)
.build();
更改 AxisSettings
的活动区域默认值 #
活动区域边界默认值已从 -0.95..=0.95
更改为 -1.0..=1.0
,以与更常见的用法保持一致。如果你依赖旧的默认值,可以通过修改 GamepadSettings::default_axis_settings
来更改它。
重命名 bevy_math::rects
转换方法 #
将 Rect::as_urect
替换为 Rect::as_irect
,将 Rect::as_rect
替换为 Rect::as_urect
,将 URect::as_urect
替换为 URect::as_irect
。
为清晰起见,将 Bezier
重命名为 CubicBezier
#
将所有 Bezier
引用更改为 CubicBezier
在所有三次曲线生成器中添加 Cubic
前缀 #
- 重命名:
BSpline
->CubicBSpline
- 重命名:
CardinalSpline
->CubicCardinalSpline
- 重命名:
Hermite
->CubicHermite
删除 bevy_dylib
特性 #
如果你之前使用过 Bevy 的 bevy_dylib
特性,请改用 Bevy 的 dynamic_linking
特性。
# 0.11
cargo run --features bevy/bevy_dylib
# 0.12
cargo run --features bevy/dynamic_linking
[dependencies]
# 0.11
bevy = { version = "0.11", features = ["bevy_dylib"] }
# 0.12
bevy = { version = "0.12", features = ["dynamic_linking"] }
重构 bevy_reflect
的 path
模块 #
如果您之前匹配由 GetPath
和 ParsedPath
方法返回的 Err(ReflectPathError)
值,现在只有解析相关的错误和偏移量是公开可访问的。您始终可以使用 fmt::Display
获取清晰的错误消息,但如果您需要以编程方式访问错误类型,请打开一个问题。
使 ParsedPath
可以传递给 GetPath #
GetPath
现在需要 Reflect
。这减少了 Bevy 方面的大量样板代码。如果您在自己的类型上手动实现 GetPath
,请与我们联系!
ParsedPath::element[_mut]
不是 ParsedPath
的内在方法,您现在必须导入 ReflectPath
。这仅与您没有导入 Bevy 前置代码时相关。
-use bevy::reflect::ParsedPath;
+use bevy::reflect::{ParsedPath, ReflectPath};
parsed_path.element(reflect_type).unwrap()
删除 TypeRegistry 重新导出重命名 #
- 包装器
bevy
crate 重新导出的TypeRegistry
现在是TypeRegistryArc
- 包装器
bevy
crate 重新导出的TypeRegistryInternal
现在是TypeRegistry
为 ReflectFromPtr 的字段提供 getter #
ReflectFromPtr::as_reflect_ptr
现在是ReflectFromPtr::as_reflect
ReflectFromPtr::as_reflect_ptr_mut
现在是ReflectFromPtr::as_reflect_mut
bevy_reflect: 修复被忽略/跳过的字段顺序 #
- 标记为
#[reflect(skip_serializing)]
的字段现在必须实现Default
或使用#[reflect(default = "path::to::some_func")]
指定自定义默认函数
#[derive(Reflect)]
struct MyStruct {
#[reflect(skip_serializing)]
#[reflect(default = "get_foo_default")]
foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function
#[reflect(skip_serializing)]
bar: Bar, // <- `Bar` impls `Default`
}
#[derive(Reflect)]
struct Foo(i32);
#[derive(Reflect, Default)]
struct Bar(i32);
fn get_foo_default() -> Foo {
Foo(123)
}
SerializationData::new
已更改为预期(usize, SkippedField)
的迭代器而不是仅usize
的迭代器
// 0.11
SerializationData::new([0, 3].into_iter());
// 0.12
SerializationData::new([
(0, SkippedField::new(field_0_default_fn)),
(3, SkippedField::new(field_3_default_fn)),
].into_iter());
Serialization::is_ignored_field
已重命名为Serialization::is_field_skipped
- 标记为
#[reflect(skip_serializing)]
的字段现在包含在反序列化输出中。这可能会影响期望这些字段不存在的逻辑。
在 Camera::physical_viewport_rect 中返回 URect 而不是 (UVec2, UVec2) #
// 0.11
fn view_physical_camera_rect(camera_query: Query<&Camera>) {
let camera = camera_query.single();
let Some((min, max)) = camera.physical_viewport_rect() else { return };
dbg!(min, max);
}
// 0.12
fn view_physical_camera_rect(camera_query: Query<&Camera>) {
let camera = camera_query.single();
let Some(URect { min, max }) = camera.physical_viewport_rect() else { return };
dbg!(min, max);
}
更新 bevy_window::PresentMode
以反映 wgpu::PresentMode
#
在手动调整窗口呈现模式时处理 bevy_window::PresentMode::FifoRelaxed
。
对 MeshUniform 使用 GpuArrayBuffer #
以旧方式访问单个网格对象的着色器 Mesh
结构的 model
成员,其中每个 MeshUniform
都存储在它自己的动态偏移量处
struct Vertex {
@location(0) position: vec3<f32>,
};
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(
mesh.model,
vec4<f32>(vertex.position, 1.0)
);
return out;
}
新方法需要索引到批次的 Mesh
数组中
struct Vertex {
@builtin(instance_index) instance_index: u32,
@location(0) position: vec3<f32>,
};
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(
mesh[vertex.instance_index].model,
vec4<f32>(vertex.position, 1.0)
);
return out;
}
请注意,使用 instance_index
是将每个对象索引传递到着色器的默认方式,但如果您希望进行自定义渲染方法,您可以按照您喜欢的任何方式传递它。
减小 MeshUniform 的大小以提高性能 #
Sphere::intersects_obb
和 Frustum::intersects_obb
现在接受 Affine3A
而不是 Mat4
。您可以使用 Affine3A::from_mat4
或 Transform::compute_affine
获取 Affine3A
。
MeshUniform
现在将其当前和之前的模型变换存储为 4x3 矩阵。为 bevy_pbr::mesh_functions
添加了帮助器函数来解包数据。
// 0.11
var model = mesh[instance_index].model;
// 0.12
#import bevy_pbr::mesh_functions::affine_to_square
var model = affine_to_square(mesh[instance_index].model);
重新排序渲染集,重构 bevy_sprite 以利用它 #
- 资产(如材质和网格)现在应该在
PrepareAssets
中创建,例如prepare_assets<Mesh>
- 将实体排队到
RenderPhase
继续在Queue
中完成,例如queue_sprites
- 准备资源(纹理、缓冲区等)现在应该在
PrepareResources
中完成,例如prepare_prepass_textures
、prepare_mesh_uniforms
- 准备绑定组现在应该在
PrepareBindGroups
中完成,例如prepare_mesh_bind_group
- 任何批处理或实例化现在可以在
Prepare
中完成,其中阶段项目的顺序已知,例如prepare_sprites
将 ComputedVisibility
分成两个组件以允许进行准确的更改检测并加快可见性传播 #
ComputedVisibility
组件已拆分为 InheritedVisibility
和 ViewVisibility
。将 ComputedVisibility::is_visible_in_hierarchy
的任何用法替换为 InheritedVisibility::get
,并将 ComputedVisibility::is_visible_in_view
替换为 ViewVisibility::get
。
// 0.11:
commands.spawn(VisibilityBundle {
visibility: Visibility::Inherited,
computed_visibility: ComputedVisibility::default(),
});
// 0.12:
commands.spawn(VisibilityBundle {
visibility: Visibility::Inherited,
inherited_visibility: InheritedVisibility::default(),
view_visibility: ViewVisibility::default(),
});
// 0.11:
fn my_system(q: Query<&ComputedVisibility>) {
for vis in &q {
if vis.is_visible_in_hierarchy() {
// 0.12:
fn my_system(q: Query<&InheritedVisibility>) {
for inherited_visibility in &q {
if inherited_visibility.get() {
// 0.11:
fn my_system(q: Query<&ComputedVisibility>) {
for vis in &q {
if vis.is_visible_in_view() {
// 0.12:
fn my_system(q: Query<&ViewVisibility>) {
for view_visibility in &q {
if view_visibility.get() {
// 0.11:
fn my_system(mut q: Query<&mut ComputedVisibility>) {
for vis in &mut q {
vis.set_visible_in_view();
// 0.12:
fn my_system(mut q: Query<&mut ViewVisibility>) {
for view_visibility in &mut q {
view_visibility.set();
清理 visibility
模块 #
check_visibility
系统的 Option<&NoFrustumCulling>
参数已被 Has<NoFrustumCulling>
替换,如果您正在手动调用它,则应更改类型以使其匹配
允许其他插件创建渲染器资源 #
RenderPlugin
现在接受 RenderCreation
枚举而不是 WgpuSettings
。RenderSettings::default()
返回 RenderSettings::Automatic(WgpuSettings::default())
。RenderSettings
也实现了 From<WgpuSettings>
。
// 0.11
RenderPlugin {
wgpu_settings: WgpuSettings {
...
},
}
// 0.12
RenderPlugin {
render_creation: RenderCreation::Automatic(WgpuSettings {
...
}),
}
// or
RenderPlugin {
render_creation: WgpuSettings {
...
}.into(),
}
对渲染世界实体存储使用 EntityHashMap<Entity, T> 以获得更好的性能 #
以前,渲染应用程序从主世界中提取网格实体及其组件数据,并将它们作为实体和组件存储在渲染世界中。现在它们被提取到本质上是 EntityHashMap<Entity, T>
中,其中 T
是包含适当数据组的结构。这意味着,虽然提取集系统将继续对主世界运行提取查询,但它们会将数据存储在哈希映射中。此外,后面集合中的系统将需要在可用资源中查找实体,例如 RenderMeshInstances
,或者维护自己的 EntityHashMap<Entity, T>
用于自己的数据。
// 0.11
fn queue_custom(
material_meshes: Query<(Entity, &MeshTransforms, &Handle<Mesh>), With<InstanceMaterialData>>,
) {
...
for (entity, mesh_transforms, mesh_handle) in &material_meshes {
...
}
}
// 0.12
fn queue_custom(
render_mesh_instances: Res<RenderMeshInstances>,
instance_entities: Query<Entity, With<InstanceMaterialData>>,
) {
...
for entity in &instance_entities {
let Some(mesh_instance) = render_mesh_instances.get(&entity) else { continue; };
// The mesh handle in `AssetId<Mesh>` form, and the `MeshTransforms` can now
// be found in `mesh_instance` which is a `RenderMeshInstance`
...
}
}
方向光/聚光灯阴影的 PCF #
方向光或聚光灯投射的阴影现在具有更平滑的边缘。要恢复到旧的行为,请将 ShadowFilteringMethod::Hardware2x2
添加到您的摄像机中。
对线框使用 Material
#
WireframePipeline
已被删除。如果您直接使用它,请创建一个问题来解释您的用例。
延迟渲染器 #
pbr 着色器清理 #
在自定义材质着色器中
pbr_functions::pbr
不再调用pbr_functions::alpha_discard
。如果您在带有 alpha 蒙版模式的自定义着色器中使用pbr
函数,现在您还需要手动调用 alpha_discard- 将
bevy_pbr::mesh_vertex_output
的导入重命名为bevy_pbr::forward_io
- 将
MeshVertexOutput
的实例重命名为VertexOutput
在自定义材质预处理着色器中
- 将
VertexOutput::clip_position
的实例重命名为VertexOutput::position
*_PREPASS
着色器定义清理 #
在使用 bevy_pbr::prepass_utils
(prepass_depth()
、prepass_normal()
、prepass_motion_vector()
)中的函数时,如果可能禁用这些预处理,则现在应该使用相应的 #ifdef
保护符包装您的调用,(#ifdef DEPTH_PREPASS
、#ifdef NORMAL_PREPASS
、#ifdef MOTION_VECTOR_PREPASS
),在适用时提供回退逻辑。
允许对 StandardMaterial 进行扩展 #
AsBindGroup
的手动实现将需要调整,更改非常简单,可以在例如 texture_binding_array
示例的差异中看到。
可变 MeshPipeline
视图绑定组布局 #
MeshPipeline::view_layout
和 MeshPipeline::view_layout_multisampled
已被一个私有数组替换,以适应可变视图绑定组布局。要为当前管道状态获取视图绑定组布局,请使用新的 MeshPipeline::get_view_layout()
或 MeshPipeline::get_view_layout_from_key()
方法。
更新着色器导入 #
naga_oil 0.10 重做了导入机制以支持更多语法使其更像 Rust,并在导入之前测试项目使用以确定哪些导入是模块,哪些是项目,这允许
- 使用 Rust 风格的导入
#import bevy_pbr::{
pbr_functions::{alpha_discard as discard, apply_pbr_lighting},
mesh_bindings,
}
- 导入部分路径
#import part::of::path
// ...
path::remainder::function();
这将调用 part::of::path::remainder::function
- 在不导入的情况下使用完全限定的路径
// #import bevy_pbr::pbr_functions
bevy_pbr::pbr_functions::pbr()
- 在不限定的情况下使用导入的项目
#import bevy_pbr::pbr_functions::pbr
// for backwards compatibility the old style is still supported:
// #import bevy_pbr::pbr_functions pbr
// ...
pbr()
- 允许大多数导入的项目以
_
和数字结尾 (naga_oil#30)。仍然不允许结构成员以_
或数字结尾,但这已经有所进步。 - 绝大多数现有的着色器代码将在无需更改的情况下运行,但会发出“已弃用”警告以提醒旧式导入。这些可以通过
allow-deprecated
功能来抑制。 - 部分打破了覆盖(据我所知,还没有人使用过这些)——现在覆盖只有在覆盖模块被添加为
Composer::make_naga_module
或Composer::add_composable_module
的参数中的附加导入时才会应用。这是支持确定导入是模块还是项目所必需的。
绑定组条目 #
- 对
RenderDevice::create_bind_group({BindGroupDescriptor { label, layout, entries })
的调用必须改为RenderDevice::create_bind_group(label, layout, entries)
。 - 如果
label
已指定为"bind_group_name".into()
,则需要将其更改为"bind_group_name"
。Some("bind_group_name")
和None
仍然有效,但Some("bind_group_name")
可以选择性地简化为"bind_group_name"
。
检测 dds 纹理的立方体贴图 #
如果您正在匹配 TextureError
,则需要添加一个新的分支来处理 TextureError::IncompleteCubemap
。
为 Image 添加便捷方法 #
将对 Image::size()
方法的调用替换为 size_f32()
。将对 Image::aspect_2d()
方法的调用替换为 aspect_ratio()
。
使用“镜面遮挡”一词来一致地熄灭环境光和环境贴图灯光上的菲涅耳 #
- 如果环境光和环境贴图灯光上的菲涅耳高光在您的材质中不再可见,请确保您使用的是更高的、物理上更合理的
reflectance
值 (⪆ 0.35)。
修复雾色不准确 #
FogSettings
结构中的颜色 (color
和 directional_light_color
) 现在以线性空间发送到 GPU。如果您使用的是 Color::rgb()
/Color::rgba()
并希望保留以前的颜色,您可以通过切换到 Color::rgb_linear()
/Color::rgba_linear()
来快速修复它。
图像采样器改进 #
- 使用
Image
API 时,使用ImageSamplerDescriptor
而不是wgpu::SamplerDescriptor
- 如果编写与
Image
一起使用的自定义 wgpu 渲染器功能,请调用&image_sampler.as_wgpu()
以转换为 wgpu 描述符。
StandardMaterial
光线透射 #
SsaoPipelineKey::temporal_noise
已重命名为SsaoPipelineKey::temporal_jitter
TAA
着色器定义(由相机中是否存在TemporalAntiAliasSettings
组件控制)已被TEMPORAL_JITTER
着色器定义(由相机中是否存在TemporalJitter
组件控制)取代。MeshPipelineKey::TAA
已被MeshPipelineKey::TEMPORAL_JITTER
取代。TEMPORAL_NOISE
着色器定义已与TEMPORAL_JITTER
合并。
增加默认法线偏移以避免常见伪影 #
DirectionalLight
和SpotLight
的默认shadow_normal_bias
值已更改以适应新的阴影 PCF 更改引入的伪影。 虽然不太可能(尤其是在这些值下新的 PCF 阴影行为),但如果您的场景需要较低的偏差并且依赖于先前的默认值,您可能需要手动调整此值。
使DirectionalLight
Cascades
计算对CameraProjection
通用 #
如果您有一个实现CameraProjection
的组件MyCustomProjection
- 您需要实现一个新的必需关联方法
get_frustum_corners
,该方法返回给定z_near
和z_far
的视锥体子集的角点数组,以局部相机空间表示。 - 您现在可以在
SimulationLightSystems::UpdateDirectionalLightCascades
中的clear_directional_light_cascades
之后添加build_directional_light_cascades::<MyCustomProjection>
系统,以使您的投影能够与定向光源一起工作。
将蒙皮代码移动到单独的模块 #
重命名蒙皮系统、资源和组件
- extract_skinned_meshes -> extract_skins
- prepare_skinned_meshes -> prepare_skins
- SkinnedMeshUniform -> SkinUniform
- SkinnedMeshJoints -> SkinIndex
将场景生成器系统移动到 SpawnScene 调度程序 #
scene_spawner_system
已移至新的SpawnScene
调度程序,该调度程序在Update
和PostUpdate
之间运行。
如果您之前在Update
中对自己的系统进行排序以在scene_spawner_system
之前运行,则可能不再需要这样做。 如果您的系统需要在scene_spawner_system
之后运行,则应将其移至SpawnScene
或PostUpdate
调度程序。
从 TaskPoolOptions 中删除 Resource 并添加 Debug #
如果出于某种原因,任何人仍在将TaskPoolOptions
用作 Resource,他们现在必须使用包装器类型
#[derive(Resource)]
pub struct MyTaskPoolOptions(pub TaskPoolOptions);
全局 TaskPool API 改进 #
ComputeTaskPool::init
、AsyncComputeTaskPool::init
和IoTaskPool::init
的使用应更改为::get_or_init
。
统一FixedTime
和Time
,同时修复多个问题 #
- 将所有访问
raw_delta()
、raw_elapsed()
和相关方法的Res<Time>
实例更改为Res<Time<Real>>
和delta()
、elapsed()
等。 - 将对
Res<FixedTime>
中的period
的访问更改为Res<Time<Fixed>>
并使用delta()
。 - 默认时间步长已从 60 Hz 更改为 64 Hz。如果您希望恢复旧的行为,请使用
app.insert_resource(Time::<Fixed>::from_hz(60.0))
。 - 将
app.insert_resource(FixedTime::new(duration))
更改为app.insert_resource(Time::<Fixed>::from_duration(duration))
- 将
app.insert_resource(FixedTime::new_from_secs(secs))
更改为app.insert_resource(Time::<Fixed>::from_seconds(secs))
- 将
system.on_fixed_timer(duration)
更改为system.on_timer(duration)
。放置在FixedUpdate
调度程序中的系统中的计时器会自动使用固定时间时钟。 - 将
ResMut<Time>
调用更改为ResMut<Time<Virtual>>
调用,用于pause()
、is_paused()
、set_relative_speed()
和相关方法。 API 相同,不同之处在于relative_speed()
将返回实际的最后一步相对速度,而effective_relative_speed()
如果时间暂停将返回 0.0,并且对应于当前帧更新开始时设置的速度。
将ContentSize
的measure_func
字段的默认值更改为 None。 #
ContentSize
的默认值现在将其measure_func
设置为None
,而不是返回Vec2::ZERO
的固定大小度量。 助手函数fixed_size
可以使用ContentSize::fixed_size(Vec2::ZERO)
调用,以获得以前的行为。
将UiScale
更改为元组结构体 #
将UiScale
的初始化(如UiScale { scale: 1.0 }
)替换为UiScale(1.0)
清理一些 bevy_text pipeline.rs #
measure_text_system
的ResMut<TextPipeline>
参数不再存在。 如果您是手动调用此系统,则应删除该参数。TextMeasureInfo
的{min,max}_width_content_size
字段分别重命名为min
和max
- 如果手动构建
TextMeasureInfo
,则其他更改也可能会破坏您的代码。 请考虑使用新的TextMeasureInfo::from_text
来构建它。 TextPipeline::create_text_measure
已被TextMeasureInfo::from_text
取代。
使GridPlacement
的字段非零并添加访问器函数。 #
GridPlacement
的构造函数不再接受值为0
的值。 给定任何值为0
的参数,它们将使用GridPlacementError
抛出异常。
删除Val
的try_*
算术方法 #
Val
的try_*
算术方法已被删除。 要对Val
执行算术运算,请使用模式匹配对其进行解构。
将Val
evaluate
重命名为resolve
并实现视口变体支持 #
- 重命名了以下
Val
方法并添加了viewport_size
参数evaluate
toresolve
try_add_with_size
totry_add_with_context
try_add_assign_with_size
totry_add_assign_with_context
try_sub_with_size
totry_sub_with_context
try_sub_assign_with_size
totry_sub_assign_with_context
TextLayoutInfo::size
应该保存文本的绘制大小,而不是缩放后的值。 #
TextLayoutInfo
的size
值以逻辑像素存储,并且已被重命名为logical_size
。 不再需要除以窗口的比例因子来获取逻辑大小。
每个根节点都有一个独立的隐式视口节点,并将视口节点设为Display::Grid
#
- Bevy UI 现在在独立的布局上下文中独立地布局根节点。 如果您依赖于根节点能够影响彼此的布局,那么您可能需要将它们包装在一个根节点中。
- 隐式视口节点(包含每个用户指定的根节点)现在为
Display::Grid
,其align_items
和justify_items
都设置为Start
。 如果您之前依赖于隐式设置,您可能需要在根节点中添加height: Val::Percent(100.)
。
将num_font_atlases
重命名为len
。 #
FontAtlasSet
的num_font_atlases
方法已被重命名为len
。
各种辅助功能 API 更新。 #
将AccessibilityRequested
的直接访问更改为使用AccessibilityRequested.::get()
/AccessibilityRequested::set()
// 0.11
use std::sync::atomic::Ordering;
// To access
accessibility_requested.load(Ordering::SeqCst)
// To update
accessibility_requested.store(true, Ordering::SeqCst);
// 0.12
// To access
accessibility_requested.get()
// To update
accessibility_requested.set(true);
为 bevy_text 添加更多文档。 #
bevy_text
中对TextSettings.max_font_atlases
的使用必须更改为TextSettings.soft_max_font_atlases
。
更新 UI 对齐文档 #
JustifyContents
枚举已扩展为包含JustifyContents::Stretch
。
添加选项以切换窗口控制按钮 #
在Window
结构体中添加了一个enabled_buttons
成员,用户可以通过该成员启用或禁用特定的窗口控制按钮。
改进bevy_winit
文档 #
UpdateMode::Reactive { max_wait: .. }
->UpdateMode::Reactive { wait: .. }
UpdateMode::ReactiveLowPower { max_wait: .. }
->UpdateMode::ReactiveLowPower { wait: .. }
解决 naga/wgpu WGSL instance_index -> GLSL gl_InstanceID 在 WebGL2 上的错误 #
之前的着色器代码
struct Vertex {
@builtin(instance_index) instance_index: u32,
...
}
@vertex
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
// ...
var model = mesh[vertex_no_morph.instance_index].model;
}
之后
#import bevy_render::instance_index
struct Vertex {
@builtin(instance_index) instance_index: u32,
// ...
}
@vertex
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
// ...
let instance_index = bevy_render::instance_index::get_instance_index(vertex_no_morph.instance_index);
var model = mesh[instance_index].model;
}
删除&mut EventReader
的IntoIterator
实现 #
&mut EventReader
不再实现IntoIterator
。 将for foo in &mut events
替换为for foo in events.iter()
更新默认ClearColor
以更好地匹配 Bevy 的品牌 #
默认应用程序背景颜色已更改。 要使用旧的默认值,请添加ClearColor
资源。
App::new()
.insert_resource(ClearColor(Color::rgb(0.4, 0.4, 0.4)))
.add_plugins(DefaultPlugins)
视图变换 #
mesh_functions::mesh_position_world_to_clip
已被移动并重命名为view_transformations::position_world_to_clip
。 它现在还接受vec3
而不是vec4
,因此您需要使用vec4.xyz
来获取vec3
。
// 0.11
#import bevy_pbr::mesh_functions::mesh_position_world_to_clip
fn mesh_position_local_to_clip(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
let world_position = mesh_position_local_to_world(model, vertex_position);
return mesh_position_world_to_clip(world_position);
}
// 0.12
#import bevy_pbr::view_transformations::position_world_to_clip;
fn mesh_position_local_to_clip(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
let world_position = mesh_position_local_to_world(model, vertex_position);
return position_world_to_clip(world_position.xyz);
}