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}, |     entity::{Entities, Entity}, | ||||||
|     world::{FromWorld, World}, |     world::{FromWorld, World}, | ||||||
| }; | }; | ||||||
| use bevy_utils::tracing::{error, warn}; | use bevy_utils::tracing::{error, info, warn}; | ||||||
| pub use command_queue::CommandQueue; | pub use command_queue::CommandQueue; | ||||||
| pub use parallel_scope::*; | pub use parallel_scope::*; | ||||||
| use std::marker::PhantomData; | 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`].
 |     /// Returns the underlying [`Commands`].
 | ||||||
|     pub fn commands(&mut self) -> &mut Commands<'w, 's> { |     pub fn commands(&mut self) -> &mut Commands<'w, 's> { | ||||||
|         self.commands |         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)] | #[cfg(test)] | ||||||
| #[allow(clippy::float_cmp, clippy::approx_constant)] | #[allow(clippy::float_cmp, clippy::approx_constant)] | ||||||
| mod tests { | mod tests { | ||||||
|  | |||||||
| @ -12,7 +12,8 @@ use crate::{ | |||||||
|     bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, |     bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, | ||||||
|     change_detection::{MutUntyped, Ticks}, |     change_detection::{MutUntyped, Ticks}, | ||||||
|     component::{ |     component::{ | ||||||
|         Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, StorageType, |         Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components, | ||||||
|  |         StorageType, | ||||||
|     }, |     }, | ||||||
|     entity::{AllocAtWithoutReplacement, Entities, Entity}, |     entity::{AllocAtWithoutReplacement, Entities, Entity}, | ||||||
|     query::{QueryState, WorldQuery}, |     query::{QueryState, WorldQuery}, | ||||||
| @ -280,6 +281,30 @@ impl World { | |||||||
|             .unwrap_or_else(|| panic!("Entity {:?} does not exist", entity)) |             .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.
 |     /// 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.
 |     /// This will return [`None`] if the `entity` exists with a different generation.
 | ||||||
|     ///
 |     ///
 | ||||||
| @ -1542,11 +1567,13 @@ mod tests { | |||||||
|     use super::World; |     use super::World; | ||||||
|     use crate::{ |     use crate::{ | ||||||
|         change_detection::DetectChanges, |         change_detection::DetectChanges, | ||||||
|         component::{ComponentDescriptor, ComponentId, StorageType}, |         component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType}, | ||||||
|         ptr::OwningPtr, |         ptr::OwningPtr, | ||||||
|     }; |     }; | ||||||
|     use bevy_ecs_macros::Component; |     use bevy_ecs_macros::Component; | ||||||
|  |     use bevy_utils::HashSet; | ||||||
|     use std::{ |     use std::{ | ||||||
|  |         any::TypeId, | ||||||
|         panic, |         panic, | ||||||
|         sync::{ |         sync::{ | ||||||
|             atomic::{AtomicBool, AtomicU32, Ordering}, |             atomic::{AtomicBool, AtomicU32, Ordering}, | ||||||
| @ -1762,4 +1789,64 @@ mod tests { | |||||||
|             world.insert_resource_by_id(invalid_component_id, ptr); |             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