diff --git a/Cargo.toml b/Cargo.toml index 634ef2391d..0609e02c33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3251,7 +3251,18 @@ doc-scrape-examples = true [package.metadata.example.projection_zoom] name = "Projection Zoom" -description = "Shows how to zoom and orbit orthographic and perspective projection cameras." +description = "Shows how to zoom orthographic and perspective projection cameras." +category = "Camera" +wasm = true + +[[example]] +name = "camera_orbit" +path = "examples/camera/camera_orbit.rs" +doc-scrape-examples = true + +[package.metadata.example.camera_orbit] +name = "Camera Orbit" +description = "Shows how to orbit a static scene using pitch, yaw, and roll." category = "Camera" wasm = true diff --git a/examples/README.md b/examples/README.md index 542a827640..92e03582aa 100644 --- a/examples/README.md +++ b/examples/README.md @@ -254,8 +254,9 @@ Example | Description Example | Description --- | --- [2D top-down camera](../examples/camera/2d_top_down_camera.rs) | A 2D top-down camera smoothly following player movements +[Camera Orbit](../examples/camera/camera_orbit.rs) | Shows how to orbit a static scene using pitch, yaw, and roll. [First person view model](../examples/camera/first_person_view_model.rs) | A first-person camera that uses a world model and a view model with different field of views (FOV) -[Projection Zoom](../examples/camera/projection_zoom.rs) | Shows how to zoom and orbit orthographic and perspective projection cameras. +[Projection Zoom](../examples/camera/projection_zoom.rs) | Shows how to zoom orthographic and perspective projection cameras. ## Dev tools diff --git a/examples/camera/camera_orbit.rs b/examples/camera/camera_orbit.rs new file mode 100644 index 0000000000..c0e80e636a --- /dev/null +++ b/examples/camera/camera_orbit.rs @@ -0,0 +1,163 @@ +//! Shows how to orbit camera around a static scene using pitch, yaw, and roll. + +use std::{f32::consts::FRAC_PI_2, ops::Range}; + +use bevy::prelude::*; + +#[derive(Debug, Default, Resource)] +struct CameraSettings { + pub orbit_distance: f32, + // Multiply keyboard inputs by this factor + pub orbit_speed: f32, + // Clamp pitch to this range + pub pitch_range: Range, +} + +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 camera_settings: ResMut, + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // Limiting pitch stops some unexpected rotation past 90° up or down. + let pitch_limit = FRAC_PI_2 - 0.01; + + camera_settings.orbit_distance = 10.0; + camera_settings.orbit_speed = 1.0; + camera_settings.pitch_range = -pitch_limit..pitch_limit; + + 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( + "W or S: pitch", + TextStyle::default(), + )); + parent.spawn(TextBundle::from_section( + "A or D: yaw", + TextStyle::default(), + )); + parent.spawn(TextBundle::from_section( + "Q or E: roll", + TextStyle::default(), + )); + }); +} + +fn orbit( + mut camera: Query<&mut Transform, With>, + camera_settings: Res, + keyboard_input: Res>, + time: Res