Skip to content

Commit b3fa479

Browse files
committed
Add ability to inspect entity's components (#5136)
# Objective - Provide a way to see the components of an entity. - Fixes #1467 ## Solution - Add `World::inspect_entity`. It accepts an `Entity` and returns a vector of `&ComponentInfo` that the entity has. - Add `EntityCommands::log_components`. It logs the component names of the entity. (info level) --- ## Changelog ### Added - Ability to inspect components of an entity through `World::inspect_entity` or `EntityCommands::log_components`
1 parent 5f8e438 commit b3fa479

File tree

2 files changed

+113
-3
lines changed

2 files changed

+113
-3
lines changed

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
entity::{Entities, Entity},
88
world::{FromWorld, World},
99
};
10-
use bevy_utils::tracing::{error, warn};
10+
use bevy_utils::tracing::{error, info, warn};
1111
pub use command_queue::CommandQueue;
1212
pub use parallel_scope::*;
1313
use std::marker::PhantomData;
@@ -588,6 +588,13 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
588588
});
589589
}
590590

591+
/// Logs the components of the entity at the info level.
592+
pub fn log_components(&mut self) {
593+
self.commands.add(LogComponents {
594+
entity: self.entity,
595+
});
596+
}
597+
591598
/// Returns the underlying [`Commands`].
592599
pub fn commands(&mut self) -> &mut Commands<'w, 's> {
593600
self.commands
@@ -793,6 +800,22 @@ impl<R: Resource> Command for RemoveResource<R> {
793800
}
794801
}
795802

803+
/// [`Command`] to log the components of a given entity. See [`EntityCommands::log_components`].
804+
pub struct LogComponents {
805+
entity: Entity,
806+
}
807+
808+
impl Command for LogComponents {
809+
fn write(self, world: &mut World) {
810+
let debug_infos: Vec<_> = world
811+
.inspect_entity(self.entity)
812+
.into_iter()
813+
.map(|component_info| component_info.name())
814+
.collect();
815+
info!("Entity {:?}: {:?}", self.entity, debug_infos);
816+
}
817+
}
818+
796819
#[cfg(test)]
797820
#[allow(clippy::float_cmp, clippy::approx_constant)]
798821
mod tests {

crates/bevy_ecs/src/world/mod.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use crate::{
1212
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
1313
change_detection::{MutUntyped, Ticks},
1414
component::{
15-
Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, StorageType,
15+
Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components,
16+
StorageType,
1617
},
1718
entity::{AllocAtWithoutReplacement, Entities, Entity},
1819
query::{QueryState, WorldQuery},
@@ -280,6 +281,30 @@ impl World {
280281
.unwrap_or_else(|| panic!("Entity {:?} does not exist", entity))
281282
}
282283

284+
/// Returns the components of an [`Entity`](crate::entity::Entity) through [`ComponentInfo`](crate::component::ComponentInfo).
285+
#[inline]
286+
pub fn inspect_entity(&self, entity: Entity) -> Vec<&ComponentInfo> {
287+
let entity_location = self
288+
.entities()
289+
.get(entity)
290+
.unwrap_or_else(|| panic!("Entity {:?} does not exist", entity));
291+
292+
let archetype = self
293+
.archetypes()
294+
.get(entity_location.archetype_id)
295+
.unwrap_or_else(|| {
296+
panic!(
297+
"Archetype {:?} does not exist",
298+
entity_location.archetype_id
299+
)
300+
});
301+
302+
archetype
303+
.components()
304+
.filter_map(|id| self.components().get_info(id))
305+
.collect()
306+
}
307+
283308
/// Returns an [`EntityMut`] for the given `entity` (if it exists) or spawns one if it doesn't exist.
284309
/// This will return [`None`] if the `entity` exists with a different generation.
285310
///
@@ -1542,11 +1567,13 @@ mod tests {
15421567
use super::World;
15431568
use crate::{
15441569
change_detection::DetectChanges,
1545-
component::{ComponentDescriptor, ComponentId, StorageType},
1570+
component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType},
15461571
ptr::OwningPtr,
15471572
};
15481573
use bevy_ecs_macros::Component;
1574+
use bevy_utils::HashSet;
15491575
use std::{
1576+
any::TypeId,
15501577
panic,
15511578
sync::{
15521579
atomic::{AtomicBool, AtomicU32, Ordering},
@@ -1762,4 +1789,64 @@ mod tests {
17621789
world.insert_resource_by_id(invalid_component_id, ptr);
17631790
});
17641791
}
1792+
1793+
#[derive(Component)]
1794+
struct Foo;
1795+
1796+
#[derive(Component)]
1797+
struct Bar;
1798+
1799+
#[derive(Component)]
1800+
struct Baz;
1801+
1802+
#[test]
1803+
fn inspect_entity_components() {
1804+
let mut world = World::new();
1805+
let ent0 = world.spawn().insert_bundle((Foo, Bar, Baz)).id();
1806+
let ent1 = world.spawn().insert_bundle((Foo, Bar)).id();
1807+
let ent2 = world.spawn().insert_bundle((Bar, Baz)).id();
1808+
let ent3 = world.spawn().insert_bundle((Foo, Baz)).id();
1809+
let ent4 = world.spawn().insert_bundle((Foo,)).id();
1810+
let ent5 = world.spawn().insert_bundle((Bar,)).id();
1811+
let ent6 = world.spawn().insert_bundle((Baz,)).id();
1812+
1813+
fn to_type_ids(component_infos: Vec<&ComponentInfo>) -> HashSet<Option<TypeId>> {
1814+
component_infos
1815+
.into_iter()
1816+
.map(|component_info| component_info.type_id())
1817+
.collect()
1818+
}
1819+
1820+
let foo_id = TypeId::of::<Foo>();
1821+
let bar_id = TypeId::of::<Bar>();
1822+
let baz_id = TypeId::of::<Baz>();
1823+
assert_eq!(
1824+
to_type_ids(world.inspect_entity(ent0)),
1825+
[Some(foo_id), Some(bar_id), Some(baz_id)].into()
1826+
);
1827+
assert_eq!(
1828+
to_type_ids(world.inspect_entity(ent1)),
1829+
[Some(foo_id), Some(bar_id)].into()
1830+
);
1831+
assert_eq!(
1832+
to_type_ids(world.inspect_entity(ent2)),
1833+
[Some(bar_id), Some(baz_id)].into()
1834+
);
1835+
assert_eq!(
1836+
to_type_ids(world.inspect_entity(ent3)),
1837+
[Some(foo_id), Some(baz_id)].into()
1838+
);
1839+
assert_eq!(
1840+
to_type_ids(world.inspect_entity(ent4)),
1841+
[Some(foo_id)].into()
1842+
);
1843+
assert_eq!(
1844+
to_type_ids(world.inspect_entity(ent5)),
1845+
[Some(bar_id)].into()
1846+
);
1847+
assert_eq!(
1848+
to_type_ids(world.inspect_entity(ent6)),
1849+
[Some(baz_id)].into()
1850+
);
1851+
}
17651852
}

0 commit comments

Comments
 (0)