-
Notifications
You must be signed in to change notification settings - Fork 406
0.16 Release Notes: Relationships and Spawn APIs #2061
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
3f5ea44
0.16 Release Notes: Relationships and Spawn APIs
cart bf61294
Update release-content/0.16/release-notes/17398_Relationships_nonfrag…
cart cc601c9
Update release-content/0.16/release-notes/17398_Relationships_nonfrag…
cart 1683978
Use cases that can always use with_children
cart b7bae8e
Immutable components link
cart ca9a7e4
Rewrite fragmenting section
cart a618c8b
Rephrase a thing
cart fd95618
Lets try bolding
cart File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
65 changes: 62 additions & 3 deletions
65
release-content/0.16/release-notes/17398_Relationships_nonfragmenting_onetomany.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,63 @@ | ||
| <!-- Relationships (non-fragmenting, one-to-many) --> | ||
| <!-- https://github.com/bevyengine/bevy/pull/17398 --> | ||
| When building Bevy apps, it is often useful to "link" entities together. The most common case in Bevy is connecting parent and child entities together. In previous Bevy versions, a child would have a `Parent` component, which stored a reference to the parent entity, and the parent entity would have a `Children` component, which stored a list of all of its children entities. To ensure these connections remained valid, developers were not allowed to modify these components directly. Instead, all changes had to be done via specialized commands. | ||
|
|
||
| <!-- TODO --> | ||
| This worked, but it had some pretty glaring downsides: | ||
|
|
||
| 1. Maintaining hierarchies was "separate" from the core ECS data model. This made it hard to improve our spawn APIs, and made interacting with hierarchies less natural. | ||
| 2. The system was specialized and not reusable. Developers wanting to define their own relationship types had to reinvent the wheel. | ||
| 3. To ensure data integrity, expensive scans were required to avoid duplicates. | ||
|
|
||
| In **Bevy 0.16** we have added initial support for Relationships: a generalized and efficient component-driven system for linking entities together bidirectionally. This is what defining a new [`Relationship`] looks like: | ||
|
|
||
| ```rust | ||
| /// This is a "relationship" component. | ||
| /// Add it to an entity that "likes" another entity. | ||
| #[derive(Component)] | ||
| #[relationship(relationship_target = LikedBy)] | ||
| struct Likes(pub Entity); | ||
|
|
||
| /// This is the "relationship target" component. | ||
| /// It will be automatically inserted and updated to contain | ||
| /// all entities that currently "like" this entity. | ||
| #[derive(Component, Deref)] | ||
| #[relationship_target(relationship = Likes)] | ||
| struct LikedBy(Vec<Entity>); | ||
|
|
||
| // Later in your app | ||
| let e1 = world.spawn_empty().id(); | ||
| let e2 = world.spawn(Likes(e1)).id(); | ||
| let e3 = world.spawn(Likes(e1)).id(); | ||
|
|
||
| // e1 is liked by e2 and e3 | ||
| let liked_by = world.entity(e1).get::<LikedBy>().unwrap(); | ||
| assert_eq!(&**liked_by, &[e2, e3]); | ||
| ``` | ||
|
|
||
| The [`Relationship`] component is the "source of truth", and the [`RelationshipTarget`] component is updated to reflect that source of truth. This means that adding/removing relationships should always be done via the [`Relationship`] component. | ||
|
|
||
| We use this "source of truth" model instead of allowing both components to "drive" for performance reasons. Allowing writes to both sides would require expensive scanning during inserts ensure they are in sync and have no duplicates. The "relationships as the source of truth" approach allows us to make adding relationships constant-time (which is an improvement over previous Bevy versions!). | ||
cart marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
cart marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Relationships are built on top of Bevy's [Component Hooks](/news/bevy-0-14/#ecs-hooks-and-observers), which immediately and efficiently maintains the connection between the [`Relationship`] and the [`RelationshipTarget`] by plugging directly into the component add/remove/update lifecycle. In combination with the new Immutable Components feature (relationship components are immutable), this ensures data integrity is maintained no matter what developers do! | ||
cart marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Bevy's existing hierarchy system has been fully replaced by the new [`ChildOf`] [`Relationship`] and [`Children`] [`RelationshipTarget`]. Adding a child is now as simple as: | ||
|
|
||
| ```rust | ||
| commands.spawn(ChildOf(some_parent)); | ||
| ``` | ||
|
|
||
| Likewise reparenting an entity is as simple as: | ||
|
|
||
| ```rust | ||
| commands.entity(some_entity).insert(ChildOf(new_parent)); | ||
| ``` | ||
|
|
||
| We also took this chance to improve our spawn APIs more generally. Read the next section for details! | ||
|
|
||
| Note that this is just the first step for relationships. We have plans to expand their capabilities: | ||
|
|
||
| 1. Many-To-Many Relationships: The current system is one-to-many (ex: The `ChildOf` Relationship points to "one" target entity and the `RelationshipTarget` can be targeted by "many" child entities). Some relationships could benefit from supporting many relationship targets. | ||
| 2. Fragmenting Relationships: The current system does not "fragment" ECS archetypes (Ex: `(Player, ChildOf(e1))`, and `(Player, ChildOf(e2))` exist in the same archetype). In some cases, for performance or logic reasons, a relationship might want to fragment the archetype based on its _value_ rather than just its type. Fragmenting Relationships would enable these cases to opt-in to fragmenting behavior. | ||
cart marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [`Relationship`]: https://dev-docs.bevyengine.org/bevy/ecs/relationship/trait.Relationship.html | ||
| [`RelationshipTarget`]: https://dev-docs.bevyengine.org/bevy/prelude/trait.RelationshipTarget.html | ||
| [`ChildOf`]: https://dev-docs.bevyengine.org/bevy/prelude/struct.ChildOf.html | ||
| [`Children`]: https://dev-docs.bevyengine.org/bevy/ecs/hierarchy/struct.Children.html | ||
108 changes: 105 additions & 3 deletions
108
release-content/0.16/release-notes/17521_Improved_Spawn_APIs_and_Bundle_Effects.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,106 @@ | ||
| <!-- Improved Spawn APIs and Bundle Effects --> | ||
| <!-- https://github.com/bevyengine/bevy/pull/17521 --> | ||
| Spawning hierarchies in Bevy has historically been a bit cumbersome: | ||
|
|
||
| <!-- TODO --> | ||
| ```rust | ||
| commands | ||
| .spawn(Player) | ||
| .with_children(|p| { | ||
| p.spawn(RightHand).with_children(|p| { | ||
| p.spawn(Sword); | ||
| }); | ||
| p.spawn(LeftHand).with_children(|p| { | ||
| p.spawn(Shield); | ||
| }); | ||
| }); | ||
| ``` | ||
cart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| We have big plans to make spawning top-tier with our [Next Generation Scene / UI System](https://github.com/bevyengine/bevy/discussions/14437) (BSN). An important stepping stone on that path is making it possible to express hierarchies directly via data, rather than using builder methods. The addition of Relationships further increases the value of building such a system, as _all_ relationships can benefit from it. | ||
cart marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| In **Bevy 0.16** we have vastly improved the ergonomics of spawning hierarchies: | ||
|
|
||
| ```rust | ||
| commands.spawn(( | ||
| Player, | ||
| children![ | ||
| (RightHand, children![Sword]), | ||
| (LeftHand, children![Shield]), | ||
| ] | ||
| )); | ||
| ``` | ||
|
|
||
| This builds on the existing Bundle API by adding support for "bundle effects", which are applied immediately after a Bundle is inserted. Notably, this enables developers to define functions that return a hierarchy like this: | ||
|
|
||
| ```rust | ||
| fn player(name: &str) -> impl Bundle { | ||
| ( | ||
| Player, | ||
| Name::new(name), | ||
| children![ | ||
| (RightHand, children![Sword]), | ||
| (LeftHand, children![Shield]), | ||
| ] | ||
| ) | ||
| } | ||
|
|
||
| // later in your app | ||
| commands.spawn(player("Bob")); | ||
| ``` | ||
|
|
||
| In most cases the `children!` macro should be preferred for ergonomics reasons. It expands to the following API: | ||
|
|
||
| ```rust | ||
| commands.spawn(( | ||
| Player, | ||
| Children::spawn(( | ||
| Spawn(( | ||
| RightHand, | ||
| Children::spawn(Spawn(Sword)) | ||
| )), | ||
| Spawn(( | ||
| LeftHand, | ||
| Children::spawn(Spawn(Shield)) | ||
| )), | ||
| )) | ||
| )); | ||
| ``` | ||
|
|
||
| There are a number of spawn wrapper variants, which provide additional flexibility: | ||
|
|
||
| ```rust | ||
| world.spawn(( | ||
| Name::new("Root"), | ||
| Children::spawn(( | ||
| Spawn(Name::new("Child1")), | ||
| SpawnIter(["Child2", "Child3"].into_iter().map(Name::new), | ||
| SpawnWith(|parent: &mut ChildSpawner| { | ||
| parent.spawn(Name::new("Child4")); | ||
| parent.spawn(Name::new("Child5")); | ||
| }) | ||
| )), | ||
| )) | ||
| ``` | ||
|
|
||
| Notably, this API works for _all_ Relationship types. For example, you could spawn a `Likes` / `LikedBy` relationship hierarchy (as defined in the relationships section above) like this: | ||
|
|
||
| ```rust | ||
| world.spawn(( | ||
| Name::new("Monica"), | ||
| LikedBy::spawn(( | ||
| Spawn(Name::new("Naomi")), | ||
| Spawn(Name::new("Dwight")), | ||
| )) | ||
| )) | ||
| ``` | ||
|
|
||
| There is also a `related!` macro, which does the same thing as `children!`, but for any relationship type: | ||
|
|
||
| ```rust | ||
| world.spawn(( | ||
| Name::new("Monica"), | ||
| related!(LikedBy[ | ||
| Name::new("Naomi"), | ||
| Name::new("Dwight"), | ||
| ]), | ||
| )) | ||
| ``` | ||
|
|
||
| This API also allows us to optimize hierarchy construction time by cutting down on re-allocations, as we can generally (with the exception of cases like `SpawnWith`) statically determine how many related entities an entity will have and preallocate space for them in the `RelationshipTarget` component (ex: `Children`). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.