 9dfd4e4b08
			
		
	
	
		9dfd4e4b08
		
	
	
	
	
		
			
			# Add Transform Examples - Adding examples for moving/rotating entities (with its own section) to resolve #2400 I've stumbled upon this project and been fiddling around a little. Saw the issue and thought I might just add some examples for the proposed transformations. Mind to check if I got the gist correctly and suggest anything I can improve?
		
			
				
	
	
		
			189 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use bevy::prelude::*;
 | |
| 
 | |
| // Define a marker for entities that should be changed via their global transform.
 | |
| #[derive(Component)]
 | |
| struct ChangeGlobal;
 | |
| 
 | |
| // Define a marker for entities that should be changed via their local transform.
 | |
| #[derive(Component)]
 | |
| struct ChangeLocal;
 | |
| 
 | |
| // Define a marker for entities that should move.
 | |
| #[derive(Component)]
 | |
| struct Move;
 | |
| 
 | |
| // Define a resource for the current movement direction;
 | |
| #[derive(Default)]
 | |
| struct Direction(Vec3);
 | |
| 
 | |
| // Define component to decide when an entity should be ignored by the movement systems.
 | |
| #[derive(Component)]
 | |
| struct ToggledBy(KeyCode);
 | |
| 
 | |
| fn main() {
 | |
|     App::new()
 | |
|         .add_plugins(DefaultPlugins)
 | |
|         .add_startup_system(setup)
 | |
|         .init_resource::<Direction>()
 | |
|         .add_system(move_cubes_according_to_global_transform)
 | |
|         .add_system(move_cubes_according_to_local_transform)
 | |
|         .add_system(update_directional_input)
 | |
|         .add_system(toggle_movement)
 | |
|         .run();
 | |
| }
 | |
| 
 | |
| // Startup system to setup the scene and spawn all relevant entities.
 | |
| fn setup(
 | |
|     mut commands: Commands,
 | |
|     mut meshes: ResMut<Assets<Mesh>>,
 | |
|     mut materials: ResMut<Assets<StandardMaterial>>,
 | |
|     asset_server: Res<AssetServer>,
 | |
| ) {
 | |
|     // To show the difference between a local transform (rotation, scale and position in respect to a given entity)
 | |
|     // and global transform (rotation, scale and position in respect to the base coordinate system of the visible scene)
 | |
|     // it's helpful to add multiple entities that are attached to each other.
 | |
|     // This way we'll see that the transform in respect to an entity's parent is different to the
 | |
|     // global transform within the visible scene.
 | |
|     // This example focuses on translation only to clearly demonstrate the differences.
 | |
| 
 | |
|     // Spawn a basic cube to have an entity as reference.
 | |
|     let mut main_entity = commands.spawn();
 | |
|     main_entity
 | |
|         .insert_bundle(PbrBundle {
 | |
|             mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
 | |
|             material: materials.add(Color::YELLOW.into()),
 | |
|             ..Default::default()
 | |
|         })
 | |
|         .insert(ChangeGlobal)
 | |
|         .insert(Move)
 | |
|         .insert(ToggledBy(KeyCode::Key1));
 | |
| 
 | |
|     // Spawn two entities as children above the original main entity.
 | |
|     // The red entity spawned here will be changed via its global transform
 | |
|     // where the green one will be changed via its local transform.
 | |
|     main_entity.with_children(|child_builder| {
 | |
|         // also see parenting example
 | |
|         child_builder
 | |
|             .spawn_bundle(PbrBundle {
 | |
|                 mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })),
 | |
|                 material: materials.add(Color::RED.into()),
 | |
|                 transform: Transform::from_translation(Vec3::Y - Vec3::Z),
 | |
|                 ..Default::default()
 | |
|             })
 | |
|             .insert(ChangeGlobal)
 | |
|             .insert(Move)
 | |
|             .insert(ToggledBy(KeyCode::Key2));
 | |
|         child_builder
 | |
|             .spawn_bundle(PbrBundle {
 | |
|                 mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })),
 | |
|                 material: materials.add(Color::GREEN.into()),
 | |
|                 transform: Transform::from_translation(Vec3::Y + Vec3::Z),
 | |
|                 ..Default::default()
 | |
|             })
 | |
|             .insert(ChangeLocal)
 | |
|             .insert(Move)
 | |
|             .insert(ToggledBy(KeyCode::Key3));
 | |
|     });
 | |
| 
 | |
|     // Spawn a camera looking at the entities to show what's happening in this example.
 | |
|     commands.spawn_bundle(PerspectiveCameraBundle {
 | |
|         transform: Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
 | |
|         ..Default::default()
 | |
|     });
 | |
| 
 | |
|     // Add a light source for better 3d visibility.
 | |
|     commands.spawn_bundle(PointLightBundle {
 | |
|         transform: Transform::from_translation(Vec3::splat(3.0)),
 | |
|         ..Default::default()
 | |
|     });
 | |
| 
 | |
|     // Add a UI cam and text to explain inputs and what is happening.
 | |
