//! Shows how to orbit camera around a static scene using pitch, yaw, and roll. //! //! See also: `first_person_view_model` example, which does something similar but as a first-person //! camera view. use std::{f32::consts::FRAC_PI_2, ops::Range}; use bevy::{input::mouse::AccumulatedMouseMotion, prelude::*}; #[derive(Debug, Resource)] struct CameraSettings { pub orbit_distance: f32, pub pitch_speed: f32, // Clamp pitch to this range pub pitch_range: Range, pub roll_speed: f32, pub yaw_speed: f32, } impl Default for CameraSettings { fn default() -> Self { // Limiting pitch stops some unexpected rotation past 90° up or down. let pitch_limit = FRAC_PI_2 - 0.01; Self { // These values are completely arbitrary, chosen because they seem to produce // "sensible" results for this example. Adjust as required. orbit_distance: 20.0, pitch_speed: 0.003, pitch_range: -pitch_limit..pitch_limit, roll_speed: 1.0, yaw_speed: 0.004, } } } fn main() { App::new() .add_plugins(DefaultPlugins) .init_resource::() .add_systems(Startup, (setup, instructions)) .add_systems(Update, orbit) .run(); } /// Set up a simple 3D scene fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.spawn(( Name::new("Camera"), Camera3dBundle { transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() }, )); commands.spawn(( Name::new("Plane"), PbrBundle { mesh: meshes.add(Plane3d::default().mesh().size(5.0, 5.0)), material: materials.add(StandardMaterial { base_color: Color::srgb(0.3, 0.5, 0.3), // Turning off culling keeps the plane visible when viewed from beneath. cull_mode: None, ..default() }), ..default() }, )); commands.spawn(( Name::new("Cube"), PbrBundle { mesh: meshes.add(Cuboid::default()), material: materials.add(Color::srgb(0.8, 0.7, 0.6)), transform: Transform::from_xyz(1.5, 0.51, 1.5), ..default() }, )); commands.spawn(( Name::new("Light"), PointLightBundle { transform: Transform::from_xyz(3.0, 8.0, 5.0), ..default() }, )); } fn instructions(mut commands: Commands) { commands .spawn(( Name::new("Instructions"), NodeBundle { style: Style { align_items: AlignItems::Start, flex_direction: FlexDirection::Column, justify_content: JustifyContent::Start, width: Val::Percent(100.), ..default() }, ..default() }, )) .with_children(|parent| { parent.spawn(TextBundle::from_section( "Mouse up or down: pitch", TextStyle::default(), )); parent.spawn(TextBundle::from_section( "Mouse left or right: yaw", TextStyle::default(), )); parent.spawn(TextBundle::from_section( "Mouse buttons: roll", TextStyle::default(), )); }); } fn orbit( mut camera: Query<&mut Transform, With>, camera_settings: Res, mouse_buttons: Res>, mouse_motion: Res, time: Res