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

Implement Bundle for every component and simplify API #2974

Closed
alice-i-cecile opened this issue Oct 15, 2021 · 9 comments
Closed

Implement Bundle for every component and simplify API #2974

alice-i-cecile opened this issue Oct 15, 2021 · 9 comments
Labels
A-ECS Entities, components, systems, and events C-Usability A simple quality-of-life change that makes Bevy easier to use

Comments

@alice-i-cecile
Copy link
Member

alice-i-cecile commented Oct 15, 2021

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

The distinction between components and bundles is often lost on new users, and we end up with a lot of redundant, nearly-duplicated APIs.

What solution would you like?

Implement Bundle for every Component, representing the one element bundle containing only that component.

Remove all duplicated API surfaces, keeping the Component names but accepting bundles:

  • insert and insert_bundle -> insert
  • remove and remove_bundle -> remove
  • remove spawn_bundle; there's now less boilerplate and the design of this routinely confuses the mental model for new users

There are some additional benefits:

  • we can remove the #[bundle] annotation in the #[derive(Bundle)] crate
  • spawn_batch becomes much easier to work with for simple 0 and 1 component cases
  • users cannot accidentally derive component on a standard bundle, as conflicting impls will exist

What alternative(s) have you considered?

Just leave it as is.

Additional context

Design credit to @BoxyUwU, @TheRawMeatball and @Frizi. Originally described here: https://hackmd.io/EvBrYLQeQbCjE2eycBlo3w

Made possible by #2254!

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A simple quality-of-life change that makes Bevy easier to use labels Oct 15, 2021
@cart
Copy link
Member

cart commented Feb 3, 2022

I don't think we should move forward on this until we resolve the "how do Bundles relate to scenes and editor experiences" conversation (ex: #3833 (comment) and the various discussions we've had on discord). Bundles might be promoted to "metadata that exists on entities" and might expand to include "prefab-like" contexts.

I'm also personally against this because it further blurs the lines between components and bundles, which our users constantly mess up in their mental models and in their code. #[derive(Component)] has helped in that regard.

Pulling in @bevyengine/ecs-team as this is pretty fundamental stuff.

@alice-i-cecile
Copy link
Member Author

alice-i-cecile commented Feb 4, 2022

I'm also personally against this because it further blurs the lines between components and bundles, which our users constantly mess up in their mental models and in their code

I agree with this: but I actually think that this change will help.

Fundamentally, this user confusion is driven by the fact that bundles are treated as a distinct concept by our APIs: particularly spawn_bundle and insert_bundle.

I think we should:

  1. Go forward with this change.
  2. Change the name of the Bundle trait to ComponentBundle and use "bundle of components" in our docs.
  3. Eliminate insert_bundle and spawn_bundle (plus any minor uses, like remove_bundle).

The type signature of .insert() becomes

fn insert(&mut EntityCommands, components: impl ComponentBundle) -> &mut EntityCommands

This removes the concept of "bundles" from our API calls, and reinforces that #[derive(ComponentBundle)] results in a bundle of components, rather than this nebulous disconnected concept.

Bundles might be promoted to "metadata that exists on entities" and might expand to include "prefab-like" contexts.

IMO if this exists, it should not use Bundles: it should be a separate concept that stores a bundle internally. A "collection of components" is a foundational idea: there's no need to overload it when we can just wrap it.

@alice-i-cecile
Copy link
Member Author

alice-i-cecile commented Feb 6, 2022

Bundles might be promoted to "metadata that exists on entities" and might expand to include "prefab-like" contexts.

IMO if this exists, it should not use Bundles: it should be a separate concept that stores a bundle internally. A "collection of components" is a foundational idea: there's no need to overload it when we can just wrap it.

#3877 is a solid vision of what this would look like in practice. The needs appear to be quite divergent, and I suspect they will only diverge further as we explore more.

@DJMcNab
Copy link
Member

DJMcNab commented Feb 24, 2022

My thinking around this is that behaviour only makes sense in relation to a combinations of components on the entity.
And so Bundles are helpers to get a useful combination of components, and thus have no meaning beyond their constituent components.
This seems to me to be the only way to maintain coherent behaviour with the underlying systems operating on components.
Which encapsulates how multiple bundles of the same 'kind' (e.g. rendering: SpriteBundle, PBRBundle, camera: OrthographicCameraBundle, PerspectiveCameraBundle) don't compose.

With this view, unifying the apis which deal with Bundles and Components is natural.

@djeedai
Copy link
Contributor

djeedai commented Jul 10, 2022

