use bevy::prelude::*; use bevy::input::mouse::{MouseMotion, MouseWheel}; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, (camera_orbit_controls,)) .run(); } #[derive(Component)] struct CameraController { pub radius: f32, pub theta: f32, // azimuthal angle pub phi: f32, // polar angle pub last_mouse_pos: Option, } fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { // Camera with controller let radius = 30.0; let theta = 0.0; let phi = std::f32::consts::FRAC_PI_4; let cam_pos = spherical_to_cartesian(radius, theta, phi); commands.spawn(( Camera3dBundle { transform: Transform::from_translation(cam_pos).looking_at(Vec3::ZERO, Vec3::Y), ..default() }, CameraController { radius, theta, phi, last_mouse_pos: None, }, )); // Light commands.spawn(PointLightBundle { point_light: PointLight { intensity: 1500.0, shadows_enabled: true, ..default() }, transform: Transform::from_xyz(4.0, 8.0, 4.0), ..default() }); // Add a sphere at the origin using the new Sphere primitive let mesh = meshes.add(Mesh::from(Sphere { radius: 1.0, ..default() })); let material = materials.add(StandardMaterial { base_color: Color::BLUE, ..default() }); commands.spawn(PbrBundle { mesh, material, ..default() }); } fn spherical_to_cartesian(radius: f32, theta: f32, phi: f32) -> Vec3 { let x = radius * phi.sin() * theta.cos(); let y = radius * phi.cos(); let z = radius * phi.sin() * theta.sin(); Vec3::new(x, y, z) } fn camera_orbit_controls( mut query: Query<(&mut Transform, &mut CameraController)>, mut mouse_motion_events: EventReader, mut mouse_wheel_events: EventReader, mouse_button_input: Res>, windows: Query<&Window>, ) { let window = if let Some(window) = windows.iter().next() { window } else { return; }; let mut delta = Vec2::ZERO; for event in mouse_motion_events.read() { delta += event.delta; } let mut scroll = 0.0; for event in mouse_wheel_events.read() { scroll += event.y; } for (mut transform, mut controller) in query.iter_mut() { // Orbit (right mouse button) if mouse_button_input.pressed(MouseButton::Right) { let sensitivity = 0.01; controller.theta -= delta.x * sensitivity; controller.phi = (controller.phi - delta.y * sensitivity).clamp(0.05, std::f32::consts::PI - 0.05); } // Zoom if scroll.abs() > 0.0 { controller.radius = (controller.radius - scroll).clamp(3.0, 200.0); } // Update camera position let pos = spherical_to_cartesian(controller.radius, controller.theta, controller.phi); *transform = Transform::from_translation(pos).looking_at(Vec3::ZERO, Vec3::Y); } }