|
1 |
| -//! In the context of game development, an "asset" is a piece of multimedia content which must be loaded from disk and displayed in the game. |
2 |
| -//! Typically, these are authored by artists and designers (in contrast to code), |
3 |
| -//! are relatively large in size, and include everything from textures and models to sounds and music to levels and scripts. |
4 |
| -//! |
5 |
| -//! This presents two main challenges: |
6 |
| -//! - assets take up a lot of memory: simply storing a copy for each instance of an asset in the game would be prohibitively expensive |
7 |
| -//! - loading assets from disk is slow, and can cause the game to stutter or freeze if done in the middle of gameplay |
8 |
| -//! |
9 |
| -//! These problems play into each other: if assets are expensive to store in memory, |
10 |
| -//! larger game worlds will need to load them from disk as needed, ideally without a loading screen! |
11 |
| -//! |
12 |
| -//! Unsurprisingly, the problem of non-blocking asset loading is done using `async`, where background tasks are used to load assets while the game is running. |
13 |
| -//! Bevy coordinates these tasks using the [`AssetServer`], storing each loaded asset in a strongly-typed [`Assets<T>`] collection. |
14 |
| -//! [`Handle`]s serve as an id-based reference to entries in the [`Assets`] collection, allowing them to be cheaply shared between systems, |
15 |
| -//! and providing a way to initialize objects (generally entities) before the required assets are loaded. |
16 |
| -//! |
17 |
| -//! ## Loading and using assets |
18 |
| -//! |
19 |
| -//! The [`AssetServer`] is the main entry point for loading assets. |
20 |
| -//! Typically, you'll use the [`AssetServer::load`] method to load an asset from disk, which returns a [`Handle`]. |
21 |
| -//! Note that this method does not attempt to reload the asset if it has already been loaded: as long as the asset hasn't been dropped, |
22 |
| -//! calling [`AssetServer::load`] on the same path will return the same handle. |
23 |
| -//! The handle that's returned will be used to instantiate various [`Component`](bevy_ecs::prelude::Component)s that require asset data to function, |
24 |
| -//! which will then be spawned into the world as part of an entity. |
25 |
| -//! |
26 |
| -//! To avoid assets "popping" into existence, you may want to check that all of the required assets are loaded before transitioning to a new scene. |
27 |
| -//! This can be done by checking the [`LoadState`] of the asset handle using [`AssetServer::is_loaded_with_dependencies`], |
28 |
| -//! which will be [`LoadState::Loaded`] when the asset is ready to use. |
29 |
| -//! Keep track of what you're waiting on using a [`HashSet`] or similar data structure, |
30 |
| -//! poll in your update loop, and transition to the new scene when all assets are loaded. |
31 |
| -//! Bevy's built-in states system can be very helpful for this! |
32 |
| -//! |
33 |
| -//! If we later want to manipulate this asset data (such as for displaying a death animation), we have three options: |
34 |
| -//! |
35 |
| -//! 1. Despawn the entity and spawn a new one with the new asset data. |
36 |
| -//! 2. Change what the handle is pointing to. |
37 |
| -//! 3. Use the [`Assets`] collection to directly modify the asset data. |
38 |
| -//! |
39 |
| -//! The first option is the simplest, but can be slow if done frequently, |
40 |
| -//! and can lead to frustrating bugs as references to the old entity (such as what is targeting it) and other data on the entity are lost. |
41 |
| -//! Generally, this isn't a great strategy. |
42 |
| -//! |
43 |
| -//! The second option is the most common: just query for the component that holds the handle, and mutate it, pointing to the new asset. |
44 |
| -//! |
45 |
| -//! The third option has different semantics: rather than modifying the asset data for a single entity, it modifies the asset data for *all* entities using this handle. |
46 |
| -//! While this might be what you want, it generally isn't! |
47 |
| -//! |
48 |
| -//! ## Handles and reference counting |
49 |
| -//! |
50 |
| -//! [`Handle`] (or their untyped counterpart [`UntypedHandle`]) are used to reference assets in the [`Assets`] collection, |
51 |
| -//! and are the primary way to interact with assets in Bevy. |
52 |
| -//! As a user, you'll be working with handles a lot! |
53 |
| -//! |
54 |
| -//! The most important thing to know about handles is that they are reference counted: when you clone a handle, you're incrementing a reference count. |
55 |
| -//! When the object holding the handle is dropped (generally because an entity was despawned), the reference count is decremented. |
56 |
| -//! When the reference count hits zero, the asset it references is removed from the [`Assets`] collection. |
57 |
| -//! To avoid incrementing the reference count, you can use the [`Handle::clone_weak`] method, which is marginally faster. |
58 |
| -//! |
59 |
| -//! This reference counting is a simple, laregely automatic way to avoid holding onto memory for game objects that are no longer in use. |
60 |
| -//! However, it can lead to surprising behavior if you're not careful! |
61 |
| -//! |
62 |
| -//! There are two categories of problems to watch out for: |
63 |
| -//! - never dropping a handle, causing the asset to never be removed from memory |
64 |
| -//! - dropping a handle too early, causing the asset to be removed from memory while it's still in use |
65 |
| -//! |
66 |
| -//! The first problem is less critical for beginners, as for tiny games, you can often get away with simply storing all of the assets in memory at once, |
67 |
| -//! and loading them all at the start of the game. |
68 |
| -//! As your game grows, you'll need to be more careful about when you load and unload assets, |
69 |
| -//! segmenting them by level or area, and loading them on-demand. |
70 |
| -//! This problem generally arises when handles are stored in a persistent "zoo" or "manifest" of possible objects (generally in a resource), |
71 |
| -//! which is convenient for easy access and zero-latency spawning, but can result in high but stable memory usage. |
72 |
| -//! |
73 |
| -//! The second problem is more concerning, and looks like your models or textures suddenly disappearing from the game. |
74 |
| -//! Debugging reveals that the *entities* are still there, but nothing is rendering! |
75 |
| -//! This is because the assets were removed from memory while they were still in use. |
76 |
| -//! You were probably too aggressive with the use of weak handles: think through the lifetime of your assets carefully! |
77 |
| -//! As soon as an asset is loaded, you must ensure that at least one strong handle is held to it until all matching entities are out of sight of the player. |
78 |
| -//! |
79 |
| -//! # Custom asset types |
80 |
| -//! |
81 |
| -//! While Bevy comes with implementations for a large number of common game-oriented asset types (often behind off-by-default feature flags!), |
82 |
| -//! implementing a custom asset type can be useful when dealing with unusual, game-specific, or proprietary formats. |
83 |
| -//! |
84 |
| -//! Defining a new asset type is as simple as implementing the [`Asset`] trait. |
85 |
| -//! This requires [`TypePath`] for metadata about the asset type, |
86 |
| -//! and [`VisitAssetDependencies`] to track asset dependencies. |
87 |
| -//! In simple cases, you can derive [`Asset`] and [`Reflect`] and be done with it: the required supertraits will be implemented for you. |
88 |
| -//! |
89 |
| -//! With a new asset type in place, we now need to figure out how to load it. |
90 |
| -//! While [`AssetReader`](io::AssetReader) describes strategies to read assets from various sources, |
91 |
| -//! [`AssetLoader`] is the trait that actually turns those into your desired format. |
92 |
| -//! Generally, only) [`AssetLoader`] needs to be implemented for custom assets, as the [`AssetReader`](io::AssetReader) implementations are provided by Bevy. |
93 |
| -//! |
94 |
| -//! However, [`AssetLoader`] shouldn't be implemented for your asset type directly: instead, this is implemented for a "loader" type |
95 |
| -//! that can store complex intermediate state, while your asset type is used as the [`AssetLoader::Asset`] associated type. |
96 |
| -//! As the trait documentation explains, this allows various [`AssetLoader::Settings`] to be used to configure the loader. |
97 |
| -//! |
98 |
| -//! After the loader is implemented, it needs to be registered with the [`AssetServer`] using [`App::register_asset_loader`](AssetApp::register_asset_loader). |
99 |
| -//! Once your asset type is loaded, you can use it in your game like any other asset type! |
100 |
| -//! |
101 |
| -//! If you want to save your assets back to disk, you should implement [`AssetSaver`](saver::AssetSaver) as well. |
102 |
| -//! This trait mirrors [`AssetLoader`] in structure, and works in tandem with [`AssetWriter`](io::AssetWriter), which mirrors [`AssetReader`](io::AssetReader). |
103 |
| -
|
104 | 1 | // FIXME(3492): remove once docs are ready
|
105 | 2 | #![allow(missing_docs)]
|
106 | 3 | #![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
@@ -334,24 +231,13 @@ impl Plugin for AssetPlugin {
|
334 | 231 | }
|
335 | 232 | }
|
336 | 233 |
|
337 |
| -/// Declares that this type is an asset, |
338 |
| -/// which can be loaded and managed by the [`AssetServer`] and stored in [`Assets`] collections. |
339 |
| -/// |
340 |
| -/// Generally, assets are large, complex, and/or expensive to load from disk, and are often authored by artists or designers. |
341 |
| -/// |
342 |
| -/// [`TypePath`] is largely used for diagnostic purposes, and should almost always be implemented by deriving [`Reflect`] on your type. |
343 |
| -/// [`VisitAssetDependencies`] is used to track asset dependencies, and an implementation automatically generated when deriving [`Asset`]. |
344 | 234 | #[diagnostic::on_unimplemented(
|
345 | 235 | message = "`{Self}` is not an `Asset`",
|
346 | 236 | label = "invalid `Asset`",
|
347 | 237 | note = "consider annotating `{Self}` with `#[derive(Asset)]`"
|
348 | 238 | )]
|
349 | 239 | pub trait Asset: VisitAssetDependencies + TypePath + Send + Sync + 'static {}
|
350 | 240 |
|
351 |
| -/// This trait defines how to visit the dependencies of an asset. |
352 |
| -/// For example, a 3D model might require both textures and meshes to be loaded. |
353 |
| -/// |
354 |
| -/// Note that this trait is automatically implemented when deriving [`Asset`]. |
355 | 241 | pub trait VisitAssetDependencies {
|
356 | 242 | fn visit_dependencies(&self, visit: &mut impl FnMut(UntypedAssetId));
|
357 | 243 | }
|
|
0 commit comments