|
| 1 | +//! Illustrates the difference between direction of a translation in respect to local object or |
| 2 | +//! global object Transform. |
| 3 | +
|
| 4 | +use bevy::{math::Vec3A, prelude::*}; |
| 5 | + |
| 6 | +// Define a marker for entities that should be changed via their global transform. |
| 7 | +#[derive(Component)] |
| 8 | +struct ChangeGlobal; |
| 9 | + |
| 10 | +// Define a marker for entities that should be changed via their local transform. |
| 11 | +#[derive(Component)] |
| 12 | +struct ChangeLocal; |
| 13 | + |
| 14 | +// Define a marker for entities that should move. |
| 15 | +#[derive(Component)] |
| 16 | +struct Move; |
| 17 | + |
| 18 | +// Define a resource for the current movement direction; |
| 19 | +#[derive(Resource, Default)] |
| 20 | +struct Direction(Vec3); |
| 21 | + |
| 22 | +// Define component to decide when an entity should be ignored by the movement systems. |
| 23 | +#[derive(Component)] |
| 24 | +struct ToggledBy(KeyCode); |
| 25 | + |
| 26 | +#[derive(Resource)] |
| 27 | +struct DynamicParent { |
| 28 | + green: Entity, |
| 29 | + yellow: Entity, |
| 30 | + has_hierarchy: bool, |
| 31 | +} |
| 32 | + |
| 33 | +fn main() { |
| 34 | + App::new() |
| 35 | + .add_plugins(DefaultPlugins) |
| 36 | + .add_startup_system(setup) |
| 37 | + .init_resource::<Direction>() |
| 38 | + .add_system(move_cubes_according_to_global_transform) |
| 39 | + .add_system(move_cubes_according_to_local_transform) |
| 40 | + .add_system(update_directional_input) |
| 41 | + .add_system(toggle_movement) |
| 42 | + .run(); |
| 43 | +} |
| 44 | + |
| 45 | +// Startup system to setup the scene and spawn all relevant entities. |
| 46 | +fn setup( |
| 47 | + mut commands: Commands, |
| 48 | + mut meshes: ResMut<Assets<Mesh>>, |
| 49 | + mut materials: ResMut<Assets<StandardMaterial>>, |
| 50 | + asset_server: Res<AssetServer>, |
| 51 | +) { |
| 52 | + let mut green = None; |
| 53 | + // To show the difference between a local transform (rotation, scale and |
| 54 | + // position in respect to a given entity) and global transform (rotation, |
| 55 | + // scale and position in respect to the base coordinate system of the visible scene) |
| 56 | + // it's helpful to add multiple entities that are attached to each other. |
| 57 | + // This way we'll see that the transform in respect to an entity's parent is different to the |
| 58 | + // global transform within the visible scene. |
| 59 | + // This example focuses on translation only to clearly demonstrate the differences. |
| 60 | + |
| 61 | + // Spawn a basic cube to have an entity as reference. |
| 62 | + let yellow = commands |
| 63 | + .spawn(( |
| 64 | + PbrBundle { |
| 65 | + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), |
| 66 | + material: materials.add(StandardMaterial { |
| 67 | + base_color: Color::YELLOW, |
| 68 | + alpha_mode: AlphaMode::Blend, |
| 69 | + ..Default::default() |
| 70 | + }), |
| 71 | + ..default() |
| 72 | + }, |
| 73 | + ChangeGlobal, |
| 74 | + Move, |
| 75 | + ToggledBy(KeyCode::Key1), |
| 76 | + )) |
| 77 | + // Spawn two entities as children above the original main entity. |
| 78 | + // The red entity spawned here will be changed via its global transform |
| 79 | + // where the green one will be changed via its local transform. |
| 80 | + .with_children(|child_builder| { |
| 81 | + // also see parenting example |
| 82 | + child_builder.spawn(( |
| 83 | + PbrBundle { |
| 84 | + mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })), |
| 85 | + material: materials.add(StandardMaterial { |
| 86 | + base_color: Color::RED, |
| 87 | + alpha_mode: AlphaMode::Blend, |
| 88 | + ..Default::default() |
| 89 | + }), |
| 90 | + transform: Transform::from_translation(Vec3::Y - Vec3::Z), |
| 91 | + ..default() |
| 92 | + }, |
| 93 | + ChangeGlobal, |
| 94 | + Move, |
| 95 | + ToggledBy(KeyCode::Key2), |
| 96 | + )); |
| 97 | + let green_content = child_builder |
| 98 | + .spawn(( |
| 99 | + PbrBundle { |
| 100 | + mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })), |
| 101 | + material: materials.add(StandardMaterial { |
| 102 | + base_color: Color::GREEN, |
| 103 | + alpha_mode: AlphaMode::Blend, |
| 104 | + ..Default::default() |
| 105 | + }), |
| 106 | + transform: Transform::from_translation(Vec3::Y + Vec3::Z), |
| 107 | + ..default() |
| 108 | + }, |
| 109 | + ChangeLocal, |
| 110 | + Move, |
| 111 | + ToggledBy(KeyCode::Key3), |
| 112 | + )) |
| 113 | + .id(); |
| 114 | + green = Some(green_content); |
| 115 | + }) |
| 116 | + .id(); |
| 117 | + commands.insert_resource(DynamicParent { |
| 118 | + green: green.unwrap(), |
| 119 | + yellow, |
| 120 | + has_hierarchy: true, |
| 121 | + }); |
| 122 | + |
| 123 | + // Spawn a camera looking at the entities to show what's happening in this example. |
| 124 | + commands.spawn(Camera3dBundle { |
| 125 | + transform: Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y), |
| 126 | + ..default() |
| 127 | + }); |
| 128 | + |
| 129 | + // Add a light source for better 3d visibility. |
| 130 | + commands.spawn(PointLightBundle { |
| 131 | + transform: Transform::from_translation(Vec3::splat(3.0)), |
| 132 | + ..default() |
| 133 | + }); |
| 134 | + |
| 135 | + // Add text to explain inputs and what is happening. |
| 136 | + commands.spawn(TextBundle::from_section( |
| 137 | + "Press the arrow keys to move the cubes. \ |
| 138 | + Toggle movement for yellow (1), red (2) and green (3) cubes via number keys.\n\n\ |
| 139 | + Notice how the green cube will translate further in respect to the \ |
| 140 | + yellow in contrast to the red cube.\n\ |
| 141 | + This is due to the use of its LocalTransform that is relative to the \ |
| 142 | + yellow cubes transform instead of the GlobalTransform as in the case of the red cube.\n\ |
| 143 | + The red cube is moved through its GlobalTransform and thus is \ |
| 144 | + unaffected by the yellows transform.\n\ |
| 145 | + You can toggle the parent relationship between the green and yellow cubes using (4)", |
| 146 | + TextStyle { |
| 147 | + font: asset_server.load("fonts/FiraSans-Bold.ttf"), |
| 148 | + font_size: 22.0, |
| 149 | + color: Color::WHITE, |
| 150 | + }, |
| 151 | + )); |
| 152 | +} |
| 153 | + |
| 154 | +// This system will move all cubes that are marked as ChangeGlobal according to their global transform. |
| 155 | +fn move_cubes_according_to_global_transform( |
| 156 | + mut cubes: Query<&mut GlobalTransform, (With<ChangeGlobal>, With<Move>)>, |
| 157 | + direction: Res<Direction>, |
| 158 | + time: Res<Time>, |
| 159 | +) { |
| 160 | + for mut global_transform in &mut cubes { |
| 161 | + *global_transform.translation_mut() += Vec3A::from(direction.0) * time.delta_seconds(); |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +// This system will move all cubes that are marked as ChangeLocal according to their local transform. |
| 166 | +fn move_cubes_according_to_local_transform( |
| 167 | + mut cubes: Query<&mut Transform, (With<ChangeLocal>, With<Move>)>, |
| 168 | + direction: Res<Direction>, |
| 169 | + time: Res<Time>, |
| 170 | +) { |
| 171 | + for mut transform in &mut cubes { |
| 172 | + transform.translation += direction.0 * time.delta_seconds(); |
| 173 | + } |
| 174 | +} |
| 175 | + |
| 176 | +// This system updates a resource that defines in which direction the cubes should move. |
| 177 | +// The direction is defined by the input of arrow keys and is only in left/right and up/down direction. |
| 178 | +fn update_directional_input(mut direction: ResMut<Direction>, keyboard_input: Res<Input<KeyCode>>) { |
| 179 | + let horizontal_movement = Vec3::X |
| 180 | + * (keyboard_input.pressed(KeyCode::Right) as i32 |
| 181 | + - keyboard_input.pressed(KeyCode::Left) as i32) as f32; |
| 182 | + let vertical_movement = Vec3::Y |
| 183 | + * (keyboard_input.pressed(KeyCode::Up) as i32 |
| 184 | + - keyboard_input.pressed(KeyCode::Down) as i32) as f32; |
| 185 | + direction.0 = horizontal_movement + vertical_movement; |
| 186 | +} |
| 187 | + |
| 188 | +// This system enables and disables the movement for each entity if their assigned key is pressed. |
| 189 | +fn toggle_movement( |
| 190 | + mut commands: Commands, |
| 191 | + movable_entities: Query<(Entity, &Handle<StandardMaterial>, &ToggledBy), With<Move>>, |
| 192 | + static_entities: Query<(Entity, &Handle<StandardMaterial>, &ToggledBy), Without<Move>>, |
| 193 | + mut materials: ResMut<Assets<StandardMaterial>>, |
| 194 | + keyboard_input: Res<Input<KeyCode>>, |
| 195 | + mut dynamic_parent: ResMut<DynamicParent>, |
| 196 | +) { |
| 197 | + if keyboard_input.just_pressed(KeyCode::Key4) { |
| 198 | + if dynamic_parent.has_hierarchy { |
| 199 | + commands |
| 200 | + .entity(dynamic_parent.green) |
| 201 | + .remove_parent_in_place(); |
| 202 | + } else { |
| 203 | + commands |
| 204 | + .entity(dynamic_parent.green) |
| 205 | + .set_parent_in_place(dynamic_parent.yellow); |
| 206 | + } |
| 207 | + dynamic_parent.has_hierarchy = !dynamic_parent.has_hierarchy; |
| 208 | + } |
| 209 | + // Update the currently movable entities and remove their Move component if |
| 210 | + // the assigned key was pressed to disable their movement. This will also |
| 211 | + // make them transparent so they can be identified as 'disabled' in the scene. |
| 212 | + for (entity, material_handle, toggled_by) in &movable_entities { |
| 213 | + if keyboard_input.just_pressed(toggled_by.0) { |
| 214 | + materials |
| 215 | + .get_mut(material_handle) |
| 216 | + .unwrap() |
| 217 | + .base_color |
| 218 | + .set_a(0.5); |
| 219 | + commands.entity(entity).remove::<Move>(); |
| 220 | + } |
| 221 | + } |
| 222 | + // Update the currently non-movable entities and add a Move component if |
| 223 | + // the assigned key was pressed to enable their movement. This will also |
| 224 | + // make them opaque so they can be identified as 'enabled' in the scene. |
| 225 | + for (entity, material_handle, toggled_by) in &static_entities { |
| 226 | + if keyboard_input.just_pressed(toggled_by.0) { |
| 227 | + materials |
| 228 | + .get_mut(material_handle) |
| 229 | + .unwrap() |
| 230 | + .base_color |
| 231 | + .set_a(1.0); |
| 232 | + commands.entity(entity).insert(Move); |
| 233 | + } |
| 234 | + } |
| 235 | +} |
0 commit comments