|     commands.spawn_bundle(UiCameraBundle::default());
 | |
|     commands.spawn_bundle(TextBundle {
 | |
|         text: Text::with_section(
 | |
|             "Press the arrow keys to move the cubes. Toggle movement for yellow (1), red (2) and green (3) cubes via number keys.
 | |
| 
 | |
| Notice how the green cube will translate further in respect to the yellow in contrast to the red cube.
 | |
| This is due to the use of its LocalTransform that is relative to the yellow cubes transform instead of the GlobalTransform as in the case of the red cube.
 | |
| The red cube is moved through its GlobalTransform and thus is unaffected by the yellows transform.",
 | |
|             TextStyle {
 | |
|                 font: asset_server.load("fonts/FiraSans-Bold.ttf"),
 | |
|                 font_size: 22.0,
 | |
|                 color: Color::WHITE,
 | |
|             },
 | |
|             TextAlignment {
 | |
|                 horizontal: HorizontalAlign::Left,
 | |
|                 ..Default::default()
 | |
|             }
 | |
|         ),
 | |
|         ..Default::default()});
 | |
| }
 | |
| 
 | |
| // This system will move all cubes that are marked as ChangeGlobal according to their global transform.
 | |
| fn move_cubes_according_to_global_transform(
 | |
|     mut cubes: Query<&mut GlobalTransform, (With<ChangeGlobal>, With<Move>)>,
 | |
|     direction: Res<Direction>,
 | |
|     timer: Res<Time>,
 | |
| ) {
 | |
|     for mut global_transform in cubes.iter_mut() {
 | |
|         global_transform.translation += direction.0 * timer.delta_seconds();
 | |
|     }
 | |
| }
 | |
| 
 | |
| // This system will move all cubes that are marked as ChangeLocal according to their local transform.
 | |
| fn move_cubes_according_to_local_transform(
 | |
|     mut cubes: Query<&mut Transform, (With<ChangeLocal>, With<Move>)>,
 | |
|     direction: Res<Direction>,
 | |
|     timer: Res<Time>,
 | |
| ) {
 | |
|     for mut transform in cubes.iter_mut() {
 | |
|         transform.translation += direction.0 * timer.delta_seconds();
 | |
|     }
 | |
| }
 | |
| 
 | |
| // This system updates a resource that defines in which direction the cubes should move.
 | |
| // The direction is defined by the input of arrow keys and is only in left/right and up/down direction.
 | |
| fn update_directional_input(mut direction: ResMut<Direction>, keyboard_input: Res<Input<KeyCode>>) {
 | |
|     let horizontal_movement = Vec3::X
 | |
|         * (keyboard_input.pressed(KeyCode::Right) as i32
 | |
|             - keyboard_input.pressed(KeyCode::Left) as i32) as f32;
 | |
|     let vertical_movement = Vec3::Y
 | |
|         * (keyboard_input.pressed(KeyCode::Up) as i32
 | |
|             - keyboard_input.pressed(KeyCode::Down) as i32) as f32;
 | |
|     direction.0 = horizontal_movement + vertical_movement;
 | |
| }
 | |
| 
 | |
| // This system enables and disables the movement for each entity if their assigned key is pressed.
 | |
| fn toggle_movement(
 | |
|     mut commands: Commands,
 | |
|     movable_entities: Query<(Entity, &Handle<StandardMaterial>, &ToggledBy), With<Move>>,
 | |
|     static_entities: Query<(Entity, &Handle<StandardMaterial>, &ToggledBy), Without<Move>>,
 | |
|     mut materials: ResMut<Assets<StandardMaterial>>,
 | |
|     keyboard_input: Res<Input<KeyCode>>,
 | |
| ) {
 | |
|     // Update the currently movable entities and remove their Move component if the assigned key was pressed to disable their movement.
 | |
|     // This will also make them transparent so they can be identified as 'disabled' in the scene.
 | |
|     for (entity, material_handle, toggled_by) in movable_entities.iter() {
 | |
|         if keyboard_input.just_pressed(toggled_by.0) {
 | |
|             materials
 | |
|                 .get_mut(material_handle)
 | |
|                 .unwrap()
 | |
|                 .base_color
 | |
|                 .set_a(0.5);
 | |
|             commands.entity(entity).remove::<Move>();
 | |
|         }
 | |
|     }
 | |
|     // Update the currently non-movable entities and add a Move component if the assigned key was pressed to enable their movement.
 | |
|     // This will also make them opaque so they can be identified as 'enabled' in the scene.
 | |
|     for (entity, material_handle, toggled_by) in static_entities.iter() {
 | |
|         if keyboard_input.just_pressed(toggled_by.0) {
 | |
|             materials
 | |
|                 .get_mut(material_handle)
 | |
|                 .unwrap()
 | |
|                 .base_color
 | |
|                 .set_a(1.0);
 | |
|             commands.entity(entity).insert(Move);
 | |
|         }
 | |
|     }
 | |
| }
 |