From e78deeccce5d5582f3ed173829f15f2558e30612 Mon Sep 17 00:00:00 2001 From: Elliott Pierce Date: Mon, 23 Jun 2025 12:08:27 -0400 Subject: [PATCH] better docs --- crates/bevy_ecs/src/entity/mod.rs | 137 +++++++++++++++++------------- 1 file changed, 80 insertions(+), 57 deletions(-) diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index bae8f9c924..452429b0f8 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -2,35 +2,18 @@ //! //! # What is an Entity? //! -//! An entity is a thing that exists in a [`World`]. -//! Entities have zero or more [`Component`]s, which are just data associated with the entity. -//! Entities serve the same purpose as things like game objects from Unity or nodes from Godot: -//! They can be created, have their data accessed and changed, and are eventually deleted. -//! In Bevy, an entity can represent anything from a player character to the game's window itself (and everything in-between!) -//! However, unlike other engines, entities are not represented as large class objects. -//! This makes Bevy much faster in principal, but it also leads to some perhaps unintuitive differences that need to be considered. +//! The ecs [docs](crate) give an overview of what entities are and generally how to use them. +//! These docs provide more detail into how they actually work. +//! In these docs "entity" will refer to an [`Entity`], an identifier for a "game object" or "thing" in the ecs [`World`]. //! -//! Because entities are not traditional, object-oriented, garbage collected, class instances, more effort is needed to interact with them. -//! The biggest difference is that the [`Entity`] type does *not* represent a conceptual entity. -//! In other words, an entity's data, its components, are not stored within the [`Entity`] type. -//! Instead, the [`Entity`] acts as an id, and it's components are stored separate from its id in the [`World`]. -//! In fact, one way to think about entities and their data is to imagine each world as a list of entity ids -//! and a hashmap for each component which maps [`Entity`] values to component values if the entity has that component. -//! Of course, the [`World`] is really quite different from this and much more efficient, but thinking about it this way will be helpful to understand how entities work. -//! Put another way, the world can be thought of as a big spreadsheet, where each component type has a column and each entity has a row. -//! In order to get an entity's components, Bevy finds the values by looking up the [`Entity`]'s [`EntityRow`] and the [`Component`](crate::component::Component)'s [`ComponentId`](crate::component::ComponentId). -//! Interacting with an entity can be done through three main interfaces: -//! Use the [`World`] with methods like [`World::entity`](crate::world::World::entity) for complete and immediate access to an entity and its components. -//! Use [`Query`]s for very fast access to component values. -//! Use [`Commands`] with methods like [`Commands::entity`](crate::system::Commands::entity) for delayed but unrestricted access to entities. +//! # In this Module //! -//! In short: +//! This module contains four main things: //! -//! - An entity is a thing in the world, similar to game objects from Unity or nodes from Godot. -//! - Entities can represent anything! (players, items, and the app window itself) -//! - Entities have data attached to them called [`Component`]s. (health on the player, damage on the item, and resolution on the window) -//! - The [`Entity`] type is an id for its entity; it is not the entity itself and does not store component data directly. -//! - To access an entity's data, use [`World`], [`Query`], or [`Commands`] apis. +//! - Core ecs types like [`Entity`], [`Entities`], and [`EntitiesAllocator`]. +//! - Utilities for [`Entity`] ids like [`MapEntities`], [`EntityHash`], and [`UniqueEntityVec`]. +//! - Helpers for game object tasks like [`EntityCloner`]. +//! - Entity-related error types like [`EntityDoesNotExistError`]. //! //! # Entity Life Cycle //! @@ -38,37 +21,37 @@ //! They are created, used for a while, and eventually destroyed. //! Let's start from the top: //! -//! **Spawn:** An entity is crated. +//! **Spawn:** An entity is created. //! In bevy, this is called spawning. //! Most commonly, this is done through [`World::spawn`](crate::world::World::spawn) or [`Commands::spawn`](crate::system::Commands::spawn). -//! This creates a fresh entity in the world and returns its [`Entity`] id, which can be used to interact with the entity. -//! These methods initialize the entity with a [`Bundle`], which is a group of [components](crate::component::Component) that it starts with. -//! It is also possible to use [`World::spawn_empty`](crate::world::World::spawn_empty) or [`Commands::spawn_empty`](crate::system::Commands::spawn_empty), which are similar but do not add any components to the entity. -//! In either case, the returned [`Entity`] id is used to further interact with the entity. -//! Once an entity is created, you will need its [`Entity`] id to progress the entity through its life cycle. +//! This creates a fresh entity in the world and returns its [`Entity`] id, which can be used to interact with the game object it identifies. +//! These methods initialize the entity with a [`Bundle`], a group of [components](crate::component::Component) that it starts with. +//! It is also possible to use [`World::spawn_empty`](crate::world::World::spawn_empty) or [`Commands::spawn_empty`](crate::system::Commands::spawn_empty), which are similar but do not add any components to the game object. +//! In either case, the returned [`Entity`] id is used to further interact with the game object. +//! Once an game object is created, you will need its [`Entity`] id to progress the object through its life cycle. //! This can be done through [`World::entity_mut`](crate::world::World::entity_mut) and [`Commands::entity`](crate::system::Commands::entity). -//! Even if you don't store the id, you can still find the entity you spawned by searching for it in a [`Query`]. +//! Even if you don't store the id, you can still find the game object you spawned by searching for it in a [`Query`]. //! -//! **Insert:** Once an entity has been created, additional [`Bundle`]s can be inserted onto the entity. -//! There are lots of ways to do this and lots of ways to configure what to do when a component in the bundle is already present on the entity. -//! Each entity can only have 0 or 1 values for a component. +//! **Insert:** Once an game object has been created, additional [`Bundle`]s can be inserted. +//! There are lots of ways to do this and lots of ways to configure what to do when a component in the bundle is already present on the object. +//! Each game object can only have 0 or 1 values for a kind of component. //! See [`EntityWorldMut::insert`](crate::world::EntityWorldMut::insert) and [`EntityCommands::insert`](crate::system::EntityCommands::insert) for a start on how to do this. //! -//! **Remove:** Components on an entity can be removed as well. +//! **Remove:** Components on an game object can be removed as well. //! See [`EntityWorldMut::remove`](crate::world::EntityWorldMut::remove) and [`EntityCommands::remove`](crate::system::EntityCommands::remove) for a start on how to do this. //! -//! **Despawn:** Despawn an entity when it is no longer needed. +//! **Despawn:** Despawn an game object when it is no longer needed. //! This destroys it and all its components. -//! The entity is no longer reachable through the [`World`], [`Commands`], or [`Query`]s. -//! Note that this means an [`Entity`] id may refer to an entity that has since been despawned! -//! Not all [`Entity`] ids refer to active entities. -//! If an [`Entity`] id is used when its entity no longer exists, an [`EntityDoesNotExistError`] is emitted. -//! Any [`System`](crate::system) could despawn entities; even if you never share an entity's id, it could still be despawned unexpectedly. +//! The game object is no longer reachable through the [`World`], [`Commands`], or [`Query`]s. +//! Note that this means an [`Entity`] id may refer to an game object that has since been despawned! +//! Not all [`Entity`] ids refer to active objects. +//! If an [`Entity`] id is used when its game object no longer exists, an [`EntityDoesNotExistError`] is emitted. +//! Any [`System`](crate::system) could despawn objects; even if you never share an entity's id, it could still be despawned unexpectedly. //! Handle these errors gracefully. //! //! In short: //! -//! - Entities are spawned through methods like [`World::spawn`](crate::world::World::spawn), which return an [`Entity`] id for the new entity. +//! - Game object are spawned through methods like [`World::spawn`](crate::world::World::spawn), which return an [`Entity`] id for the new entity. //! - Once spawned, they can be accessed and modified through [`Query`]s and other apis. //! - You can get the [`Entity`] id of an entity through [`Query`]s, so loosing an [`Entity`] id is not a problem. //! - Entities can have components inserted and removed via [`World::entity_mut`](crate::world::World::entity_mut) and [`Commands::entity`](crate::system::Commands::entity). @@ -80,28 +63,55 @@ //! As mentioned entities each have an [`Entity`] id, which is used to interact with that entity. //! But what actually is this id? //! This [`Entity`] id is the combination of two ideas: [`EntityRow`] and [`EntityGeneration`]. +//! You can think of the [`Entity`] type as a `struct Entity { row: u32, generation: u32 }`. //! -//! An [`EntityRow`] always references exactly 1 entity in the [`World`]; they are always valid. -//! This differs from [`Entity`] which references 0 or 1 entities, depending on if the entity it refers to still exists. -//! Each [`EntityRow`] refers to an entity, and each entity has an [`EntityRow`]. -//! The rows are represented with 32 bits, so there are always over 4 billion entities in the world. -//! However, not all these entities are usable or stored in memory. -//! To understand why, let's look at the states an entity row can be in: +//! To understand these ids, picture thee ecs [`World`] as a spreadsheet. +//! Each kind of component is represented by a column in the spreadsheet and each entity is a row. +//! That's what the `row` does in [`Entity`]; it identifies where in the spreadsheet to find component values. +//! If an entity doesn't have a component, picture leaving the cell at the that entity row and component column blank or `None`. +//! To find the component values of an entity, Bevy searches through the spreadsheet at the [`EntityRow`] for the entity and the [`ComponentId`](crate::component::ComponentId) for the component. //! -//! Each [`EntityRow`] has a [`EntityIdLocation`] which defines that row/entity's state. +//! An [`EntityRow`] always references exactly 1 entity in the [`World`]. +//! Think about it, even if the spreadsheet only *uses* rows 1, 2, and 12, it still *has* millions of rows. +//! In the spreadsheet analogy, you can think of each row in one of 3 states: +//! +//! 1. The row is not used. +//! Think of this as graying out the row or otherwise hiding it. +//! This row doesn't just have no components; it isn't even participating at this point. +//! 2. The row is empty. +//! The row is being used; it's visible, discoverable, etc; it just happens to not have any component values. +//! 3. The row is full. +//! This is the "normal" state of a row. +//! It has some component values and is being used. +//! +//! [`EntityRow`] behaves much the same way. +//! Each row has a [`EntityIdLocation`] which defines that row/entity's state. //! The [`EntityIdLocation`] is an `Option` of [`EntityLocation`]. -//! If this is `Some`, the row is considered constructed, otherwise it is considered destructed. +//! If this is `Some`, the row is considered constructed (think *used* in the spreadsheet), otherwise it is considered destructed (think *grayed out* in the spreadsheet). //! Only constructed entities, entities with `Some` [`EntityLocation`], participate in the [`World`]. -//! The [`EntityLocation`] further describes which components an entity has and where to find them. -//! That means each entity row can be in three states: 1) It has some components, 2) It has no components *empty*, 3) It has no location *null*. -//! Only non-null entities are discoverable through [`Query`]s, etc. +//! The [`EntityLocation`] further describes which components an entity has and where to find them; it determines which spreadsheet cells are blank and which ones have values. +//! Only constructed rows are discoverable through [`Query`]s, etc. +//! +//! With that spreadsheet intuition, lets get a bit more precise with some definitions: +//! +//! - An entity that is used, not grayed out in the spreadsheet, is considered *constructed*. +//! - An entity that is is grayed out in the spreadsheet, not used, is considered *destructed* or *null*. +//! - A constructed entity that has no components is considered *empty* or *void*, which is different from *null* since these are still participating entities, discoverable through queries and interact-able through commands. +//! +//! An [`EntityRow`] always references exactly 1 entity in the [`World`]; they always exist (even though they may still be null). +//! This differs from [`Entity`] which references 0 or 1 entities, depending on if the entity it refers to still exists. +//! Each [`EntityRow`] refers to an entity, and each entity has an [`EntityRow`]; this is a [bijection](https://en.wikipedia.org/wiki/Bijection). +//! The rows are represented with 32 bits, so there are always over 4 billion entities in the world. +//! However, not all these entities are usable or stored in memory; Bevy doesn't store information for rows that have always been *null* (never been constructed). //! //! Rows can be repeatedly constructed and destructed. //! Each construction and destruction corresponds to a [`EntityGeneration`]. //! The first time a row is constructed, it has a generation of 0, and when it is destructed, it gets a generation of 1. //! This differentiates each construction of that [`EntityRow`]. -//! All an [`Entity`] id is is a [`EntityRow`] (which entity it is) and a [`EntityGeneration`] (which version of that row it references). +//! Again, all an [`Entity`] id is is a [`EntityRow`] (which entity it is) and a [`EntityGeneration`] (which version of that row it references). //! When an [`Entity`] id is invalid, it just means that that generation of its row has been destructed. +//! It could still be null or it could have since been constructed again. +//! Either way, that entity, that row-generation pair no longer exists. //! //! As mentioned, once an [`EntityRow`] is destructed, it is not discoverable until it is constructed again. //! To prevent these rows from being forgotten, bevy tracks them in an [`EntitiesAllocator`]. @@ -114,7 +124,7 @@ //! Of course, to make that entity usable, it will need to be passed to [`World::construct`](crate::world::World::construct). //! Managing entity ids manually is advanced but can be very useful for concurrency, custom entity allocators, etc. //! But there are risks when used improperly: -//! Loosing a destructed entity row without returning it to bevy's allocator will cause that row to be unreachable, effectively a memory leak. +//! Loosing a null entity row without returning it to bevy's allocator will cause that row to be unreachable, effectively a memory leak. //! Further, constructing an arbitrary [`EntityRow`] can cause problems if that same row is queued for reuse in the allocator. //! Use this powerfully but with caution. //! @@ -130,6 +140,19 @@ //! - While understanding these details help build an intuition for how bevy handles entities, using these apis directly is risky but powerful. //! - Lots of id information can be obtained from [`Entities`]. //! +//! # Storage +//! +//! As mentioned about, an ecs [`World`] can be imagined as a spreadsheet. +//! One way that spreadsheet could be implemented is a list of [`Entity`]s and a hashmap for each component that maps an [`EntityRow`] to a component value if that row has the entity. +//! Bevy's ecs is quite different from that implementation (and much, much faster). +//! For details on how component storage actually works, see [`storage`](crate::storage). +//! +//! Regardless, the spreadsheet also needs a special column that tracks metadata about an entity. +//! This column doesn't represents a component and is specific to the [`EntityRow`], not the [`Entity`]. +//! For example, one thing Bevy stores in this metadata is the current [`EntityGeneration`] of the row. +//! It also stores more information like the [`Tick`] a row was last constructed or destructed, and the [`EntityIdLocation`] itself. +//! For more information about what's stored here, see [`Entities`], Bevy's implementation of this special column. +//! //! [`World`]: crate::world::World //! [`Query`]: crate::system::Query //! [`Bundle`]: crate::bundle::Bundle