Documented Handles and Assets (#3348)

# Objective

Add documentation to [Handle](7356f1586d/crates/bevy_asset/src/handle.rs (L63)) and [Assets](7356f1586d/crates/bevy_asset/src/assets.rs (L48)).

## Tasks
- [x] Document `Handle`
- [x] Document `Assets`

---

Fixes #3347


Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
This commit is contained in:
MrGVSV 2022-01-04 23:38:39 +00:00
parent 24b21ea35e
commit 36390a80ec
2 changed files with 92 additions and 0 deletions

View File

@ -9,6 +9,8 @@ use crossbeam_channel::Sender;
use std::fmt::Debug;
/// Events that happen on assets of type `T`
///
/// Events sent via the [Assets] struct will always be sent with a _Weak_ handle
pub enum AssetEvent<T: Asset> {
Created { handle: Handle<T> },
Modified { handle: Handle<T> },
@ -44,6 +46,18 @@ impl<T: Asset> Debug for AssetEvent<T> {
}
/// Stores Assets of a given type and tracks changes to them.
///
/// Each asset is mapped by a unique [HandleId](crate::HandleId), allowing any [Handle](crate::Handle)
/// with the same HandleId to access it. These assets remain loaded for as long as a Strong handle
/// to that asset exists.
///
/// To store a reference to an asset without forcing it to stay loaded, you can use a Weak handle.
/// To make a Weak handle a Strong one, use [Assets::get_handle](crate::Assets::get_handle) or pass
/// the Assets collection into the handle's [make_strong](crate::Handle::make_strong) method.
///
/// Remember, if there are no Strong handles for an asset (i.e. they have all been dropped), the asset
/// will unload. Make sure you always have a Strong handle when you want to keep an asset loaded!
///
#[derive(Debug)]
pub struct Assets<T: Asset> {
assets: HashMap<HandleId, T>,
@ -60,6 +74,10 @@ impl<T: Asset> Assets<T> {
}
}
/// Adds an asset to the collection, returning a Strong handle to that asset.
///
/// # Events
/// * [`AssetEvent::Created`]
pub fn add(&mut self, asset: T) -> Handle<T> {
let id = HandleId::random::<T>();
self.assets.insert(id, asset);
@ -69,6 +87,12 @@ impl<T: Asset> Assets<T> {
self.get_handle(id)
}
/// Add/modify the asset pointed to by the given handle.
///
/// Unless there exists another Strong handle for this asset, it's advised to use the returned
/// Strong handle. Not doing so may result in the unexpected release of the asset.
///
/// See [set_untracked](Assets::set_untracked) for more info.
#[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
pub fn set<H: Into<HandleId>>(&mut self, handle: H, asset: T) -> Handle<T> {
let id: HandleId = handle.into();
@ -76,6 +100,14 @@ impl<T: Asset> Assets<T> {
self.get_handle(id)
}
/// Add/modify the asset pointed to by the given handle.
///
/// If an asset already exists with the given HandleId, it will be modified. Otherwise the new asset
/// will be inserted.
///
/// # Events
/// * [`AssetEvent::Created`]: Sent if the asset did not yet exist with the given handle
/// * [`AssetEvent::Modified`]: Sent if the asset with given handle already existed
pub fn set_untracked<H: Into<HandleId>>(&mut self, handle: H, asset: T) {
let id: HandleId = handle.into();
if self.assets.insert(id, asset).is_some() {
@ -89,14 +121,23 @@ impl<T: Asset> Assets<T> {
}
}
/// Get the asset for the given handle.
///
/// This is the main method for accessing asset data from an [Assets] collection. If you need
/// mutable access to the asset, use [get_mut](Assets::get_mut).
pub fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&T> {
self.assets.get(&handle.into())
}
/// Checks if an asset exists for the given handle
pub fn contains<H: Into<HandleId>>(&self, handle: H) -> bool {
self.assets.contains_key(&handle.into())
}
/// Get mutable access to the asset for the given handle.
///
/// This is the main method for mutably accessing asset data from an [Assets] collection. If you
/// do not need mutable access to the asset, you may also use [get](Assets::get).
pub fn get_mut<H: Into<HandleId>>(&mut self, handle: H) -> Option<&mut T> {
let id: HandleId = handle.into();
self.events.send(AssetEvent::Modified {
@ -105,10 +146,15 @@ impl<T: Asset> Assets<T> {
self.assets.get_mut(&id)
}
/// Gets a _Strong_ handle pointing to the same asset as the given one
pub fn get_handle<H: Into<HandleId>>(&self, handle: H) -> Handle<T> {
Handle::strong(handle.into(), self.ref_change_sender.clone())
}
/// Get mutable access to an asset for the given handle, inserting a new value if none exists.
///
/// # Events
/// * [`AssetEvent::Created`]: Sent if the asset did not yet exist with the given handle
pub fn get_or_insert_with<H: Into<HandleId>>(
&mut self,
handle: H,
@ -129,10 +175,12 @@ impl<T: Asset> Assets<T> {
borrowed
}
/// Get an iterator over all assets in the collection.
pub fn iter(&self) -> impl Iterator<Item = (HandleId, &T)> {
self.assets.iter().map(|(k, v)| (*k, v))
}
/// Get a mutable iterator over all assets in the collection.
pub fn iter_mut(&mut self) -> impl Iterator<Item = (HandleId, &mut T)> {
for id in self.assets.keys() {
self.events.send(AssetEvent::Modified {
@ -142,10 +190,17 @@ impl<T: Asset> Assets<T> {
self.assets.iter_mut().map(|(k, v)| (*k, v))
}
/// Get an iterator over all HandleIds in the collection.
pub fn ids(&self) -> impl Iterator<Item = HandleId> + '_ {
self.assets.keys().cloned()
}
/// Remove an asset for the given handle.
///
/// The asset is returned if it existed in the collection, otherwise `None`.
///
/// # Events
/// * [`AssetEvent::Removed`]
pub fn remove<H: Into<HandleId>>(&mut self, handle: H) -> Option<T> {
let id: HandleId = handle.into();
let asset = self.assets.remove(&id);
@ -190,10 +245,12 @@ impl<T: Asset> Assets<T> {
}
}
/// Gets the number of assets in the collection
pub fn len(&self) -> usize {
self.assets.len()
}
/// Returns true if there are no stored assets
pub fn is_empty(&self) -> bool {
self.assets.is_empty()
}

View File

@ -69,12 +69,38 @@ impl HandleId {
///
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
/// collection.
///
/// # Accessing the Asset
///
/// A handle is _not_ the asset itself, but should be seen as a pointer to the asset. Modifying a
/// handle's `id` only modifies which asset is being pointed to. To get the actual asset, try using
/// [`Assets::get`](crate::Assets::get) or [`Assets::get_mut`](crate::Assets::get_mut).
///
/// # Strong and Weak
///
/// A handle can be either "Strong" or "Weak". Simply put: Strong handles keep the asset loaded,
/// while Weak handles do not affect the loaded status of assets. This is due to a type of
/// _reference counting_. When the number of Strong handles that exist for any given asset reach
/// zero, the asset is dropped and becomes unloaded. In some cases, you might want a reference to an
/// asset but don't want to take the responsibility of keeping it loaded that comes with a Strong handle.
/// This is where a Weak handle can be very useful.
///
/// For example, imagine you have a `Sprite` component and a `Collider` component. The `Collider` uses
/// the `Sprite`'s image size to check for collisions. It does so by keeping a Weak copy of the
/// `Sprite`'s Strong handle to the image asset.
///
/// If the `Sprite` is removed, its Strong handle to the image is dropped with it. And since it was the
/// only Strong handle for that asset, the asset is unloaded. Our `Collider` component still has a Weak
/// handle to the unloaded asset, but it will not be able to retrieve the image data, resulting in
/// collisions no longer being detected for that entity.
///
#[derive(Component, Reflect, FromReflect)]
#[reflect(Component)]
pub struct Handle<T>
where
T: Asset,
{
/// The ID of the asset as contained within its respective [Assets](crate::Assets) collection
pub id: HandleId,
#[reflect(ignore)]
handle_type: HandleType,
@ -123,6 +149,7 @@ impl<T: Asset> Handle<T> {
}
}
/// Get a copy of this handle as a Weak handle
pub fn as_weak<U: Asset>(&self) -> Handle<U> {
Handle {
id: self.id,
@ -139,6 +166,9 @@ impl<T: Asset> Handle<T> {
matches!(self.handle_type, HandleType::Strong(_))
}
/// Makes this handle Strong if it wasn't already.
///
/// This method requires the corresponding [Assets](crate::Assets) collection
pub fn make_strong(&mut self, assets: &mut Assets<T>) {
if self.is_strong() {
return;
@ -266,6 +296,8 @@ impl<T: Asset> Clone for Handle<T> {
///
/// This allows handles to be mingled in a cross asset context. For example, storing `Handle<A>` and
/// `Handle<B>` in the same `HashSet<HandleUntyped>`.
///
/// To convert back to a typed handle, use the [typed](HandleUntyped::typed) method.
#[derive(Debug)]
pub struct HandleUntyped {
pub id: HandleId,
@ -307,6 +339,9 @@ impl HandleUntyped {
matches!(self.handle_type, HandleType::Strong(_))
}
/// Convert this handle into a typed [Handle].
///
/// The new handle will maintain the Strong or Weak status of the current handle.
pub fn typed<T: Asset>(mut self) -> Handle<T> {
if let HandleId::Id(type_uuid, _) = self.id {
if T::TYPE_UUID != type_uuid {