Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
119 changes: 69 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,69 @@ 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>`].
Comment thread
cart marked this conversation as resolved.
Outdated
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 +189,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 +221,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 +247,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 +269,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 +286,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 +336,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 +371,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 +395,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 +421,7 @@ pub struct NameEntityReference {

impl Scene for NameEntityReference {
fn resolve(
&self,
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand All @@ -429,7 +448,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 +466,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 +507,7 @@ impl<
> Scene for OnTemplate<I, E, B, M>
{
fn resolve(
&self,
self,
_context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
Expand Down
52 changes: 47 additions & 5 deletions crates/bevy_scene/src/scene_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use variadics_please::all_tuples;
/// [`Scene`] is to [`Entity`] as [`SceneList`] is to [`Vec<Entity>`].
///
/// [`Entity`]: bevy_ecs::entity::Entity
pub trait SceneList: Send + Sync + 'static {
pub trait SceneList: SceneListBox {
/// This will apply the changes described in this [`SceneList`] to the given [`Vec<ResolvedScene>`]. This should not be called until all of
/// the dependencies in [`Scene::register_dependencies`] have been loaded.
fn resolve_list(
&self,
self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError>;
Expand All @@ -22,14 +22,56 @@ pub trait SceneList: Send + Sync + 'static {
fn register_dependencies(&self, dependencies: &mut SceneDependencies);
}

/// Boxed version of [`SceneList`], which enables implementing [`SceneList`] for [`Box<dyn SceneList>`].
Comment thread
cart marked this conversation as resolved.
pub trait SceneListBox: Send + Sync + 'static {
/// See [`SceneList::resolve_list`].
fn resolve_list_box(
self: Box<Self>,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError>;

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

impl<L: SceneList> SceneListBox for L {
#[inline]
fn resolve_list_box(
self: Box<Self>,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError> {
(*self).resolve_list(context, scenes)
}

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

impl<T: ?Sized + SceneListBox> SceneList for Box<T> {
fn resolve_list(
self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError> {
self.resolve_list_box(context, scenes)
}

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

/// Corresponds to a single member of a [`SceneList`] (an [`Entity`] with an `S` [`Scene`]).
///
/// [`Entity`]: bevy_ecs::entity::Entity
pub struct EntityScene<S>(pub S);

impl<S: Scene> SceneList for EntityScene<S> {
fn resolve_list(
&self,
self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError> {
Expand All @@ -47,7 +89,7 @@ impl<S: Scene> SceneList for EntityScene<S> {
macro_rules! scene_list_impl {
($($list: ident),*) => {
impl<$($list: SceneList),*> SceneList for ($($list,)*) {
fn resolve_list(&self, _context: &mut ResolveContext, _scenes: &mut Vec<ResolvedScene>) -> Result<(), ResolveSceneError> {
fn resolve_list(self, _context: &mut ResolveContext, _scenes: &mut Vec<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 @@ -81,7 +123,7 @@ all_tuples!(scene_list_impl, 0, 12, P);

impl<S: Scene> SceneList for Vec<S> {
fn resolve_list(
&self,
self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError> {
Expand Down
Loading