Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an abstraction like bundles that can define child entities as well #2565

Open
ricardoalcantara opened this issue Jul 29, 2021 · 12 comments
Labels
A-ECS Entities, components, systems, and events A-Hierarchy Parent-child entity hierarchies A-Scenes Serialized ECS data stored on the disk C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged
Milestone

Comments

@ricardoalcantara
Copy link

What problem does this solve or what need does it fill?

It's just a syntax sugar for better entity code organization

What solution would you like?

#[derive(Bundle)]
pub struct PlayerBundle {
    player: Player,
    movement: Movement,
    transform: Transform,
    global_transform: GlobalTransform,
    
    #[child]
    sprite: SpriteSheetBundle,
}

What alternative(s) have you considered?

I can already spawn two entities and parent them together, but I would be easier to spawn an entity and alto spawn it's children.

@ricardoalcantara ricardoalcantara added C-Feature A new feature, making something new possible S-Needs-Triage This issue needs to be labelled labels Jul 29, 2021
@TheRawMeatball TheRawMeatball added A-ECS Entities, components, systems, and events A-Scenes Serialized ECS data stored on the disk S-Needs-Design This issue requires design work to think about how it would best be accomplished and removed S-Needs-Triage This issue needs to be labelled labels Jul 29, 2021
@blaind
Copy link
Contributor

blaind commented Jul 31, 2021

I think some solution to the bundle with children -creation should be present. Ideally, it'd reduce the boilerplate code needed, and make the code cleaner.

However, some questions related to the proposed solution:

  • how would nested bundles / bundle trees be handled?
  • how would it handle cases where code needs to be executed while the bundle is being created?

I experimented with this solution a few days ago:

use bevy::{ecs::system::EntityCommands, prelude::*};
        
pub trait EntityBuilder {
    type Bundle: bevy::prelude::Bundle;
    
    fn new() -> Self;
        
    fn spawn<'a, 'b>(
        &self,
        cmd: &'b mut Commands<'a>,
        meshes: &mut Assets<Mesh>,
        materials: &mut Assets<StandardMaterial>,
    ) -> EntityCommands<'a, 'b> {
        let mut entity_cmd = cmd.spawn_bundle(self.build_bundle());
        entity_cmd.with_children(self.build_child_fn(meshes, materials));
        entity_cmd
    }

    fn build_bundle(&self) -> Self::Bundle;

    fn build_child_fn<'a>(
        &self,
        meshes: &mut Assets<Mesh>,
        materials: &mut Assets<StandardMaterial>,
    ) -> Box<dyn FnOnce(&mut ChildBuilder)>;

usage:

    let entity_id = MyBundleBuilder::new()
        .set_transform(Transform::from_xyz(2., 3., 4.)) // the struct where `EntityBuilder` is implemented for can have additional methods
        .spawn(&mut commands, &mut meshes, &mut materials)
        .id();

and then the struct (e.g. PlayerStructBuilder in above example) would implement the code for adding necessary children in the build_child_fn method.

Currently meshes & materials are hardcoded, that'll cause problems if other references are needed.

@Davier
Copy link
Contributor

Davier commented Jul 31, 2021

To build groups of entities, I make a builder struct that contains a new World.
The advantage is that the builder struct can have member functions that spawn new entities or give mutable access to any component of the already spawned entities.
When done, I transform it into a Scene and spawn it with a command.
However it's a bit involved currently because it requires all components to be properly reflected.

@mio991
Copy link

mio991 commented Oct 22, 2021

You could make it possible to give initializer functions to bundles.

On this thought is there a reason not to make bundles just functions itself?
Yes it is less convenient.

But I would love something like a Post-Initializer function.

@mockersf
Copy link
Member

@mio991 to make sure to understand what you mean, could you show an example in Rust or pseudocode of what you would want to do?

@mio991
Copy link

mio991 commented Oct 24, 2021

@mockersf I thought of something like

pub struct TextBox {
    pub node:NodeBundle,
    /* cut for brevity */
}

impl Bundle for TextBox{
    fn initialize(root: &mut ChildBuilder) {
        root.spawn_bundle(
             /* cut for brevity */
        );
    }
}

