mirror of
https://github.com/bevyengine/bevy
synced 2024-11-30 00:20:20 +00:00
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?
188 lines
7.7 KiB
Rust
188 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);
|
|
}
|
|
}
|
|
}
|