use bevy::{
camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
gizmos::transform_gizmo::{
TransformGizmoCamera, TransformGizmoFocus, TransformGizmoMode, TransformGizmoPlugin,
TransformGizmoSettings, TransformGizmoSpace,
},
picking::{pointer::PointerButton, Pickable},
prelude::*,
};
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
FreeCameraPlugin,
MeshPickingPlugin,
TransformGizmoPlugin,
))
.add_systems(Startup, setup)
.add_systems(Update, (gizmo_mode_keys, update_instructions))
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Text::new(
"Click an object to select it\n1: Translate | 2: Rotate | 3: Scale | X: World/Local space",
),
Node {
position_type: PositionType::Absolute,
top: px(12),
left: px(12),
..default()
},
InstructionsText,
));
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))),
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.3, 0.3))),
Pickable::IGNORE,
));
commands
.spawn((
Mesh3d(meshes.add(Cuboid::new(1.5, 0.15, 1.0))),
MeshMaterial3d(materials.add(Color::srgb(0.8, 0.3, 0.3))),
Transform::from_xyz(-2.0, 1.0, 0.0),
TransformGizmoFocus,
))
.observe(on_click_select)
.with_children(|parent| {
parent.spawn((
Mesh3d(meshes.add(Cuboid::new(0.1, 0.85, 0.1))),
MeshMaterial3d(materials.add(Color::srgb(0.6, 0.2, 0.2))),
Transform::from_xyz(-0.6, -0.5, 0.4),
Pickable::IGNORE,
));
parent.spawn((
Mesh3d(meshes.add(Cuboid::new(0.1, 0.85, 0.1))),
MeshMaterial3d(materials.add(Color::srgb(0.6, 0.2, 0.2))),
Transform::from_xyz(0.6, -0.5, 0.4),
Pickable::IGNORE,
));
parent.spawn((
Mesh3d(meshes.add(Cuboid::new(0.1, 0.85, 0.1))),
MeshMaterial3d(materials.add(Color::srgb(0.6, 0.2, 0.2))),
Transform::from_xyz(-0.6, -0.5, -0.4),
Pickable::IGNORE,
));
parent.spawn((
Mesh3d(meshes.add(Cuboid::new(0.1, 0.85, 0.1))),
MeshMaterial3d(materials.add(Color::srgb(0.6, 0.2, 0.2))),
Transform::from_xyz(0.6, -0.5, -0.4),
Pickable::IGNORE,
));
});
commands
.spawn((
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.8, 0.3))),
Transform::from_xyz(2.0, 0.5, 0.0),
))
.observe(on_click_select);
commands.spawn((
DirectionalLight {
shadow_maps_enabled: true,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, -0.8, 0.4, 0.0)),
));
commands.spawn((
Camera3d::default(),
Transform::from_xyz(0.0, 4.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
FreeCamera::default(),
TransformGizmoCamera,
));
}
fn on_click_select(
click: On<Pointer<Click>>,
mut commands: Commands,
existing: Query<Entity, With<TransformGizmoFocus>>,
) {
if click.button != PointerButton::Primary {
return;
}
for e in &existing {
commands.entity(e).remove::<TransformGizmoFocus>();
}
commands.entity(click.entity).insert(TransformGizmoFocus);
}
fn gizmo_mode_keys(
keyboard: Res<ButtonInput<KeyCode>>,
mut settings: ResMut<TransformGizmoSettings>,
) {
if keyboard.just_pressed(KeyCode::Digit1) {
settings.mode = TransformGizmoMode::Translate;
}
if keyboard.just_pressed(KeyCode::Digit2) {
settings.mode = TransformGizmoMode::Rotate;
}
if keyboard.just_pressed(KeyCode::Digit3) {
settings.mode = TransformGizmoMode::Scale;
}
if keyboard.just_pressed(KeyCode::KeyX) {
settings.space = match settings.space {
TransformGizmoSpace::World => TransformGizmoSpace::Local,
TransformGizmoSpace::Local => TransformGizmoSpace::World,
};
}
}
#[derive(Component)]
struct InstructionsText;
fn update_instructions(
settings: Res<TransformGizmoSettings>,
mut text: Single<&mut Text, With<InstructionsText>>,
) {
let mode_str = match settings.mode {
TransformGizmoMode::Translate => "Translate",
TransformGizmoMode::Rotate => "Rotate",
TransformGizmoMode::Scale => "Scale",
};
let space_str = match settings.space {
TransformGizmoSpace::World => "World",
TransformGizmoSpace::Local => "Local",
};
text.0 = format!(
"Click an object to select it\n1: Translate | 2: Rotate | 3: Scale | X: World/Local space\nMode: {mode_str} | Space: {space_str}"
);
}