27个版本 (重大变更)

0.21.0 2024年7月6日
0.20.1 2024年4月13日
0.20.0 2024年2月18日
0.19.0 2023年12月27日
0.4.0 2021年6月26日

#10 in 游戏开发

Download history 2516/week @ 2024-05-04 2144/week @ 2024-05-11 2754/week @ 2024-05-18 2235/week @ 2024-05-25 2387/week @ 2024-06-01 2023/week @ 2024-06-08 2380/week @ 2024-06-15 2061/week @ 2024-06-22 1151/week @ 2024-06-29 1906/week @ 2024-07-06 2417/week @ 2024-07-13 2594/week @ 2024-07-20 3002/week @ 2024-07-27 2297/week @ 2024-08-03 3107/week @ 2024-08-10 2433/week @ 2024-08-17

11,246每月下载量
用于 21 个包(直接使用19个)

MIT/Apache

1MB
1.5K SLoC

Bevy资源加载器

crates.io docs license crates.io

用最少样板代码加载状态

Bevy插件减少了处理游戏资源的样板代码。该包提供了可派生的AssetCollection特质,并可以自动加载实现它的结构体。资源集合包含对游戏资源的引用,并在加载后作为资源提供给您的系统。

在大多数情况下,您希望在加载状态(例如加载界面)期间加载资源集合。在此状态下,所有资源都将被加载,并观察其加载进度。只有在资源集合可以使用完全加载的资源引用构建时,集合才会被插入到Bevy的ECS中作为资源。如果您不想使用加载状态,资源集合仍然可以生成更简洁的代码和更好的可维护性(请参阅"无加载状态的使用"部分)。

主分支和最新版本支持Bevy版本0.14(请参阅版本表

加载状态

加载状态负责管理在可配置的Bevy状态(请参阅状态技巧手册)期间的加载过程。

如果您的LoadingState已设置,您可以从配置的"下一个状态"开始您的游戏逻辑,并在您的系统中使用资源集合。加载状态保证在下一个状态开始时,您的集合中的所有引用都已完全加载。

use bevy::prelude::*;
use bevy_asset_loader::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .init_state::<MyStates>()
        .add_loading_state(
            LoadingState::new(MyStates::AssetLoading)
                .continue_to_state(MyStates::Next)
                .load_collection::<AudioAssets>(),
        )
        .add_systems(OnEnter(MyStates::Next), start_background_audio)
        .run();
}

#[derive(AssetCollection, Resource)]
struct AudioAssets {
    #[asset(path = "audio/background.ogg")]
    background: Handle<AudioSource>,
}

/// This system runs in MyStates::Next. Thus, AudioAssets is available as a resource
/// and the contained handle is done loading.
fn start_background_audio(mut commands: Commands, audio_assets: Res<AudioAssets>) {
    commands.spawn(AudioBundle {
        source: audio_assets.background.clone(),
        settings: PlaybackSettings::LOOP,
    });
}

#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
enum MyStates {
    #[default]
    AssetLoading,
    Next,
}

如果我们想向刚刚添加的加载状态添加额外的资源集合,我们可以在应用的任何位置使用configure_loading_state。例如,我们可以在添加加载状态后向App添加一个PlayerPlugin。该PlayerPlugin将包含所有"玩家"相关的事物,包括其精灵。

use bevy::prelude::*;
use bevy_asset_loader::prelude::*;

struct PlayerPlugin;

impl Plugin for PlayerPlugin {
    fn build(&self, app: &mut App) {
        app
            .configure_loading_state(
                LoadingStateConfig::new(MyStates::AssetLoading)
                    .load_collection::<PlayerAssets>(),
            );
    }
}

#[derive(AssetCollection, Resource)]
struct PlayerAssets {
    #[asset(path = "images/player.png")]
    sprite: Handle<Image>,
}

#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
enum MyStates {
    #[default]
    AssetLoading,
    Next,
}

编译时与运行时(动态)资源

