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`
This commit is contained in:
		
							parent
							
								
									5f8e43833d
								
							
						
					
					
						commit
						b3fa4790b7
					
				| @ -7,7 +7,7 @@ use crate::{ | ||||
|     entity::{Entities, Entity}, | ||||
|     world::{FromWorld, World}, | ||||
| }; | ||||
| use bevy_utils::tracing::{error, warn}; | ||||
| use bevy_utils::tracing::{error, info, warn}; | ||||
| pub use command_queue::CommandQueue; | ||||
| pub use parallel_scope::*; | ||||
| use std::marker::PhantomData; | ||||
| @ -588,6 +588,13 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /// Logs the components of the entity at the info level.
 | ||||
|     pub fn log_components(&mut self) { | ||||
|         self.commands.add(LogComponents { | ||||
|             entity: self.entity, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the underlying [`Commands`].
 | ||||
|     pub fn commands(&mut self) -> &mut Commands<'w, 's> { | ||||
|         self.commands | ||||
| @ -793,6 +800,22 @@ impl<R: Resource> Command for RemoveResource<R> { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// [`Command`] to log the components of a given entity. See [`EntityCommands::log_components`].
 | ||||
| pub struct LogComponents { | ||||
|     entity: Entity, | ||||
| } | ||||
| 
 | ||||
| impl Command for LogComponents { | ||||
|     fn write(self, world: &mut World) { | ||||
|         let debug_infos: Vec<_> = world | ||||
|             .inspect_entity(self.entity) | ||||
|             .into_iter() | ||||
|             .map(|component_info| component_info.name()) | ||||
|             .collect(); | ||||
|         info!("Entity {:?}: {:?}", self.entity, debug_infos); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| #[allow(clippy::float_cmp, clippy::approx_constant)] | ||||
| mod tests { | ||||
|  | ||||
| @ -12,7 +12,8 @@ use crate::{ | ||||
|     bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, | ||||
|     change_detection::{MutUntyped, Ticks}, | ||||
|     component::{ | ||||
|         Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, StorageType, | ||||
|         Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components, | ||||
|         StorageType, | ||||
|     }, | ||||
|     entity::{AllocAtWithoutReplacement, Entities, Entity}, | ||||
|     query::{QueryState, WorldQuery}, | ||||
| @ -280,6 +281,30 @@ impl World { | ||||
|             .unwrap_or_else(|| panic!("Entity {:?} does not exist", entity)) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the components of an [`Entity`](crate::entity::Entity) through [`ComponentInfo`](crate::component::ComponentInfo).
 | ||||
|     #[inline] | ||||
|     pub fn inspect_entity(&self, entity: Entity) -> Vec<&ComponentInfo> { | ||||
|         let entity_location = self | ||||
|             .entities() | ||||
|             .get(entity) | ||||
|             .unwrap_or_else(|| panic!("Entity {:?} does not exist", entity)); | ||||
| 
 | ||||
|         let archetype = self | ||||
|             .archetypes() | ||||
|             .get(entity_location.archetype_id) | ||||
|             .unwrap_or_else(|| { | ||||
|                 panic!( | ||||
|                     "Archetype {:?} does not exist", | ||||
|                     entity_location.archetype_id | ||||
|                 ) | ||||
|             }); | ||||
| 
 | ||||
|         archetype | ||||
|             .components() | ||||
|             .filter_map(|id| self.components().get_info(id)) | ||||
|             .collect() | ||||
|     } | ||||
| 
 | ||||
|     /// Returns an [`EntityMut`] for the given `entity` (if it exists) or spawns one if it doesn't exist.
 | ||||
|     /// This will return [`None`] if the `entity` exists with a different generation.
 | ||||
|     ///
 | ||||
| @ -1542,11 +1567,13 @@ mod tests { | ||||
|     use super::World; | ||||
|     use crate::{ | ||||
|         change_detection::DetectChanges, | ||||
|         component::{ComponentDescriptor, ComponentId, StorageType}, | ||||
|         component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType}, | ||||
|         ptr::OwningPtr, | ||||
|     }; | ||||
|     use bevy_ecs_macros::Component; | ||||
|     use bevy_utils::HashSet; | ||||
|     use std::{ | ||||
|         any::TypeId, | ||||
|         panic, | ||||
|         sync::{ | ||||
|             atomic::{AtomicBool, AtomicU32, Ordering}, | ||||
| @ -1762,4 +1789,64 @@ mod tests { | ||||
|             world.insert_resource_by_id(invalid_component_id, ptr); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     #[derive(Component)] | ||||
|     struct Foo; | ||||
| 
 | ||||
|     #[derive(Component)] | ||||
|     struct Bar; | ||||
| 
 | ||||
|     #[derive(Component)] | ||||
|     struct Baz; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn inspect_entity_components() { | ||||
|         let mut world = World::new(); | ||||
|         let ent0 = world.spawn().insert_bundle((Foo, Bar, Baz)).id(); | ||||
|         let ent1 = world.spawn().insert_bundle((Foo, Bar)).id(); | ||||
|         let ent2 = world.spawn().insert_bundle((Bar, Baz)).id(); | ||||
|         let ent3 = world.spawn().insert_bundle((Foo, Baz)).id(); | ||||
|         let ent4 = world.spawn().insert_bundle((Foo,)).id(); | ||||
|         let ent5 = world.spawn().insert_bundle((Bar,)).id(); | ||||
|         let ent6 = world.spawn().insert_bundle((Baz,)).id(); | ||||
| 
 | ||||
|         fn to_type_ids(component_infos: Vec<&ComponentInfo>) -> HashSet<Option<TypeId>> { | ||||
|             component_infos | ||||
|                 .into_iter() | ||||
|                 .map(|component_info| component_info.type_id()) | ||||
|                 .collect() | ||||
|         } | ||||
| 
 | ||||
|         let foo_id = TypeId::of::<Foo>(); | ||||
|         let bar_id = TypeId::of::<Bar>(); | ||||
|         let baz_id = TypeId::of::<Baz>(); | ||||
|         assert_eq!( | ||||
|             to_type_ids(world.inspect_entity(ent0)), | ||||
|             [Some(foo_id), Some(bar_id), Some(baz_id)].into() | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             to_type_ids(world.inspect_entity(ent1)), | ||||
|             [Some(foo_id), Some(bar_id)].into() | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             to_type_ids(world.inspect_entity(ent2)), | ||||
|             [Some(bar_id), Some(baz_id)].into() | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             to_type_ids(world.inspect_entity(ent3)), | ||||
|             [Some(foo_id), Some(baz_id)].into() | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             to_type_ids(world.inspect_entity(ent4)), | ||||
|             [Some(foo_id)].into() | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             to_type_ids(world.inspect_entity(ent5)), | ||||
|             [Some(bar_id)].into() | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             to_type_ids(world.inspect_entity(ent6)), | ||||
|             [Some(baz_id)].into() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 harudagondi
						harudagondi