Migrate bevy_transform to required components (#14964)

The first step in the migration to required components! This PR removes
`GlobalTransform` from all user-facing code, since it's now added
automatically wherever `Transform` is used.

## Testing

- None of the examples I tested were broken, and I assume breaking
transforms in any way would be visible *everywhere*

---

## Changelog

- Make `Transform` require `GlobalTransform`
~~- Remove `GlobalTransform` from all engine bundles~~
- Remove in-engine insertions of GlobalTransform and TransformBundle
- Deprecate `TransformBundle`
- update docs to reflect changes

## Migration Guide

Replace all insertions of `GlobalTransform` and/or `TransformBundle`
with `Transform` alone.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Tim <JustTheCoolDude@gmail.com>
This commit is contained in:
Emerson Coskey 2024-09-27 10:06:48 -07:00 committed by GitHub
parent a0c722ff4c
commit b04947d44f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 43 additions and 67 deletions

View File

@ -1,3 +1,4 @@
#![expect(deprecated)]
use bevy_ecs::bundle::Bundle; use bevy_ecs::bundle::Bundle;
use crate::prelude::{GlobalTransform, Transform}; use crate::prelude::{GlobalTransform, Transform};
@ -24,6 +25,10 @@ use crate::prelude::{GlobalTransform, Transform};
/// update the [`Transform`] of an entity in this schedule or after, you will notice a 1 frame lag /// update the [`Transform`] of an entity in this schedule or after, you will notice a 1 frame lag
/// before the [`GlobalTransform`] is updated. /// before the [`GlobalTransform`] is updated.
#[derive(Clone, Copy, Debug, Default, Bundle)] #[derive(Clone, Copy, Debug, Default, Bundle)]
#[deprecated(
since = "0.15.0",
note = "Use the `Transform` component instead. Inserting `Transform` will now also insert a `GlobalTransform` automatically."
)]
pub struct TransformBundle { pub struct TransformBundle {
/// The transform of the entity. /// The transform of the entity.
pub local: Transform, pub local: Transform,

View File

@ -17,7 +17,9 @@ use {
/// ///
/// * To get the global transform of an entity, you should get its [`GlobalTransform`]. /// * To get the global transform of an entity, you should get its [`GlobalTransform`].
/// * For transform hierarchies to work correctly, you must have both a [`Transform`] and a [`GlobalTransform`]. /// * For transform hierarchies to work correctly, you must have both a [`Transform`] and a [`GlobalTransform`].
/// * You may use the [`TransformBundle`](crate::bundles::TransformBundle) to guarantee this. /// ~* You may use the [`TransformBundle`](crate::bundles::TransformBundle) to guarantee this.~
/// * [`TransformBundle`](crate::bundles::TransformBundle) is now deprecated.
/// [`GlobalTransform`] is automatically inserted whenever [`Transform`] is inserted.
/// ///
/// ## [`Transform`] and [`GlobalTransform`] /// ## [`Transform`] and [`GlobalTransform`]
/// ///

View File

@ -13,7 +13,9 @@ use {
/// * To place or move an entity, you should set its [`Transform`]. /// * To place or move an entity, you should set its [`Transform`].
/// * To get the global transform of an entity, you should get its [`GlobalTransform`]. /// * To get the global transform of an entity, you should get its [`GlobalTransform`].
/// * To be displayed, an entity must have both a [`Transform`] and a [`GlobalTransform`]. /// * To be displayed, an entity must have both a [`Transform`] and a [`GlobalTransform`].
/// * You may use the [`TransformBundle`](crate::bundles::TransformBundle) to guarantee this. /// ~* You may use the [`TransformBundle`](crate::bundles::TransformBundle) to guarantee this.~
/// * [`TransformBundle`](crate::bundles::TransformBundle) is now deprecated.
/// [`GlobalTransform`] is inserted automatically whenever [`Transform`] is inserted.
/// ///
/// ## [`Transform`] and [`GlobalTransform`] /// ## [`Transform`] and [`GlobalTransform`]
/// ///
@ -39,6 +41,7 @@ use {
#[cfg_attr( #[cfg_attr(
feature = "bevy-support", feature = "bevy-support",
derive(Component, Reflect), derive(Component, Reflect),
require(GlobalTransform),
reflect(Component, Default, PartialEq, Debug) reflect(Component, Default, PartialEq, Debug)
)] )]
#[cfg_attr( #[cfg_attr(

View File

@ -88,7 +88,6 @@ mod tests {
use bevy_math::{Quat, Vec3}; use bevy_math::{Quat, Vec3};
use crate::{ use crate::{
bundles::TransformBundle,
components::{GlobalTransform, Transform}, components::{GlobalTransform, Transform},
helper::TransformHelper, helper::TransformHelper,
plugins::TransformPlugin, plugins::TransformPlugin,
@ -122,7 +121,7 @@ mod tests {
let mut entity = None; let mut entity = None;
for transform in transforms { for transform in transforms {
let mut e = app.world_mut().spawn(TransformBundle::from(transform)); let mut e = app.world_mut().spawn(transform);
if let Some(entity) = entity { if let Some(entity) = entity {
e.set_parent(entity); e.set_parent(entity);

View File

@ -32,6 +32,8 @@ pub mod systems;
/// The transform prelude. /// The transform prelude.
/// ///
/// This includes the most common types in this crate, re-exported for your convenience. /// This includes the most common types in this crate, re-exported for your convenience.
#[doc(hidden)]
#[expect(deprecated)]
pub mod prelude { pub mod prelude {
#[doc(hidden)] #[doc(hidden)]
pub use crate::components::*; pub use crate::components::*;

View File

@ -190,7 +190,7 @@ mod test {
use bevy_math::{vec3, Vec3}; use bevy_math::{vec3, Vec3};
use bevy_tasks::{ComputeTaskPool, TaskPool}; use bevy_tasks::{ComputeTaskPool, TaskPool};
use crate::{bundles::TransformBundle, systems::*}; use crate::systems::*;
use bevy_hierarchy::{BuildChildren, ChildBuild}; use bevy_hierarchy::{BuildChildren, ChildBuild};
#[test] #[test]
@ -199,8 +199,7 @@ mod test {
let mut world = World::default(); let mut world = World::default();
let offset_global_transform = let offset_global_transform =
|offset| GlobalTransform::from(Transform::from_xyz(offset, offset, offset)); |offset| GlobalTransform::from(Transform::from_xyz(offset, offset, offset));
let offset_transform = let offset_transform = |offset| Transform::from_xyz(offset, offset, offset);
|offset| TransformBundle::from_transform(Transform::from_xyz(offset, offset, offset));
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.add_systems((sync_simple_transforms, propagate_transforms)); schedule.add_systems((sync_simple_transforms, propagate_transforms));
@ -257,22 +256,14 @@ mod test {
schedule.add_systems((sync_simple_transforms, propagate_transforms)); schedule.add_systems((sync_simple_transforms, propagate_transforms));
// Root entity // Root entity
world.spawn(TransformBundle::from(Transform::from_xyz(1.0, 0.0, 0.0))); world.spawn(Transform::from_xyz(1.0, 0.0, 0.0));
let mut children = Vec::new(); let mut children = Vec::new();
world world
.spawn(TransformBundle::from(Transform::from_xyz(1.0, 0.0, 0.0))) .spawn(Transform::from_xyz(1.0, 0.0, 0.0))
.with_children(|parent| { .with_children(|parent| {
children.push( children.push(parent.spawn(Transform::from_xyz(0.0, 2.0, 0.)).id());
parent children.push(parent.spawn(Transform::from_xyz(0.0, 0.0, 3.)).id());
.spawn(TransformBundle::from(Transform::from_xyz(0.0, 2.0, 0.)))
.id(),
);
children.push(
parent
.spawn(TransformBundle::from(Transform::from_xyz(0.0, 0.0, 3.)))
.id(),
);
}); });
schedule.run(&mut world); schedule.run(&mut world);
@ -299,18 +290,10 @@ mod test {
let mut commands = Commands::new(&mut queue, &world); let mut commands = Commands::new(&mut queue, &world);
let mut children = Vec::new(); let mut children = Vec::new();
commands commands
.spawn(TransformBundle::from(Transform::from_xyz(1.0, 0.0, 0.0))) .spawn(Transform::from_xyz(1.0, 0.0, 0.0))
.with_children(|parent| { .with_children(|parent| {
children.push( children.push(parent.spawn(Transform::from_xyz(0.0, 2.0, 0.0)).id());
parent children.push(parent.spawn(Transform::from_xyz(0.0, 0.0, 3.0)).id());
.spawn(TransformBundle::from(Transform::from_xyz(0.0, 2.0, 0.0)))
.id(),
);
children.push(
parent
.spawn(TransformBundle::from(Transform::from_xyz(0.0, 0.0, 3.0)))
.id(),
);
}); });
queue.apply(&mut world); queue.apply(&mut world);
schedule.run(&mut world); schedule.run(&mut world);
@ -417,15 +400,12 @@ mod test {
let mut grandchild = Entity::from_raw(1); let mut grandchild = Entity::from_raw(1);
let parent = app let parent = app
.world_mut() .world_mut()
.spawn(( .spawn(Transform::from_translation(translation))
Transform::from_translation(translation),
GlobalTransform::IDENTITY,
))
.with_children(|builder| { .with_children(|builder| {
child = builder child = builder
.spawn(TransformBundle::IDENTITY) .spawn(Transform::IDENTITY)
.with_children(|builder| { .with_children(|builder| {
grandchild = builder.spawn(TransformBundle::IDENTITY).id(); grandchild = builder.spawn(Transform::IDENTITY).id();
}) })
.id(); .id();
}) })
@ -462,9 +442,9 @@ mod test {
fn setup_world(world: &mut World) -> (Entity, Entity) { fn setup_world(world: &mut World) -> (Entity, Entity) {
let mut grandchild = Entity::from_raw(0); let mut grandchild = Entity::from_raw(0);
let child = world let child = world
.spawn(TransformBundle::IDENTITY) .spawn(Transform::IDENTITY)
.with_children(|builder| { .with_children(|builder| {
grandchild = builder.spawn(TransformBundle::IDENTITY).id(); grandchild = builder.spawn(Transform::IDENTITY).id();
}) })
.id(); .id();
(child, grandchild) (child, grandchild)
@ -477,7 +457,7 @@ mod test {
assert_eq!(temp_grandchild, grandchild); assert_eq!(temp_grandchild, grandchild);
app.world_mut() app.world_mut()
.spawn(TransformBundle::IDENTITY) .spawn(Transform::IDENTITY)
.add_children(&[child]); .add_children(&[child]);
core::mem::swap( core::mem::swap(
&mut *app.world_mut().get_mut::<Parent>(child).unwrap(), &mut *app.world_mut().get_mut::<Parent>(child).unwrap(),
@ -496,14 +476,9 @@ mod test {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.add_systems((sync_simple_transforms, propagate_transforms)); schedule.add_systems((sync_simple_transforms, propagate_transforms));
// Spawn a `TransformBundle` entity with a local translation of `Vec3::ONE` // Spawn a `Transform` entity with a local translation of `Vec3::ONE`
let mut spawn_transform_bundle = || { let mut spawn_transform_bundle =
world || world.spawn(Transform::from_translation(translation)).id();
.spawn(TransformBundle::from_transform(
Transform::from_translation(translation),
))
.id()
};
// Spawn parent and child with identical transform bundles // Spawn parent and child with identical transform bundles
let parent = spawn_transform_bundle(); let parent = spawn_transform_bundle();

View File

@ -29,7 +29,7 @@ fn setup_cube(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
commands commands
.spawn(TransformBundle::default()) .spawn(Transform::default())
.with_children(|parent| { .with_children(|parent| {
// cube // cube
parent.spawn(PbrBundle { parent.spawn(PbrBundle {
@ -61,8 +61,7 @@ doesn't have a [`ViewVisibility`] or [`InheritedVisibility`] component.
Since the cube is spawned as a child of an entity without the Since the cube is spawned as a child of an entity without the
visibility components, it will not be visible at all. visibility components, it will not be visible at all.
To fix this, you must use [`SpatialBundle`] over [`TransformBundle`], To fix this, you must use [`SpatialBundle`], as follows:
as follows:
```rust,no_run ```rust,no_run
use bevy::prelude::*; use bevy::prelude::*;
@ -73,7 +72,7 @@ fn setup_cube(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
commands commands
// We use SpatialBundle instead of TransformBundle, it contains the // We use SpatialBundle instead of Transform, it contains the
// visibility components needed to display the cube, // visibility components needed to display the cube,
// In addition to the Transform and GlobalTransform components. // In addition to the Transform and GlobalTransform components.
.spawn(SpatialBundle::default()) .spawn(SpatialBundle::default())
@ -103,9 +102,8 @@ fn main() {
``` ```
A similar problem occurs when the [`GlobalTransform`] component is missing. A similar problem occurs when the [`GlobalTransform`] component is missing.
However, when a parent [`GlobalTransform`] is missing, However, it will be automatically inserted whenever `Transform` is
it will simply prevent all transform propagation, inserted, as it's a required component.
including when updating the [`Transform`] component of the child.
You will most likely encounter this warning when loading a scene You will most likely encounter this warning when loading a scene
as a child of a pre-existing [`Entity`] that does not have the proper components. as a child of a pre-existing [`Entity`] that does not have the proper components.
@ -113,8 +111,6 @@ as a child of a pre-existing [`Entity`] that does not have the proper components
[`InheritedVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.InheritedVisibility.html [`InheritedVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.InheritedVisibility.html
[`ViewVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.ViewVisibility.html [`ViewVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.ViewVisibility.html
[`GlobalTransform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.GlobalTransform.html [`GlobalTransform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.GlobalTransform.html
[`Transform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.Transform.html
[`Parent`]: https://docs.rs/bevy/*/bevy/hierarchy/struct.Parent.html [`Parent`]: https://docs.rs/bevy/*/bevy/hierarchy/struct.Parent.html
[`Entity`]: https://docs.rs/bevy/*/bevy/ecs/entity/struct.Entity.html [`Entity`]: https://docs.rs/bevy/*/bevy/ecs/entity/struct.Entity.html
[`SpatialBundle`]: https://docs.rs/bevy/*/bevy/render/prelude/struct.SpatialBundle.html [`SpatialBundle`]: https://docs.rs/bevy/*/bevy/render/prelude/struct.SpatialBundle.html
[`TransformBundle`]: https://docs.rs/bevy/*/bevy/transform/struct.TransformBundle.html

View File

@ -130,15 +130,9 @@ fn setup(
for i in -5..5 { for i in -5..5 {
// Create joint entities // Create joint entities
let joint_0 = commands let joint_0 = commands
.spawn(TransformBundle::from(Transform::from_xyz( .spawn(Transform::from_xyz(i as f32 * 1.5, 0.0, i as f32 * 0.1))
i as f32 * 1.5,
0.0,
i as f32 * 0.1,
)))
.id();
let joint_1 = commands
.spawn((AnimatedJoint, TransformBundle::IDENTITY))
.id(); .id();
let joint_1 = commands.spawn((AnimatedJoint, Transform::IDENTITY)).id();
// Set joint_1 as a child of joint_0. // Set joint_1 as a child of joint_0.
commands.entity(joint_0).add_children(&[joint_1]); commands.entity(joint_0).add_children(&[joint_1]);

View File

@ -111,7 +111,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
..default() ..default()
}, },
Sprite::default(), Sprite::default(),
TransformBundle::default(), Transform::default(),
VisibilityBundle::default(), VisibilityBundle::default(),
)); ));
} }

View File

@ -380,7 +380,7 @@ fn spawn_tree(
} }
// insert root // insert root
ents.push(commands.spawn(TransformBundle::from(root_transform)).id()); ents.push(commands.spawn(root_transform).id());
let mut result = InsertResult::default(); let mut result = InsertResult::default();
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
@ -426,7 +426,7 @@ fn spawn_tree(
}; };
// only insert the components necessary for the transform propagation // only insert the components necessary for the transform propagation
cmd = cmd.insert(TransformBundle::from(transform)); cmd = cmd.insert(transform);
cmd.id() cmd.id()
}; };