资产配置,例如文件路径或精灵表尺寸,可以在编译时(通过派生宏属性)或运行时(见"动态资产")给出。后者允许将资产配置作为资产来管理。这意味着您可以在资产文件中保留一个资产文件及其属性的列表。使用动态资产的主要优点是实现代码和数据更清晰的分离,从而在处理资产时减少重新编译次数。它还使游戏更易于被希望贡献而不修改代码的人接触。

AssetCollection提供的派生宏支持多个属性。它们配置了资产的加载方式。

编译时资产

前两个代码示例显示了具有所有配置在派生宏属性中的集合的加载状态。玩家精灵的路径被硬编码为"images/player.png"。更改它需要重新编译您的应用程序。

full_collection 示例展示了资产集合可以使用派生宏属性包含的所有不同类型的字段。

动态资产

动态资产通过派生宏属性 key 进行配置,不允许有 pathpaths 属性

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct ImageAssets {
  #[asset(key = "player")]
  player: Handle<Image>,
  #[asset(key = "tree")]
  tree: Handle<Image>,
}

示例中 playertree 这两个键应在加载状态之前在 DynamicAssets 资源中手动设置(见manual_dynamic_asset 示例),或作为动态资产文件的一部分(见 dynamic_asset.rs)。上述集合的动态资产文件可能看起来像这样

({
    "player": File (
        path: "images/player.png",
    ),
    "tree": File (
        path: "images/tree.png",
    ),
})

使用类似于 File 的动态资产和加载 ron 文件需要启用 standard_dynamic_assets 功能。

默认情况下,文件扩展名为 LoadingState::set_standard_dynamic_asset_collection_file_endings

示例 full_dynamic_collection 展示了动态资产支持的所有字段类型。请注意,将动态资产文件添加到加载状态需要 AssetServer 资源可用。在大多数情况下,这意味着您应该在配置加载状态之前添加 DefaultPlugins

自定义动态资产

您可以定义自己的类型以作为动态资产加载。请参阅custom_dynamic_assets.rs 示例中的代码。

支持的资产字段

最简单的字段类型为 Handle<T>,并从单个文件加载。一个例子可能是音频源,但任何已注册 Bevy 的资产加载器类型的资产都可以这样使用。

字段应仅设置 path 属性。

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(path = "my-background.ogg")]
    background: Handle<AudioSource>,
}

同一集合的动态版本看起来像这样

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(key = "background")]
    background: Handle<AudioSource>,
}
({
    "background": File (
        path: "my-background.ogg",
    ),
})

以下部分将描述更多类型的资产字段,您可以通过资产集合加载它们。

纹理图集

如果您启用 2d 功能,可以在 AssetCollection 中创建纹理图集布局。有关完整示例,请参阅 atlas_from_grid.rs

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(texture_atlas_layout(tile_size_x = 64, tile_size_y = 64, columns = 8, rows = 1, padding_x = 12, padding_y = 12, offset_x = 6, offset_y = 6))]
    layout: Handle<TextureAtlasLayout>,
    #[asset(path = "images/sprite_sheet.png")]
    sprite: Handle<Image>,
}

作为一个动态资产,这个例子变成了

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(key = "player.layout")]
    layout: Handle<TextureAtlasLayout>,
    #[asset(key = "player.image")]
    sprite: Handle<Image>,
}
({
    "player.image": File (
        path: "images/sprite_sheet.png",
    ),
    "player.layout": TextureAtlasLayout (
        tile_size_x: 100,
        tile_size_y: 64,
        columns: 8,
        rows: 1,
        padding_x: 12,
        padding_y: 12,
        offset_x: 6,
        offset_y: 6,
    ),
})

四个填充 & 偏移字段/属性是可选的,默认值为 0

具有采样器配置的图像

资产集合支持通过派生属性配置图像资产的采样器。

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct ImageAssets {
    #[asset(path = "images/pixel_tree.png")]
    #[asset(image(sampler = linear))]
    tree_linear: Handle<Image>,

    #[asset(path = "images/pixel_tree.png")]
    #[asset(image(sampler = nearest))]
    tree_nearest: Handle<Image>,
}

