Path: blob/main/examples/camera/free_camera_controller.rs
7223 views
//! This example showcases the default `FreeCamera` camera controller.1//!2//! The default `FreeCamera` controller is useful for exploring large scenes, debugging and editing purposes. To use it,3//! simply add the [`FreeCameraPlugin`] to your [`App`] and attach the [`FreeCamera`] component to the camera entity you4//! wish to control.5//!6//! ## Default Controls7//!8//! This controller has a simple 6-axis control scheme, and mouse controls for camera orientation. There are also9//! bindings for capturing the mouse, both while holding the button and toggle, a run feature that increases the10//! max speed, and scrolling changes the movement speed. All keybinds can be changed by editing the [`FreeCamera`]11//! component.12//!13//! | Default Key Binding | Action |14//! |:--------------------|:-----------------------|15//! | Mouse | Look around |16//! | Left click | Capture mouse (hold) |17//! | M | Capture mouse (toggle) |18//! | WASD | Horizontal movement |19//! | QE | Vertical movement |20//! | Left shift | Run |21//! | Scroll wheel | Change movement speed |22//!23//! The movement speed, sensitivity and friction can also be changed by the [`FreeCamera`] component.24//!25//! ## Example controls26//!27//! This example also provides a few extra keybinds to change the camera sensitivity, friction (how fast the camera28//! stops), scroll factor (how much scrolling changes speed) and enabling/disabling the controller.29//!30//! | Key Binding | Action |31//! |:------------|:-----------------------|32//! | Z | Decrease sensitivity |33//! | X | Increase sensitivity |34//! | C | Decrease friction |35//! | V | Increase friction |36//! | F | Decrease scroll factor |37//! | G | Increase scroll factor |38//! | B | Enable/Disable |3940use std::f32::consts::{FRAC_PI_4, PI};4142use bevy::{43camera_controller::free_camera::{FreeCamera, FreeCameraPlugin, FreeCameraState},44color::palettes::tailwind,45prelude::*,46};4748fn main() {49App::new()50.add_plugins(DefaultPlugins)51// Plugin that enables FreeCamera functionality52.add_plugins(FreeCameraPlugin)53// Example code plugins54.add_plugins((CameraPlugin, CameraSettingsPlugin, ScenePlugin))55.run();56}5758// Plugin that spawns the camera.59struct CameraPlugin;60impl Plugin for CameraPlugin {61fn build(&self, app: &mut App) {62app.add_systems(Startup, spawn_camera);63}64}6566fn spawn_camera(mut commands: Commands) {67commands.spawn((68Camera3d::default(),69Transform::from_xyz(0.0, 1.0, 0.0).looking_to(Vec3::X, Vec3::Y),70// This component stores all camera settings and state, which is used by the FreeCameraPlugin to71// control it. These properties can be changed at runtime, but beware the controller system is72// constantly using and modifying those values unless the enabled field is false.73FreeCamera {74sensitivity: 0.2,75friction: 25.0,76walk_speed: 3.0,77run_speed: 9.0,78..default()79},80));81}8283// Plugin that handles camera settings controls and information text84struct CameraSettingsPlugin;85impl Plugin for CameraSettingsPlugin {86fn build(&self, app: &mut App) {87app.add_systems(PostStartup, spawn_text)88.add_systems(Update, (update_camera_settings, update_text));89}90}9192#[derive(Component)]93struct InfoText;9495fn spawn_text(mut commands: Commands, free_camera_query: Query<&FreeCamera>) {96commands.spawn((97Node {98position_type: PositionType::Absolute,99top: px(-16),100left: px(12),101..default()102},103children![Text::new(format!(104"{}",105free_camera_query.single().unwrap()106))],107));108commands.spawn((109Node {110position_type: PositionType::Absolute,111bottom: px(12),112left: px(12),113..default()114},115children![Text::new(concat![116"Z/X: decrease/increase sensitivity\n",117"C/V: decrease/increase friction\n",118"F/G: decrease/increase scroll factor\n",119"B: enable/disable controller",120]),],121));122123// Mutable text marked with component124commands.spawn((125Node {126position_type: PositionType::Absolute,127top: px(12),128right: px(12),129..default()130},131children![(InfoText, Text::new(""))],132));133}134135fn update_camera_settings(136mut camera_query: Query<(&mut FreeCamera, &mut FreeCameraState)>,137input: Res<ButtonInput<KeyCode>>,138) {139let (mut free_camera, mut free_camera_state) = camera_query.single_mut().unwrap();140141if input.pressed(KeyCode::KeyZ) {142free_camera.sensitivity = (free_camera.sensitivity - 0.005).max(0.005);143}144if input.pressed(KeyCode::KeyX) {145free_camera.sensitivity += 0.005;146}147if input.pressed(KeyCode::KeyC) {148free_camera.friction = (free_camera.friction - 0.2).max(0.0);149}150if input.pressed(KeyCode::KeyV) {151free_camera.friction += 0.2;152}153if input.pressed(KeyCode::KeyF) {154free_camera.scroll_factor = (free_camera.scroll_factor - 0.02).max(0.02);155}156if input.pressed(KeyCode::KeyG) {157free_camera.scroll_factor += 0.02;158}159if input.just_pressed(KeyCode::KeyB) {160free_camera_state.enabled = !free_camera_state.enabled;161}162}163164fn update_text(165mut text_query: Query<&mut Text, With<InfoText>>,166camera_query: Query<(&FreeCamera, &FreeCameraState)>,167) {168let mut text = text_query.single_mut().unwrap();169170let (free_camera, free_camera_state) = camera_query.single().unwrap();171172text.0 = format!(173"Enabled: {},\nSensitivity: {:.03}\nFriction: {:.01}\nScroll factor: {:.02}\nWalk Speed: {:.02}\nRun Speed: {:.02}\nSpeed: {:.02}",174free_camera_state.enabled,175free_camera.sensitivity,176free_camera.friction,177free_camera.scroll_factor,178free_camera.walk_speed,179free_camera.run_speed,180free_camera_state.velocity.length(),181);182}183184// Plugin that spawns the scene and lighting.185struct ScenePlugin;186impl Plugin for ScenePlugin {187fn build(&self, app: &mut App) {188app.add_systems(Startup, (spawn_lights, spawn_world));189}190}191192fn spawn_lights(mut commands: Commands) {193// Main light194commands.spawn((195PointLight {196color: Color::from(tailwind::ORANGE_300),197shadows_enabled: true,198..default()199},200Transform::from_xyz(0.0, 3.0, 0.0),201));202// Light behind wall203commands.spawn((204PointLight {205color: Color::WHITE,206shadows_enabled: true,207..default()208},209Transform::from_xyz(-3.5, 3.0, 0.0),210));211// Light under floor212commands.spawn((213PointLight {214color: Color::from(tailwind::RED_300),215shadows_enabled: true,216..default()217},218Transform::from_xyz(0.0, -0.5, 0.0),219));220}221222fn spawn_world(223mut commands: Commands,224mut materials: ResMut<Assets<StandardMaterial>>,225mut meshes: ResMut<Assets<Mesh>>,226) {227let cube = meshes.add(Cuboid::new(1.0, 1.0, 1.0));228let floor = meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(10.0)));229let sphere = meshes.add(Sphere::new(0.5));230let wall = meshes.add(Cuboid::new(0.2, 4.0, 3.0));231232let blue_material = materials.add(Color::from(tailwind::BLUE_700));233let red_material = materials.add(Color::from(tailwind::RED_950));234let white_material = materials.add(Color::WHITE);235236// Top side of floor237commands.spawn((238Mesh3d(floor.clone()),239MeshMaterial3d(white_material.clone()),240));241// Under side of floor242commands.spawn((243Mesh3d(floor.clone()),244MeshMaterial3d(white_material.clone()),245Transform::from_xyz(0.0, -0.01, 0.0).with_rotation(Quat::from_rotation_x(PI)),246));247// Blue sphere248commands.spawn((249Mesh3d(sphere.clone()),250MeshMaterial3d(blue_material.clone()),251Transform::from_xyz(3.0, 1.5, 0.0),252));253// Tall wall254commands.spawn((255Mesh3d(wall.clone()),256MeshMaterial3d(white_material.clone()),257Transform::from_xyz(-3.0, 2.0, 0.0),258));259// Cube behind wall260commands.spawn((261Mesh3d(cube.clone()),262MeshMaterial3d(blue_material.clone()),263Transform::from_xyz(-4.2, 0.5, 0.0),264));265// Hidden cube under floor266commands.spawn((267Mesh3d(cube.clone()),268MeshMaterial3d(red_material.clone()),269Transform {270translation: Vec3::new(3.0, -2.0, 0.0),271rotation: Quat::from_euler(EulerRot::YXZEx, FRAC_PI_4, FRAC_PI_4, 0.0),272..default()273},274));275}276277278