diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 6e5b488ee0..3e6dfe39ab 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -422,6 +422,12 @@ impl Assets { } } + #[inline] + /// Retrieves many references to the [`Asset`] with the given `id`s, if they exists. + pub fn get_many(&self, ids: [AssetId; N]) -> [Option<&A>; N] { + ids.map(|id| self.get(id)) + } + /// Retrieves a mutable reference to the [`Asset`] with the given `id`, if it exists. /// Note that this supports anything that implements `Into>`, which includes [`Handle`] and [`AssetId`]. #[inline] @@ -437,6 +443,36 @@ impl Assets { result } + #[inline] + #[expect(unsafe_code, reason = "Required to get several mutable references")] + /// Retrieves may mutable references to the [`Asset`]s with the given `id`s, if they exists. + /// Will return `None` if any `id`s alias. + pub fn get_many_mut( + &mut self, + ids: [AssetId; N], + ) -> Option<[Option<&mut A>; N]> { + use core::mem::MaybeUninit; + + // SAFETY: Verify that all indices are unique + for i in 0..N { + for j in 0..i { + if ids[i] == ids[j] { + return None; + } + } + } + + let mut values = [(); N].map(|_| MaybeUninit::uninit()); + for (value, asset) in core::iter::zip(&mut values, ids) { + let asset = self.get_mut(asset).map(|asset| core::ptr::from_mut(asset)); + *value = MaybeUninit::new(asset); + } + + // SAFETY: Each value has been fully initialized. + let values = values.map(|x| unsafe { x.assume_init().map(|raw| &mut *raw) }); + Some(values) + } + /// Retrieves a mutable reference to the [`Asset`] with the given `id`, if it exists. /// /// This is the same as [`Assets::get_mut`] except it doesn't emit [`AssetEvent::Modified`]. @@ -661,6 +697,8 @@ pub struct InvalidGenerationError { #[cfg(test)] mod test { + use crate::prelude::*; + use crate::tests::{SubText, TestAsset}; use crate::AssetIndex; #[test] @@ -672,4 +710,28 @@ mod test { let roundtripped = AssetIndex::from_bits(asset_index.to_bits()); assert_eq!(asset_index, roundtripped); } + + #[test] + fn get_many_mut_no_aliases() { + let mut assets: Assets = Assets::default(); + + let one = "One"; + let two = "Two"; + + let v = assets.add(SubText { text: one.into() }).id(); + let w = assets.add(SubText { text: two.into() }).id(); + + let [x, y] = assets.get_many_mut([v, w]).unwrap(); + assert_eq!(x.unwrap().text, one); + assert_eq!(y.unwrap().text, two); + } + + #[test] + fn get_many_mut_aliases() { + let mut assets: Assets = Assets::default(); + + let id = assets.add(TestAsset).id(); + let result = assets.get_many_mut([id, id]); + assert!(result.is_none()); + } } diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 8186b6315d..d44a0cedbe 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -712,7 +712,7 @@ mod tests { #[derive(Asset, TypePath, Debug)] pub struct SubText { - text: String, + pub text: String, } #[derive(Serialize, Deserialize)]