相应的动态资产将是

({
    "tree_nearest": Image (
        path: "images/tree.png",
        sampler: Nearest
    ),
    "tree_linear": Image (
        path: "images/tree.png",
        sampler: Linear
    ),
})

标准材质

如果您启用功能 3d,则可以直接加载标准材质。请查看standard_material.rs 获取完整示例。

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(standard_material)]
    #[asset(path = "images/player.png")]
    player: Handle<StandardMaterial>,
}

这也可以作为动态资产支持

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(key = "image.player")]
    player: Handle<StandardMaterial>,
}
({
    "image.player": StandardMaterial (
        path: "images/player.png",
    ),
})

集合

文件夹

此资产字段类型不支持在Web构建中。有关在Web兼容方式中加载文件集合的说明,请参阅文件

您可以将文件夹中的所有文件作为未类型化句柄的向量加载。此字段需要额外的派生宏属性 collection

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(path = "images", collection)]
    folder: Vec<UntypedHandle>,
}

就像Bevy的 load_folder 一样,此功能也将递归加载子文件夹。

如果文件夹中的所有资产都具有相同的(已知)类型,则可以通过将 typed 设置在 collection 属性中来将文件夹作为 Vec<Handle<T>> 加载。不要忘记调整结构字段类型

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(path = "images", collection(typed))]
    folder: Vec<Handle<Image>>,
}

文件夹也支持作为动态资产。路径属性由 key 属性替换

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(key = "my.images", collection(typed))]
    images: Vec<Handle<Image>>,
}
({
    "my.images": Folder (
        path: "images",
    ),
})

Web构建不支持加载文件夹。如果需要与Wasm兼容,请从路径列表中加载句柄(请参阅下一节)。

路径列表

如果要将相同类型的资产文件列表加载到 Handle<T> 的向量中,可以在属性中列出它们的路径

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(paths("images/player.png", "images/tree.png"), collection(typed))]
    files_typed: Vec<Handle<Image>>,
}

如果您不知道它们的类型,或者它们可能具有不同的类型,句柄也可以是未类型化的

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(paths("images/player.png", "sound/background.ogg"), collection)]
    files_untyped: Vec<UntypedHandle>,
}

作为动态资产,这两个字段将它们的 paths 属性替换为 key。这与文件夹相同。

use bevy::prelude::*;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(key = "files_untyped", collection)]
    files_untyped: Vec<UntypedHandle>,
    #[asset(key = "files_typed", collection(typed))]
    files_typed: Vec<Handle<Image>>,
}

相应的资产文件与文件夹示例不同

({
    "files_untyped": Files (
        paths: ["images/tree.png", "images/player.png"],
    ),
    "files_typed": Files (
        paths: ["images/tree.png", "images/player.png"],
    ),
})

集合作为映射

可以使用任何类型作为键,实现 MapKey,将集合作为映射加载。有关更多详细信息,请参阅文档。此更改仅涉及派生属性和资产字段类型。上面的示例将看起来像这样

use bevy::prelude::*;
use bevy::utils::HashMap;
use bevy_asset_loader::asset_collection::AssetCollection;

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(path = "images", collection(mapped))]
    folder: HashMap<String, UntypedHandle>,
    #[asset(paths("images/player.png", "images/tree.png"), collection(typed, mapped))]
    files_typed: HashMap<String, Handle<Image>>,
    #[asset(key = "files_untyped", collection(mapped))]
    dynamic_files_untyped: HashMap<String, UntypedHandle>,
    #[asset(key = "files_typed", collection(typed, mapped))]
    dynamic_files_typed: HashMap<String, Handle<Image>>,
}

实现FromWorld的类型

资产集合中没有任何属性的任何字段都必须实现 FromWorld 特性。当资产集合构建时,将调用 FromWorld 实现来获取字段的值。

初始化FromWorld资源

