Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e016df0
Added path resolve methods to `World` and use them in `EntityTemplate`
Freyja-moth Apr 28, 2026
d5574ba
Remove old doc notes
Freyja-moth Apr 28, 2026
6da3ba4
Merge branch 'main' into bsn_path_resolution
Freyja-moth Apr 28, 2026
a0f72ee
Finally fix naming merge issues
Freyja-moth Apr 28, 2026
c23f117
Spelling error as suggested by ci
Freyja-moth Apr 28, 2026
6ba9274
Fixed doc generic issues
Freyja-moth Apr 28, 2026
49c3b8a
Even more doc issues
Freyja-moth Apr 28, 2026
65c05a1
Changed methods to remove generic `C`
Freyja-moth Apr 28, 2026
92dff77
Update docs
Freyja-moth Apr 28, 2026
ce7192e
Update template method to remove generic
Freyja-moth Apr 28, 2026
e3e8edb
Remove unneeded generics
Freyja-moth Apr 28, 2026
5942266
Added release notes
Freyja-moth Apr 28, 2026
8a52fcb
Cleaned up docs and added note about ambiuous / usage
Freyja-moth Apr 28, 2026
40e06fc
Formatting
Freyja-moth Apr 28, 2026
24ec7d6
Even more doc issues
Freyja-moth Apr 28, 2026
e8ac3d1
Final round for formatting, please
Freyja-moth Apr 28, 2026
4319a8e
Please ci I am begging you I am tired
Freyja-moth Apr 28, 2026
57a920a
Added language to code block
Freyja-moth Apr 28, 2026
32449ab
Pedantic stuff
Freyja-moth Apr 28, 2026
45cb793
Even more formatting
Freyja-moth Apr 28, 2026
83b42b3
Final doc cleanup and test making
Freyja-moth Apr 29, 2026
88912ed
Spelling errors and formatting
Freyja-moth Apr 29, 2026
969bf02
Yet more test issues
Freyja-moth Apr 29, 2026
d44b6cc
Wrong vice
alice-i-cecile Apr 29, 2026
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
37 changes: 37 additions & 0 deletions _release-content/release-notes/entity_path_resolving.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: Feature name
authors: ["@Freyja-moth"]
pull_requests: [24018]
---

Paths can now be resolved to an entity and vice versa.
These use the `Name` component, following the `ChildOf` relationship.

```rust
fn character(world: &mut World, player: Entity) -> Result {
let sword = world.get_entity_from_path("Items/Weapons/Sword", None)?;

world.entity_mut(player)
.add_one_related::<ItemOf>(sword);

// Relative paths can also be resolved
let left_arm = world.get_entity_from_path("Arms/Left", Some(player))?;

// You can even use custom relationships.
let apple = world.get_entity_from_relationship_path::<ItemOf>("Player/Satchel/Apple", None)?;

Ok(())
}
```

With this a new variant has been added to `EntityTemplate` so that paths can be resolved in scenes

```rust
fn player() -> impl Scene {
bsn! {
#Player
Player
Wielding("Items/Weapons/Sword")
}
}
```
18 changes: 17 additions & 1 deletion crates/bevy_ecs/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
resource::Resource,
world::{EntityWorldMut, Mut, World},
};
use alloc::{vec, vec::Vec};
use alloc::{string::String, vec, vec::Vec};
use variadics_please::all_tuples;

/// A [`Template`] is something that, given a spawn context (target [`Entity`], [`World`], etc), can produce a [`Template::Output`].
Expand Down Expand Up @@ -405,6 +405,8 @@ pub trait SpecializeFromTemplate: Sized {}
pub enum EntityTemplate {
/// A reference to a specific [`Entity`]
Entity(Entity),
/// A path to a specific [`Entity`]. Resolved via [`World::get_entity_from_path::<ChildOf, Name>`]
EntityPath(String),
Comment thread
Freyja-moth marked this conversation as resolved.
/// A reference to an entity via a [`ScopedEntityIndex`]
ScopedEntityIndex(ScopedEntityIndex),
}
Expand Down Expand Up @@ -435,12 +437,25 @@ impl From<Entity> for EntityTemplate {
}
}

impl From<String> for EntityTemplate {
fn from(path: String) -> Self {
Self::EntityPath(path)
}
}

impl From<&'static str> for EntityTemplate {
fn from(path: &'static str) -> Self {
Self::EntityPath(path.into())
}
}

impl Template for EntityTemplate {
type Output = Entity;

fn build_template(&self, context: &mut TemplateContext) -> Result<Self::Output> {
Ok(match self {
Self::Entity(entity) => *entity,
Self::EntityPath(path) => context.entity.world().get_entity_from_path(path, None)?,
Self::ScopedEntityIndex(scoped_entity_index) => {
context.get_scoped_entity(*scoped_entity_index)
}
Expand All @@ -450,6 +465,7 @@ impl Template for EntityTemplate {
fn clone_template(&self) -> Self {
match self {
Self::Entity(entity) => Self::Entity(*entity),
Self::EntityPath(path) => Self::EntityPath(path.clone()),
Self::ScopedEntityIndex(scoped_entity_index) => {
Self::ScopedEntityIndex(*scoped_entity_index)
}
Expand Down
22 changes: 21 additions & 1 deletion crates/bevy_ecs/src/world/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Contains error types returned by bevy's schedule.

use alloc::vec::Vec;
use alloc::{string::String, vec::Vec};
use bevy_utils::prelude::DebugName;

use crate::{
Expand Down Expand Up @@ -75,6 +75,26 @@ pub enum ResourceFetchError {
NoResourceAccess(ComponentId),
}

/// An error returned when a path cannot be resolved.
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum EntityPathError {
/// Path is empty
#[error("Entity path is empty")]
EmptyPath,
/// Root entity is not an ancestor of the starting entity
#[error("{0} is not an ancestor of {1}")]
RootIsNotAncestor(Entity, Entity),
/// One of the entities along the path does not contain a `Name` component
#[error("{0}, or one of it's ancestors does not contain a `Name` component")]
BrokenPath(Entity),
/// No paths match the given path
#[error("{0} does not match any entity")]
NoMatchingPath(String),
/// More than one paths match the given path
#[error("{0} matches multiple entities")]
AmbiguousPath(String),
}

#[cfg(test)]
mod tests {
use crate::{
Expand Down
Loading