Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
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 to 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!).

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](#immutable-components) feature (relationship components are immutable), this ensures data integrity is maintained no matter what developers do!

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: In the current system, relationship components "fragment" ECS archetypes based on their _type_, just like a normal component (Ex: `(Player, ChildOf(e1))`, and `(Player, ChildOf(e2))` exist in the same archetype). Fragmenting relationships would be an opt-in system that fragment archetypes based on their _value_ as well, which would result in entities with the same relationship targets being stored next to each other. This serves as an index, making querying by value faster, and making some access patterns more cache friendly.

[`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
Original file line number Diff line number Diff line change
@@ -1,4 +1,108 @@
<!-- 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(Glove);
p.spawn(Sword);
});
p.spawn(LeftHand).with_children(|p| {
p.spawn(Glove);
p.spawn(Shield);
});
});
```

We have big plans to improve Bevy's spawning experience 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.

In **Bevy 0.16** we have vastly improved the ergonomics of spawning hierarchies:

```rust
commands.spawn((
Player,
children![
(RightHand, children![Glove, Sword]),
(LeftHand, children![Glove, 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![Glove, Sword]),
(LeftHand, children![Glove, 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(Glove), Spawn(Sword))),
)),
Spawn((
LeftHand,
Children::spawn((Spawn(Glove), 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`).
80 changes: 40 additions & 40 deletions release-content/0.16/release-notes/_release-notes.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ prs = [16372, 16668, 18532]
file_name = "16372_Add_Immutable_Component_Support.md"

[[release_notes]]
title = "Relationships (non-fragmenting, one-to-many)"
title = "Relationships"
authors = ["@cart"]
contributors = ["@alice-i-cecile"]
prs = [17398]
file_name = "17398_Relationships_nonfragmenting_onetomany.md"

[[release_notes]]
title = "Improved Spawn APIs and Bundle Effects"
title = "Improved Spawn API"
authors = ["@cart"]
contributors = ["@alice-i-cecile", "@MrGVSV", "@ecoskey"]
prs = [17521]
Expand Down Expand Up @@ -46,11 +46,11 @@ file_name = "unified_error_handling.md"
title = "Better location tracking"
authors = ["@SpecificProtagonist", "@chescock"]
contributors = [
"@alice-i-cecile",
"Freya Pines",
"@pin3-free",
"@mweatherley",
"@skimmedsquare",
"@alice-i-cecile",
"Freya Pines",
"@pin3-free",
"@mweatherley",
"@skimmedsquare",
]
prs = [15607, 16047, 16778, 17075, 17602]
file_name = "16778_Event_source_location_tracking.md"
Expand Down Expand Up @@ -229,40 +229,40 @@ file_name = "17840_Parallel_Transform_Propagation.md"
title = "Add `no_std` support to `bevy`"
authors = ["@bushrat011899"]
contributors = [
"@alice-i-cecile",
"@BD103",
"@BenjaminBrienen",
"@hymm",
"@Jondolf",
"@MrGVSV",
"@mweatherley",
"@Victoronz",
"@alice-i-cecile",
"@BD103",
"@BenjaminBrienen",
"@hymm",
"@Jondolf",
"@MrGVSV",
"@mweatherley",
"@Victoronz",
]
prs = [
15281,
15463,
15464,
15465,
15528,
15810,
16256,
16633,
16758,
16874,
16995,
16998,
17027,
17028,
17030,
17031,
17086,
17470,
17490,
17491,
17505,
17507,
17955,
18061,
18333,
15281,
15463,
15464,
15465,
15528,
15810,
16256,
16633,
16758,
16874,
16995,
16998,
17027,
17028,
17030,
17031,
17086,
17470,
17490,
17491,
17505,
17507,
17955,
18061,
18333,
]
file_name = "17955_Add_no_std_support_to_bevy.md"