在您想要根据加载的资产集合准备其他资源的情况下,可以使用 LoadingState::init_resourceLoadingStateConfig::init_resource 来初始化 FromWorld 资源。请参阅init_resource.rs 的示例,该示例加载两个图像,然后将其像素数据组合成第三个图像。

init_resource 两种方法来自 bevy_asset_loader 与 Bevy 的 App::init_resource 做同样的事情,但时间点不同。当 Bevy 在应用程序启动时插入资源时,bevy_asset_loader 只在您的资产集合可用后初始化它们。这意味着您可以在资源的 FromWorld 实现中使用资产集合。

进度跟踪

使用功能 progress_tracking,您可以集成 iyes_progress 以在加载状态期间跟踪资产加载。例如,这可以启用进度条。

查看完整的示例 progress_tracking

关于系统排序的说明

加载状态在一个私有计划中组织,该计划在 Update 计划中运行的单个系统中运行。如果您想明确地针对运行加载状态的系统进行排序,可以使用导出的系统集 LoadingStateSet 来这样做。

失败状态

您可以通过使用状态调用 on_failure_continue_to 来配置失败状态,以处理某个集合中的资产加载失败的情况(请参阅failure_state.rs 示例)。如果没有配置失败状态并且某些资产加载失败,则您的应用程序将陷入加载状态。

在大多数加载状态失败的案例中,资产文件丢失或某个资产没有注册资产加载器。在这两种情况下,应用程序日志应该有所帮助,因为 Bevy 会打印有关这些问题的警告。

无加载状态的使用

尽管加载状态的模式相当好(个人看法),您可能有不使用它的原因。在这种情况下,bevy_asset_loader 仍然很有帮助。在资源上派生 AssetCollection 可以显著减少管理资产时的样板代码。

没有加载状态加载的资产集合不支持文件夹或动态资产,因为这些无法立即创建最终指向已加载资产的句柄。

您可以直接在 bevy 的 AppWorld 上初始化资产集合。请参阅 no_loading_state.rs 以获取完整的示例。

use bevy::prelude::*;
use bevy_asset_loader::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .init_collection::<MyAssets>()
        .run();
}

#[derive(AssetCollection, Resource)]
struct MyAssets {
    #[asset(texture_atlas_layout(tile_size_x = 64, tile_size_y = 64, columns = 8, rows = 1, padding_x = 12, padding_y = 12, offset_x = 6, offset_y = 6))]
    layout: Handle<TextureAtlasLayout>,
    #[asset(path = "images/sprite_sheet.png")]
    sprite: Handle<Image>,
}

卸载资产

当没有指向资产的强资产句柄时,Bevy 会卸载资产。一个 AssetCollection 存储强句柄并确保包含其中的资产不会被从内存中移除。如果您想卸载资产,则需要移除任何持有指向这些资产的句柄的 AssetCollection 资源。例如,您可以在离开需要集合的状态时这样做。

兼容的 Bevy 版本

主分支与最新的 Bevy 版本兼容,而 bevy_main 分支试图跟踪 Bevy 的 main 分支(欢迎提交更新跟踪提交的 PR)。

bevy_asset_loader 版本的兼容性

Bevy 版本 bevy_asset_loader 版本
0.14 0.21
0.13 0.20
0.12 0.18 - 0.19
0.11 0.17
0.10 0.15 - 0.16
0.9 0.14
0.8 0.12 - 0.13
0.7 0.10 - 0.11
0.6 0.8 - 0.9
0.5 0.1 - 0.7
0.13 分支 main
main 分支 bevy_main

许可证

双重许可以下任一项

任选一项。

示例中的资产可能以不同的条款分发。请参阅 bevy_asset_loader/examples 目录中的 readme

贡献

除非您明确说明,否则根据 Apache-2.0 许可证定义的,您提交的旨在包含在作品中的任何有意贡献,都应如上所述双重许可,不附加任何额外条款或条件。

依赖项

~23–62MB
~1M SLoC