Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/bevy_scene/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ bevy_utils = { path = "../bevy_utils", version = "0.19.0-dev" }
thiserror = { version = "2", default-features = false }
tracing = { version = "0.1", default-features = false, features = ["std"] }
variadics_please = "1.0"

[lints]
workspace = true
53 changes: 51 additions & 2 deletions crates/bevy_scene/src/resolved_scene.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{ResolveContext, ScenePatch};
use bevy_asset::{AssetId, AssetPath, Assets, Handle, UntypedAssetId};
use crate::{ResolveContext, ResolveSceneError, Scene, SceneList, ScenePatch};
use bevy_asset::{AssetId, AssetPath, AssetServer, Assets, Handle, UntypedAssetId};
use bevy_ecs::{
bundle::{Bundle, BundleScratch, BundleWriter},
component::{Component, ComponentsRegistrator},
Expand All @@ -23,6 +23,31 @@ pub struct ResolvedSceneRoot {
}

impl ResolvedSceneRoot {
/// Resolves the current `scene` (using [`Scene::resolve`]). This should only be called after every dependency has loaded from the `scene`'s
/// [`Scene::register_dependencies`].
pub fn resolve(
scene: Box<dyn Scene>,
assets: &AssetServer,
patches: &Assets<ScenePatch>,
) -> Result<Self, ResolveSceneError> {
let mut resolved_scene = ResolvedScene::default();
let mut entity_scopes = EntityScopes::default();
scene.resolve_box(
&mut ResolveContext {
assets,
patches,
current_scope: 0,
entity_scopes: &mut entity_scopes,
inherited: None,
},
&mut resolved_scene,
)?;
Ok(ResolvedSceneRoot {
scene: resolved_scene,
entity_scopes,
})
}

/// This will spawn a new [`Entity`], then call [`ResolvedSceneRoot::apply`] on it.
/// If this fails mid-spawn, the intermediate entity will be despawned.
pub fn spawn<'w>(&self, world: &'w mut World) -> Result<EntityWorldMut<'w>, ApplySceneError> {
Expand Down Expand Up @@ -75,6 +100,30 @@ pub struct ResolvedSceneListRoot {
}

impl ResolvedSceneListRoot {
/// Resolves the current `scene_list` (using [`SceneList::resolve_list`]). This should only be
/// called after every dependency has loaded from the `scene_list`'s [`SceneList::register_dependencies`].
pub fn resolve(
scene_list: Box<dyn SceneList>,
assets: &AssetServer,
patches: &Assets<ScenePatch>,
) -> Result<Self, ResolveSceneError> {
let mut resolved_scenes = Vec::new();
let mut entity_scopes = EntityScopes::default();
scene_list.resolve_list_box(
&mut ResolveContext {
assets,
patches,
current_scope: 0,
entity_scopes: &mut entity_scopes,
inherited: None,
},
&mut resolved_scenes,
)?;
Ok(ResolvedSceneListRoot {
scenes: resolved_scenes,
entity_scopes,
})
}
/// Spawns a new [`Entity`] for each [`ResolvedScene`] in the list, and applies that [`ResolvedScene`] to them.
pub fn spawn<'w>(&self, world: &'w mut World) -> Result<Vec<Entity>, ApplySceneError> {
self.spawn_with(world, |_| {})
Expand Down
128 changes: 78 additions & 50 deletions crates/bevy_scene/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use variadics_please::all_tuples;
///
/// See [`ResolvedScene`] for more information on how it can be composed.
///
/// A [`Scene`] can have dependencies (defined with [`Scene::register_dependencies`]), which _must_ be loaded before calling [`Scene::resolve`], or it
/// A [`Scene`] can have dependencies (defined with [`Self::register_dependencies`]), which _must_ be loaded before calling [`Scene::resolve`], or it
Comment thread
cart marked this conversation as resolved.
Outdated
/// might return a [`ResolveSceneError`].
///
/// You generally don't need to resolve [`Scene`]s yourself. Instead use APIs like [`World::spawn_scene`] or [`World::queue_spawn_scene`]
Expand All @@ -47,26 +47,78 @@ use variadics_please::all_tuples;
/// [`World::queue_spawn_scene`]: crate::WorldSceneExt::queue_spawn_scene
/// [`Entity`]: bevy_ecs::entity::Entity
/// [`Component`]: bevy_ecs::component::Component
pub trait Scene: Send + Sync + 'static {
pub trait Scene: SceneBox {
/// This will apply the changes described in this [`Scene`] to the given [`ResolvedScene`]. This should not be called until all of the dependencies
/// in [`Scene::register_dependencies`] have been loaded. The scene system will generally call this method on behalf of developers.
///
/// [`Scene`]s are free to modify [`ResolvedScene`] in arbitrary ways. In the context of related entities, in general they should just be pushing new
/// entities to the back of the list.
fn resolve(
&self,
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError>;

/// [`Scene`] can have [`Asset`] dependencies, which _must_ be loaded before calling [`Scene::resolve`] or it might return a [`ResolveSceneError`]!
/// [`Scene`] can have [`Asset`] dependencies, which _must_ be loaded before calling [`Scene::resolve`] / [`SceneList::resolve_list`] or it might return a [`ResolveSceneError`]!
///
/// In most cases, the scene system will ensure [`Scene::resolve`] is called _after_ these dependencies have been loaded.
/// In most cases, the scene system will ensure [`Scene::resolve`] / [`SceneList::resolve_list`] is called _after_ these dependencies have been loaded.
///
/// [`Asset`]: bevy_asset::Asset
fn register_dependencies(&self, _dependencies: &mut SceneDependencies) {}
}

/// Boxed version of [`Scene`], which enables implementing [`Scene`] for [`Box<dyn Scene>`]. Most
/// developers do not need to think about or use this trait.
///
/// ## Why does this exist?
Comment thread
cart marked this conversation as resolved.
///
/// [`Scene::resolve`] consumes `self`, which by default is not something that [`Box<dyn Scene>`]
/// can do in Rust, as `dyn Scene` is "unsized". The "way out" is to have every [`Scene`] type
/// _also_ know how to resolve itself for `self: Box<Self>`. [`SceneBox`] has a blanket impl
/// for `Scene + Sized` (which can just rely on the [`Scene`] impl). Then [`Box<dyn Scene>`] has a
/// manual [`SceneBox`] impl that relies on the _stored_ [`SceneBox::resolve_box`] impl.
pub trait SceneBox: Send + Sync + 'static {
Comment thread
cart marked this conversation as resolved.
/// See [`Scene::resolve`].
fn resolve_box(
self: Box<Self>,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError>;

/// See [`Scene::register_dependencies`].
fn register_dependencies_box(&self, _dependencies: &mut SceneDependencies);
}

impl<S: Scene + Sized> SceneBox for S {
#[inline]
fn resolve_box(
self: Box<Self>,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
(*self).resolve(context, scene)
}

#[inline]
fn register_dependencies_box(&self, dependencies: &mut SceneDependencies) {
self.register_dependencies(dependencies);
}
}

impl<T: ?Sized + SceneBox> Scene for Box<T> {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
self.resolve_box(context, scene)
}

fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
(**self).register_dependencies_box(dependencies);
}
}

/// A collection of asset dependencies required by a [`Scene`].
#[derive(Default)]
pub struct SceneDependencies(Vec<SceneDependency>);
Expand Down Expand Up @@ -146,7 +198,7 @@ impl<'a> ResolveContext<'a> {
macro_rules! scene_impl {
($($patch: ident),*) => {
impl<$($patch: Scene),*> Scene for ($($patch,)*) {
fn resolve(&self, _context: &mut ResolveContext, _scene: &mut ResolvedScene) -> Result<(), ResolveSceneError> {
fn resolve(self, _context: &mut ResolveContext, _scene: &mut ResolvedScene) -> Result<(), ResolveSceneError> {
#[expect(
clippy::allow_attributes,
reason = "This is inside a macro, and as such, may not trigger in all cases."
Expand Down Expand Up @@ -178,33 +230,6 @@ macro_rules! scene_impl {

all_tuples!(scene_impl, 0, 12, P);

impl Scene for Box<dyn Scene> {
fn resolve(
&self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
(**self).resolve(context, scene)
}
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
(**self).register_dependencies(dependencies);
}
}

impl SceneList for Box<dyn SceneList> {
fn resolve_list(
&self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError> {
(**self).resolve_list(context, scenes)
}

fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
(**self).register_dependencies(dependencies);
}
}

/// A [`Scene`] that patches a [`Template`] of type `T` with a given function `F`.
///
/// Functionally, a [`TemplatePatch`] scene will initialize a [`Default`] value of the patched
Expand All @@ -231,16 +256,16 @@ impl SceneList for Box<dyn SceneList> {
/// let position = Position { x: 0, y: 0};
/// // applying patch to position would result in { x: 10, y: 0 }
/// ```
pub struct TemplatePatch<F: Fn(&mut T, &mut ResolveContext), T>(pub F, pub PhantomData<T>);
pub struct TemplatePatch<F: FnOnce(&mut T, &mut ResolveContext), T>(pub F, pub PhantomData<T>);

/// Returns a [`Scene`] that completely overwrites the current value of a [`Template`] `T` with the given `value`.
/// The `value` is cloned each time the [`Template`] is built.
pub fn template_value<T: Template + Clone>(
pub fn template_value<T: Template>(
value: T,
) -> TemplatePatch<impl Fn(&mut T, &mut ResolveContext), T> {
) -> TemplatePatch<impl FnOnce(&mut T, &mut ResolveContext), T> {
TemplatePatch(
move |input: &mut T, _context: &mut ResolveContext| {
*input = value.clone();
*input = value;
},
PhantomData,
)
Expand All @@ -253,14 +278,14 @@ pub trait PatchFromTemplate {
type Template;

/// Takes a "patch function" `func`, and turns it into a [`TemplatePatch`].
fn patch<F: Fn(&mut Self::Template, &mut ResolveContext)>(
fn patch<F: FnOnce(&mut Self::Template, &mut ResolveContext)>(
func: F,
) -> TemplatePatch<F, Self::Template>;
}

impl<G: FromTemplate> PatchFromTemplate for G {
type Template = G::Template;
fn patch<F: Fn(&mut Self::Template, &mut ResolveContext)>(
fn patch<F: FnOnce(&mut Self::Template, &mut ResolveContext)>(
func: F,
) -> TemplatePatch<F, Self::Template> {
TemplatePatch(func, PhantomData)
Expand All @@ -270,22 +295,25 @@ impl<G: FromTemplate> PatchFromTemplate for G {
/// A helper function that returns a [`TemplatePatch`] [`Scene`] for something that implements [`Template`].
pub trait PatchTemplate: Sized {
/// Takes a "patch function" `func` that patches this [`Template`], and turns it into a [`TemplatePatch`].
fn patch_template<F: Fn(&mut Self, &mut ResolveContext)>(func: F) -> TemplatePatch<F, Self>;
fn patch_template<F: FnOnce(&mut Self, &mut ResolveContext)>(func: F)
-> TemplatePatch<F, Self>;
}

impl<T: Template> PatchTemplate for T {
fn patch_template<F: Fn(&mut Self, &mut ResolveContext)>(func: F) -> TemplatePatch<F, Self> {
fn patch_template<F: FnOnce(&mut Self, &mut ResolveContext)>(
func: F,
) -> TemplatePatch<F, Self> {
TemplatePatch(func, PhantomData)
}
}

impl<
F: Fn(&mut T, &mut ResolveContext) + Send + Sync + 'static,
F: FnOnce(&mut T, &mut ResolveContext) + Send + Sync + 'static,
T: Template<Output: Component> + Send + Sync + Default + 'static,
> Scene for TemplatePatch<F, T>
{
fn resolve(
&self,
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand Down Expand Up @@ -317,7 +345,7 @@ impl<R: Relationship, L: SceneList> RelatedScenes<R, L> {

impl<R: Relationship, L: SceneList> Scene for RelatedScenes<R, L> {
fn resolve(
&self,
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand Down Expand Up @@ -352,7 +380,7 @@ impl<I: Into<AssetPath<'static>>> From<I> for InheritSceneAsset {

impl Scene for InheritSceneAsset {
fn resolve(
&self,
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand All @@ -376,7 +404,7 @@ impl<F: (Fn(&mut TemplateContext) -> Result<O>) + Clone + Send + Sync + 'static,
for FnTemplate<F, O>
{
fn resolve(
&self,
self,
_context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand All @@ -402,7 +430,7 @@ pub struct NameEntityReference {

impl Scene for NameEntityReference {
fn resolve(
&self,
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand All @@ -429,7 +457,7 @@ pub struct SceneScope<S: Scene>(pub S);

impl<S: Scene> Scene for SceneScope<S> {
fn resolve(
&self,
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand All @@ -447,7 +475,7 @@ pub struct SceneListScope<L: SceneList>(pub L);

impl<L: SceneList> SceneList for SceneListScope<L> {
fn resolve_list(
&self,
self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError> {
Expand Down Expand Up @@ -488,7 +516,7 @@ impl<
> Scene for OnTemplate<I, E, B, M>
{
fn resolve(
&self,
self,
_context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand Down
Loading