All agreeing with the above from @alice-i-cecile and @DJMcNab, but I thought this feature was further along / already implemented, so let me present another use case that is blocked until this is implemented: fluent-style component construction. In bevy_tweening (but the concept is more general, applies to any library) we have an Animator component which animates another component. Although this can be added manually, a much nicer way to do this in other libraries / languages is to do something like this (pseudo-code):

my_component.move_to(Vec3(1,2,3))

For Bevy, this could be implemented with a trait:

pub trait TransformAnimation {
  fn move_to(self, dst: Vec3) -> (Transform, Animator);
}

impl TransformAnimation for Transform {
  pub fn move_to(self, dst: Vec3) -> (Transform, Animator) {
    // create the Animator
    // [...]
    (self, animator)
  }
}

commands.spawn(Transform::default().move_to(Vec3::new(1., 2., 3.));

However this is blocked by the fact the API for adding a single component is different from the one for adding 2+ components, which means users would have to change all call sites where they want to animate a component on insertion.

@urben1680

This comment was marked as resolved.

@DJMcNab
Copy link
Member

DJMcNab commented Sep 1, 2022

Which impl do you think will cause (C1, C2) to be Component? This issue only suggests adding new implementations of Bundle.

@alice-i-cecile

This comment was marked as outdated.

@urben1680
Copy link
Contributor

Oh then I misunderstood this proposal! Thanks for making this clear to me.

@bors bors bot closed this as completed in 1a2aedd Sep 20, 2022
james7132 pushed a commit to james7132/bevy that referenced this issue Oct 19, 2022
bevyengine#2975)

@BoxyUwU this is your fault. 

Also cart didn't arrive in time to tell us not to do this.

# Objective

- Fix bevyengine#2974

## Solution

- The first commit just does the actual change
- Follow up commits do steps to prove that this method works to unify as required, but this does not remove `insert_bundle`.

## Changelog

### Changed
Nested bundles now collapse automatically, and every `Component` now implements `Bundle`.
This means that you can combine bundles and components arbitrarily, for example:
```rust
// before:
.insert(A).insert_bundle(MyBBundle{..})
// after:
.insert_bundle((A, MyBBundle {..}))
```

Note that there will be a follow up PR that removes the current `insert` impl and renames `insert_bundle` to `insert`.

### Removed
The `bundle` attribute in `derive(Bundle)`.

## Migration guide

In `derive(Bundle)`, the `bundle` attribute has been removed. Nested bundles are not collapsed automatically. You should remove `#[bundle]` attributes.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
james7132 pushed a commit to james7132/bevy that referenced this issue Oct 28, 2022
bevyengine#2975)

@BoxyUwU this is your fault. 

Also cart didn't arrive in time to tell us not to do this.

# Objective

- Fix bevyengine#2974

## Solution

- The first commit just does the actual change
- Follow up commits do steps to prove that this method works to unify as required, but this does not remove `insert_bundle`.

## Changelog

### Changed
Nested bundles now collapse automatically, and every `Component` now implements `Bundle`.
This means that you can combine bundles and components arbitrarily, for example:
```rust
// before:
.insert(A).insert_bundle(MyBBundle{..})
// after:
.insert_bundle((A, MyBBundle {..}))
```

Note that there will be a follow up PR that removes the current `insert` impl and renames `insert_bundle` to `insert`.

### Removed
The `bundle` attribute in `derive(Bundle)`.

## Migration guide

In `derive(Bundle)`, the `bundle` attribute has been removed. Nested bundles are not collapsed automatically. You should remove `#[bundle]` attributes.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
ItsDoot pushed a commit to ItsDoot/bevy that referenced this issue Feb 1, 2023
bevyengine#2975)

@BoxyUwU this is your fault. 

Also cart didn't arrive in time to tell us not to do this.

# Objective

- Fix bevyengine#2974

## Solution

- The first commit just does the actual change
- Follow up commits do steps to prove that this method works to unify as required, but this does not remove `insert_bundle`.

## Changelog

### Changed
Nested bundles now collapse automatically, and every `Component` now implements `Bundle`.
This means that you can combine bundles and components arbitrarily, for example:
```rust
// before:
.insert(A).insert_bundle(MyBBundle{..})
// after:
.insert_bundle((A, MyBBundle {..}))
```

Note that there will be a follow up PR that removes the current `insert` impl and renames `insert_bundle` to `insert`.

### Removed
The `bundle` attribute in `derive(Bundle)`.

## Migration guide

In `derive(Bundle)`, the `bundle` attribute has been removed. Nested bundles are not collapsed automatically. You should remove `#[bundle]` attributes.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
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 C-Usability A simple quality-of-life change that makes Bevy easier to use
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants