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 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
/// before the [`GlobalTransform`] is updated.
#[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 {
/// The transform of the entity.
pub local: Transform,

View file

@ -17,7 +17,9 @@ use {
///
/// * 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`].
/// * 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`]
///

View file

@ -13,7 +13,9 @@ use {
/// * 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 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`]
///
@ -39,6 +41,7 @@ use {
#[cfg_attr(
feature = "bevy-support",
derive(Component, Reflect),
require(GlobalTransform),
reflect(Component, Default, PartialEq, Debug)
)]
#[cfg_attr(

View file

@ -88,7 +88,6 @@ mod tests {
use bevy_math::{Quat, Vec3};
use crate::{
bundles::TransformBundle,
components::{GlobalTransform, Transform},
helper::TransformHelper,
plugins::TransformPlugin,
@ -122,7 +121,7 @@ mod tests {
let mut entity = None;
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 {
e.set_parent(entity);

View file

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

View file

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

View file

@ -29,7 +29,7 @@ fn setup_cube(
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands
.spawn(TransformBundle::default())
.spawn(Transform::default())
.with_children(|parent| {
// cube
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
visibility components, it will not be visible at all.
To fix this, you must use [`SpatialBundle`] over [`TransformBundle`],
as follows:
To fix this, you must use [`SpatialBundle`], as follows:
```rust,no_run
use bevy::prelude::*;
@ -73,7 +72,7 @@ fn setup_cube(
mut materials: ResMut<Assets<StandardMaterial>>,
) {
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,
// In addition to the Transform and GlobalTransform components.
.spawn(SpatialBundle::default())
@ -103,9 +102,8 @@ fn main() {
```
A similar problem occurs when the [`GlobalTransform`] component is missing.
However, when a parent [`GlobalTransform`] is missing,
it will simply prevent all transform propagation,
including when updating the [`Transform`] component of the child.
However, it will be automatically inserted whenever `Transform` is
inserted, as it's a required component.
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.
@ -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
[`ViewVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.ViewVisibility.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
[`Entity`]: https://docs.rs/bevy/*/bevy/ecs/entity/struct.Entity.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 {
// Create joint entities
let joint_0 = commands
.spawn(TransformBundle::from(Transform::from_xyz(
i as f32 * 1.5,
0.0,
i as f32 * 0.1,
)))
.id();
let joint_1 = commands
.spawn((AnimatedJoint, TransformBundle::IDENTITY))
.spawn(Transform::from_xyz(i as f32 * 1.5, 0.0, i as f32 * 0.1))
.id();
let joint_1 = commands.spawn((AnimatedJoint, Transform::IDENTITY)).id();
// Set joint_1 as a child of joint_0.
commands.entity(joint_0).add_children(&[joint_1]);

View file

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

View file

@ -380,7 +380,7 @@ fn spawn_tree(
}
// 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 rng = rand::thread_rng();
@ -426,7 +426,7 @@ fn spawn_tree(
};
// only insert the components necessary for the transform propagation
cmd = cmd.insert(TransformBundle::from(transform));
cmd = cmd.insert(transform);
cmd.id()
};