but i don't know if this is possible or even the rust way.

@mio991
Copy link

mio991 commented Oct 25, 2021

I played around a bit here is what I got:
https://github.com/mio991/bevy/tree/entity-builders

I don't want to open a pull request just know, because it doesn't currently work.

@alice-i-cecile alice-i-cecile added the A-Hierarchy Parent-child entity hierarchies label Apr 4, 2022
@SIGSTACKFAULT
Copy link
Contributor

SIGSTACKFAULT commented Oct 19, 2023

had a brain wave. placeholder names.

#[derive(Bundle)]
struct MyBundle {
    sprite: Sprite2dBundle,
    name: Name,
}
impl BundleWithChildren for MyBundle {
    fn spawn_children(&self, builder: ChildBuilder){
        builder.spawn(
            Text2dBundle {
                text: Text::from_section(self.name.clone(), TextStyle::default())
                ..default()
            },
        );
    }
}

fn spawn_whatever(mut commands: Commands){
    // might need to be like `spawn_with_children`, idk if there's some typing magic
    commands.spawn(MyBundle {...});
}

I have no idea how #[derive] works so maybe spawn_children could just be an optional method of Bundle

@SIGSTACKFAULT
Copy link
Contributor

SIGSTACKFAULT commented Oct 19, 2023

tried to implement this myself by adding a spawn_children(builder: ChildBuilder) method to bevy_ecs::Bundle but failed right at the start with a cyclic package dependency between bevy_hierarchy and bevy_ecs

@alice-i-cecile alice-i-cecile changed the title Spawn Bundle with child. Add an abstraction like bundles that can define child entities as well Nov 26, 2023
@alice-i-cecile
Copy link
Member

alice-i-cecile commented Nov 26, 2023

An abstraction like this will be required for the bsn! macro described in #9538.

Prior third-party art includes bevy_proto and moonshine-spawn.

This is related to #3877, which is a more complex version of this idea, targeted towards easy migrations.

@janhohenheim
Copy link
Member

janhohenheim commented Aug 8, 2024

Note that since we have observers and hooks now, a possible workaround is something like this:

#[derive(Bundle)]
pub struct PlayerBundle {
    player: Player,
    movement: Movement,
    transform: Transform,
    global_transform: GlobalTransform,
    sprite: BundleChild<SpriteBundle>,
}

struct BundleChild<T: Bundle>(Option<T>);

impl<T: Bundle> BundleChild<T> {
    fn new(value: T) -> Self {
        Self(Some(value))
    }
}

impl<T: Bundle> Component for BundleChild<T> {
    const STORAGE_TYPE: StorageType = StorageType::Table;

    fn register_component_hooks(hooks: &mut ComponentHooks) {
        hooks.on_add(|mut world, entity, _component_id| {
            let bundle = world.get_mut::<BundleChild<T>>(entity).unwrap().0.take();
            if let Some(bundle) = bundle {
                world.commands().entity(entity).with_children(|parent| {
                    parent.spawn(bundle);
                });
            }
        });
    }
}

@alice-i-cecile
Copy link
Member

#14437 discusses this problem, as part of an overhaul to how scenes work and how entities are spawned.

@alice-i-cecile
Copy link
Member

I've created a crate, i-cant-believe-its-not-bsn that provides a reasonably nice stopgap solution for this using WithChild and WithChildren components. Through the power of component hooks, these get transformed into children when the component is added or the entity is spawned.

This isn't a perfect solution (the requirement for unique component types really hurts), but it does a good job making Bundle a more useful abstraction for working with entity hierarchies.

@alice-i-cecile alice-i-cecile added this to the 0.16 milestone Oct 17, 2024
@alice-i-cecile alice-i-cecile added S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged and removed S-Needs-Design This issue requires design work to think about how it would best be accomplished labels Oct 17, 2024
@BenjaminBrienen BenjaminBrienen added the D-Complex Quite challenging from either a design or technical perspective. Ask for help! label Oct 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events A-Hierarchy Parent-child entity hierarchies A-Scenes Serialized ECS data stored on the disk C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged
Projects
None yet
Development

No branches or pull requests

10 participants