-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Freecam example #21477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Freecam example #21477
Changes from 10 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
f12d585
Makes first draft of example, registers example in Cargo.toml and REA…
Niyudi e8b6c50
refines scene and adds example documentation
Niyudi 8bae35b
implements first version with settings control
Niyudi 84e4432
renames example fiel to match other code, finishes text and camera co…
Niyudi be9a82a
removes unecessary feature flag
Niyudi 2477647
typos fixed
Niyudi ba64405
cargo fmt -all
Niyudi 16ba085
adds forgotten _ character...
Niyudi e159ac6
adds more visible properties and a few keybindings for editing contro…
Niyudi 491937c
cargo fmt --all
Niyudi e2c9d11
Fix typo
alice-i-cecile File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,267 @@ | ||
| //! This example showcases the default freecam camera controller. | ||
| //! | ||
| //! The default freecam controller is useful for exploring large scenes, debugging and editing purposes. To use it, | ||
| //! simply add the [`FreeCamPlugin`] to your [`App`] and attach the [`FreeCam`] component to the camera entity you | ||
| //! wish to control. | ||
| //! | ||
| //! ## Default Controls | ||
| //! | ||
| //! This controller has a simple 6-axis control scheme, and mouse controls for camera orientation. There are also | ||
| //! bindings for capturing the mouse, both while holding the button and toggle, a run feature that increases the | ||
| //! max speed, and scrolling changes the movement speed. All keybinds can be changed by editing the [`FreeCam`] | ||
| //! component. | ||
| //! | ||
| //! | Default Key Binding | Action | | ||
| //! |:--------------------|:-----------------------| | ||
| //! | Mouse | Look around | | ||
| //! | Left click | Capture mouse (hold) | | ||
| //! | M | Capture mouse (toggle) | | ||
| //! | WASD | Horizontal movement | | ||
| //! | QE | Vertical movement | | ||
| //! | Left shift | Run | | ||
| //! | Scroll wheel | Change movement speed | | ||
| //! | ||
| //! The movement speed, sensitivity and friction can also be changed by the [`FreeCam`] component. | ||
| //! | ||
| //! ## Example controls | ||
| //! | ||
| //! This example also provides a few extra keybinds to change the camera sensitivity, friction (how fast the camera | ||
| //! stops), scroll factor (how much scrolling changes speed) and enabling/disabling the controller. | ||
| //! | ||
| //! | Key Binding | Action | | ||
| //! |:------------|:-----------------------| | ||
| //! | Z | Decrease sensitivity | | ||
| //! | X | Increase snsitivity | | ||
| //! | C | Decrease friction | | ||
| //! | V | Increase friction | | ||
| //! | F | Decrease scroll factor | | ||
| //! | G | Increase scroll factor | | ||
| //! | B | Enable/Disable | | ||
|
|
||
| use std::f32::consts::{FRAC_PI_4, PI}; | ||
|
|
||
| use bevy::{ | ||
| camera_controller::free_cam::{FreeCam, FreeCamPlugin}, | ||
| color::palettes::tailwind, | ||
| prelude::*, | ||
| }; | ||
|
|
||
| fn main() { | ||
| App::new() | ||
| .add_plugins(DefaultPlugins) | ||
| // Plugin that enables freecam functionality | ||
| .add_plugins(FreeCamPlugin) | ||
| // Example code plugins | ||
| .add_plugins((CameraPlugin, CameraSettingsPlugin, ScenePlugin)) | ||
| .run(); | ||
| } | ||
|
|
||
| // Plugin that spawns the camera. | ||
| struct CameraPlugin; | ||
| impl Plugin for CameraPlugin { | ||
| fn build(&self, app: &mut App) { | ||
| app.add_systems(Startup, spawn_camera); | ||
| } | ||
| } | ||
|
|
||
| fn spawn_camera(mut commands: Commands) { | ||
| commands.spawn(( | ||
| Camera3d::default(), | ||
| Transform::from_xyz(0.0, 1.0, 0.0).looking_to(Vec3::X, Vec3::Y), | ||
| // This component stores all camera settings and state, which is used by the FreeCamPlugin to | ||
| // control it. These properties can be changed at runtime, but beware the controller system is | ||
| // constantly using and modifying those values unless the enabled field is false. | ||
| FreeCam { | ||
| sensitivity: 0.2, | ||
| friction: 25.0, | ||
| walk_speed: 3.0, | ||
| run_speed: 9.0, | ||
| ..default() | ||
| }, | ||
| )); | ||
| } | ||
|
|
||
| // Plugin that handles camera settings controls and information text | ||
| struct CameraSettingsPlugin; | ||
| impl Plugin for CameraSettingsPlugin { | ||
| fn build(&self, app: &mut App) { | ||
| app.add_systems(PostStartup, spawn_text) | ||
| .add_systems(Update, (update_camera_settings, update_text)); | ||
| } | ||
| } | ||
|
|
||
| #[derive(Component)] | ||
| struct InfoText; | ||
|
|
||
| fn spawn_text(mut commands: Commands, freecam_query: Query<&FreeCam>) { | ||
| commands.spawn(( | ||
| Node { | ||
| position_type: PositionType::Absolute, | ||
| top: px(-16), | ||
| left: px(12), | ||
| ..default() | ||
| }, | ||
| children![Text::new(format!("{}", freecam_query.single().unwrap()))], | ||
| )); | ||
| commands.spawn(( | ||
| Node { | ||
| position_type: PositionType::Absolute, | ||
| bottom: px(12), | ||
| left: px(12), | ||
| ..default() | ||
| }, | ||
| children![Text::new(concat![ | ||
| "Z/X: decrease/increase sensitivity\n", | ||
| "C/V: decrease/increase friction\n", | ||
| "F/G: decrease/increase scroll factor\n", | ||
| "B: enable/disable controller", | ||
| ]),], | ||
| )); | ||
|
|
||
| // Mutable text marked with component | ||
| commands.spawn(( | ||
| Node { | ||
| position_type: PositionType::Absolute, | ||
| top: px(12), | ||
| right: px(12), | ||
| ..default() | ||
| }, | ||
| children![(InfoText, Text::new(""))], | ||
| )); | ||
| } | ||
|
|
||
| fn update_camera_settings(mut camera_query: Query<&mut FreeCam>, input: Res<ButtonInput<KeyCode>>) { | ||
| let mut free_cam = camera_query.single_mut().unwrap(); | ||
|
|
||
| if input.pressed(KeyCode::KeyZ) { | ||
| free_cam.sensitivity = (free_cam.sensitivity - 0.005).max(0.005); | ||
| } | ||
| if input.pressed(KeyCode::KeyX) { | ||
| free_cam.sensitivity += 0.005; | ||
| } | ||
| if input.pressed(KeyCode::KeyC) { | ||
| free_cam.friction = (free_cam.friction - 0.2).max(0.0); | ||
| } | ||
| if input.pressed(KeyCode::KeyV) { | ||
| free_cam.friction += 0.2; | ||
| } | ||
| if input.pressed(KeyCode::KeyF) { | ||
| free_cam.scroll_factor = (free_cam.scroll_factor - 0.02).max(0.02); | ||
| } | ||
| if input.pressed(KeyCode::KeyG) { | ||
| free_cam.scroll_factor += 0.02; | ||
| } | ||
| if input.just_pressed(KeyCode::KeyB) { | ||
| free_cam.enabled = !free_cam.enabled; | ||
| } | ||
| } | ||
|
|
||
| fn update_text(mut text_query: Query<&mut Text, With<InfoText>>, camera_query: Query<&FreeCam>) { | ||
| let mut text = text_query.single_mut().unwrap(); | ||
|
|
||
| let free_cam = camera_query.single().unwrap(); | ||
|
|
||
| text.0 = format!( | ||
| "Enabled: {},\nSensitivity: {:.03}\nFriction: {:.01}\nScroll factor: {:.02}\nWalk Speed: {:.02}\nRun Speed: {:.02}\nSpeed: {:.02}", | ||
| free_cam.enabled, | ||
| free_cam.sensitivity, | ||
| free_cam.friction, | ||
| free_cam.scroll_factor, | ||
| free_cam.walk_speed, | ||
| free_cam.run_speed, | ||
| free_cam.velocity.length(), | ||
| ); | ||
| } | ||
|
|
||
| // Plugin that spawns the scene and lighting. | ||
| struct ScenePlugin; | ||
| impl Plugin for ScenePlugin { | ||
| fn build(&self, app: &mut App) { | ||
| app.add_systems(Startup, (spawn_lights, spawn_world)); | ||
| } | ||
| } | ||
|
|
||
| fn spawn_lights(mut commands: Commands) { | ||
| // Main light | ||
| commands.spawn(( | ||
| PointLight { | ||
| color: Color::from(tailwind::ORANGE_300), | ||
| shadows_enabled: true, | ||
| ..default() | ||
| }, | ||
| Transform::from_xyz(0.0, 3.0, 0.0), | ||
| )); | ||
| // Light behind wall | ||
| commands.spawn(( | ||
| PointLight { | ||
| color: Color::WHITE, | ||
| shadows_enabled: true, | ||
| ..default() | ||
| }, | ||
| Transform::from_xyz(-3.5, 3.0, 0.0), | ||
| )); | ||
| // Light under floor | ||
| commands.spawn(( | ||
| PointLight { | ||
| color: Color::from(tailwind::RED_300), | ||
| shadows_enabled: true, | ||
| ..default() | ||
| }, | ||
| Transform::from_xyz(0.0, -0.5, 0.0), | ||
| )); | ||
| } | ||
|
|
||
| fn spawn_world( | ||
| mut commands: Commands, | ||
| mut materials: ResMut<Assets<StandardMaterial>>, | ||
| mut meshes: ResMut<Assets<Mesh>>, | ||
| ) { | ||
| let cube = meshes.add(Cuboid::new(1.0, 1.0, 1.0)); | ||
| let floor = meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(10.0))); | ||
| let sphere = meshes.add(Sphere::new(0.5)); | ||
| let wall = meshes.add(Cuboid::new(0.2, 4.0, 3.0)); | ||
|
|
||
| let blue_material = materials.add(Color::from(tailwind::BLUE_700)); | ||
| let red_material = materials.add(Color::from(tailwind::RED_950)); | ||
| let white_material = materials.add(Color::WHITE); | ||
|
|
||
| // Top side of floor | ||
| commands.spawn(( | ||
| Mesh3d(floor.clone()), | ||
| MeshMaterial3d(white_material.clone()), | ||
| )); | ||
| // Under side of floor | ||
| commands.spawn(( | ||
| Mesh3d(floor.clone()), | ||
| MeshMaterial3d(white_material.clone()), | ||
| Transform::from_xyz(0.0, -0.01, 0.0).with_rotation(Quat::from_rotation_x(PI)), | ||
| )); | ||
| // Blue sphere | ||
| commands.spawn(( | ||
| Mesh3d(sphere.clone()), | ||
| MeshMaterial3d(blue_material.clone()), | ||
| Transform::from_xyz(3.0, 1.5, 0.0), | ||
| )); | ||
| // Tall wall | ||
| commands.spawn(( | ||
| Mesh3d(wall.clone()), | ||
| MeshMaterial3d(white_material.clone()), | ||
| Transform::from_xyz(-3.0, 2.0, 0.0), | ||
| )); | ||
| // Cube behind wall | ||
| commands.spawn(( | ||
| Mesh3d(cube.clone()), | ||
| MeshMaterial3d(blue_material.clone()), | ||
| Transform::from_xyz(-4.2, 0.5, 0.0), | ||
| )); | ||
| // Hidden cube under floor | ||
| commands.spawn(( | ||
| Mesh3d(cube.clone()), | ||
| MeshMaterial3d(red_material.clone()), | ||
| Transform { | ||
| translation: Vec3::new(3.0, -2.0, 0.0), | ||
| rotation: Quat::from_euler(EulerRot::YXZEx, FRAC_PI_4, FRAC_PI_4, 0.0), | ||
| ..default() | ||
| }, | ||
| )); | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.