Transforms

Relevant official examples: transform, translation, rotation, 3d_rotation, scale, move_sprite, parenting, anything that spawns 2D or 3D objects.


First, a quick definition, if you are new to game development:

a Transform is what allows you to place an object in the game world. It is a combination of the object's "translation" (position/coordinates), "rotation", and "scale" (size adjustment).

You move objects around by modifying the translation, rotate them by modifying the rotation, and make them larger or smaller by modifying the scale.

// To simply position something at specific coordinates
let xf_pos567 = Transform::from_xyz(5.0, 6.0, 7.0);

// To scale an object, making it twice as big in all dimensions
let xf_scale = Transform::from_scale(Vec3::splat(2.0));

// To rotate an object in 2D (Z-axis rotation) by 30°
// (angles are in radians! must convert from degrees!)
let xf_rot2d = Transform::from_rotation(Quat::from_rotation_z((30.0_f32).to_radians()));

// 3D rotations can be complicated; explore the methods available on `Quat`

// Simple 3D rotation by Euler-angles (X, Y, Z)
let xf_rot2d = Transform::from_rotation(Quat::from_euler(
    EulerRot::XYZ,
    (20.0_f32).to_radians(),
    (10.0_f32).to_radians(),
    (30.0_f32).to_radians(),
));

// Everything:
let xf = Transform::from_xyz(1.0, 2.0, 3.0)
    .with_scale(Vec3::new(0.5, 0.5, 1.0))
    .with_rotation(Quat::from_rotation_y(0.125 * std::f32::consts::PI));

Transform Components

In Bevy, transforms are represented by two components: Transform and GlobalTransform.

Any Entity that represents an object in the game world needs to have both. All of Bevy's built-in bundle types include them.

If you are creating a custom entity without using those bundles, you can use one of the following to ensure you don't miss them:

fn spawn_special_entity(
    mut commands: Commands,
) {
    // create an entity that does not use one of the common Bevy bundles,
    // but still needs transforms and visibility
    commands.spawn((
        ComponentA,
        ComponentB,
        SpatialBundle {
            transform: Transform::from_scale(Vec3::splat(3.0)),
            visibility: Visibility {
                is_visible: false,
            },
            ..Default::default()
        },
    ));
}

Transform

Transform is what you typically work with. It is a struct containing the translation, rotation, and scale. To read or manipulate these values, access it from your systems using a query.

If the entity has a parent, the Transform component is relative to the parent. This means that the child object will move/rotate/scale along with the parent.

fn inflate_balloons(
    mut query: Query<&mut Transform, With<Balloon>>,
    keyboard: Res<Input<KeyCode>>,
) {
    // every time the Spacebar is pressed,
    // make all the balloons in the game bigger by 25%
    if keyboard.just_pressed(KeyCode::Space) {
        for mut transform in query.iter_mut() {
            transform.scale *= 1.25;
        }
    }
}

GlobalTransform

GlobalTransform represents the absolute global position in the world.

If the entity does not have a parent, then this will have the same value as the Transform.

The value of GlobalTransform is calculated/managed internally by Bevy. See below.

Transform Propagation

Beware: The two components are synchronized by a bevy-internal system (the "transform propagation system"), which runs in the PostUpdate stage.

When you mutate the Transform, the GlobalTransform is not updated immediately. They will be out-of-sync until the transform propagation system runs.

If you need to work with GlobalTransform directly, you should add your system to the PostUpdate stage and order it after the TransformSystem::TransformPropagate label.

/// Print the up-to-date global coordinates of the player as of **this frame**.
fn debug_globaltransform(
    query: Query<&GlobalTransform, With<Player>>,
) {
    let gxf = query.single();
    debug!("Player at: {:?}", gxf.translation());
}

fn main() {
    // the label to use for ordering
    use bevy::transform::TransformSystem;

    App::new()
        .add_plugins(DefaultPlugins)
        .add_system_to_stage(
            CoreStage::PostUpdate,
            debug_globaltransform
                .after(TransformSystem::TransformPropagate)
        )
        .run();
}