bevy/release-content/migration-guides/entity_representation.md
Eagster 12aba64900
Make entity generation a new type and remove identifier (#19121)
# Objective

This is a followup to #18704 . There's lots more followup work, but this
is the minimum to unblock #18670, etc.

This direction has been given the green light by Alice
[here](https://github.com/bevyengine/bevy/pull/18704#issuecomment-2853368129).

## Solution

I could have split this over multiple PRs, but I figured skipping
straight here would be easiest for everyone and would unblock things the
quickest.

This removes the now no longer needed `identifier` module and makes
`Entity::generation` go from `NonZeroU32` to `struct
EntityGeneration(u32)`.

## Testing

CI

---------

Co-authored-by: Mark Nokalt <marknokalt@live.com>
2025-05-08 04:03:05 +00:00

3.0 KiB

title pull_requests
Manual Entity Creation and Representation
18704
19121

An entity is made of two parts: and index and a generation. Both have changes:

Index

Entity no longer stores its index as a plain u32 but as the new EntityRow, which wraps a NonMaxU32. Previously, Entity::index could be u32::MAX, but that is no longer a valid index. As a result, Entity::from_raw now takes EntityRow as a parameter instead of u32. EntityRow can be constructed via EntityRow::new, which takes a NonMaxU32. If you don't want to add nonmax as a dependency, use Entity::from_raw_u32 which is identical to the previous Entity::from_raw, except that it now returns Option where the result is None if u32::MAX is passed.

Bevy made this change because it puts a niche in the EntityRow type which makes Option<EntityRow> half the size of Option<u32>. This is used internally to open up performance improvements to the ECS.

Although you probably shouldn't be making entities manually, it is sometimes useful to do so for tests. To migrate tests, use:

- let entity = Entity::from_raw(1);
+ let entity = Entity::from_raw_u32(1).unwrap();

If you are creating entities manually in production, don't do that! Use Entities::alloc instead. But if you must create one manually, either reuse a EntityRow you know to be valid by using Entity::from_raw and Entity::row, or handle the error case of None returning from Entity::from_raw_u32(my_index).

Generation

An entity's generation is no longer a NonZeroU32. Instead, it is an EntityGeneration. Internally, this stores a u32, but that might change later.

Working with the generation directly has never been recommended, but it is sometimes useful to do so in tests. To create a generation do EntityGeneration::FIRST.after_versions(expected_generation). To use this in tests, do assert_eq!(entity.generation(), EntityGeneration::FIRST.after_versions(expected_generation)).

Removed Interfaces

The identifier module and all its contents have been removed. These features have been slimmed down and rolled into Entity.

This means that where Result<T, IdentifierError> was returned, Option<T> is now returned.

Functionality

It is well documented that both the bit format, serialization, and Ord implementations for Entity are subject to change between versions. Those have all changed in this version.

For entity ordering, the order still prioretizes an entity's generation, but after that, it now considers higher index entities less than lower index entities.

The changes to serialization and the bit format are directly related. Effectively, this means that all serialized and transmuted entities will not work as expected and may crash. To migrate, invert the lower 32 bits of the 64 representation of the entity, and subtract 1 from the upper bits. Again, this is still subject to change, and serialized scenes may break between versions.