首先增加一个CameraController
,代码在bevy
的例程中也可以找到
use bevy::window::CursorGrabMode;
use bevy::{input::mouse::MouseMotion, prelude::*};use std::f32::consts::*;
use std::fmt;
pub const RADIANS_PER_DOT: f32 = 1.0 / 180.0;#[derive(Component)]
pub struct CameraController {pub enabled: bool,pub initialized: bool,pub sensitivity: f32,pub key_forward: KeyCode,pub key_back: KeyCode,pub key_left: KeyCode,pub key_right: KeyCode,pub key_up: KeyCode,pub key_down: KeyCode,pub key_run: KeyCode,pub mouse_key_enable_mouse: MouseButton,pub keyboard_key_enable_mouse: KeyCode,pub walk_speed: f32,pub run_speed: f32,pub friction: f32,pub pitch: f32,pub yaw: f32,pub velocity: Vec3,
}impl Default for CameraController {fn default() -> Self {Self {enabled: true,initialized: false,sensitivity: 1.0,key_forward: KeyCode::W,key_back: KeyCode::S,key_left: KeyCode::A,key_right: KeyCode::D,key_up: KeyCode::E,key_down: KeyCode::Q,key_run: KeyCode::ShiftLeft,mouse_key_enable_mouse: MouseButton::Left,keyboard_key_enable_mouse: KeyCode::M,walk_speed: 5.0,run_speed: 15.0,friction: 0.5,pitch: 0.0,yaw: 0.0,velocity: Vec3::ZERO,}}
}impl fmt::Display for CameraController {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {write!(f,"
Freecam Controls:MOUSE\t- Move camera orientation{:?}/{:?}\t- Enable mouse movement{:?}{:?}\t- forward/backward{:?}{:?}\t- strafe left/right{:?}\t- 'run'{:?}\t- up{:?}\t- down",self.mouse_key_enable_mouse,self.keyboard_key_enable_mouse,self.key_forward,self.key_back,self.key_left,self.key_right,self.key_run,self.key_up,self.key_down)}
}pub struct CameraControllerPlugin;impl Plugin for CameraControllerPlugin {fn build(&self, app: &mut App) {app.add_systems(Update, camera_controller);}
}fn camera_controller(time: Res<Time>,mut windows: Query<&mut Window>,mut mouse_events: EventReader<MouseMotion>,mouse_button_input: Res<Input<MouseButton>>,key_input: Res<Input<KeyCode>>,mut move_toggled: Local<bool>,mut query: Query<(&mut Transform, &mut CameraController), With<Camera>>,
) {let dt = time.delta_seconds();if let Ok((mut transform, mut options)) = query.get_single_mut() {if !options.initialized {let (yaw, pitch, _roll) = transform.rotation.to_euler(EulerRot::YXZ);options.yaw = yaw;options.pitch = pitch;options.initialized = true;}if !options.enabled {return;}let mut axis_input = Vec3::ZERO;if key_input.pressed(options.key_forward) {axis_input.z += 1.0;}if key_input.pressed(options.key_back) {axis_input.z -= 1.0;}if key_input.pressed(options.key_right) {axis_input.x += 1.0;}if key_input.pressed(options.key_left) {axis_input.x -= 1.0;}if key_input.pressed(options.key_up) {axis_input.y += 1.0;}if key_input.pressed(options.key_down) {axis_input.y -= 1.0;}if key_input.just_pressed(options.keyboard_key_enable_mouse) {*move_toggled = !*move_toggled;}if axis_input != Vec3::ZERO {let max_speed = if key_input.pressed(options.key_run) {options.run_speed} else {options.walk_speed};options.velocity = axis_input.normalize() * max_speed;} else {let friction = options.friction.clamp(0.0, 1.0);options.velocity *= 1.0 - friction;if options.velocity.length_squared() < 1e-6 {options.velocity = Vec3::ZERO;}}let forward = transform.forward();let right = transform.right();transform.translation += options.velocity.x * dt * right+ options.velocity.y * dt * Vec3::Y+ options.velocity.z * dt * forward;let mut mouse_delta = Vec2::ZERO;if mouse_button_input.pressed(options.mouse_key_enable_mouse) || *move_toggled {for mut window in &mut windows {if !window.focused {continue;}window.cursor.grab_mode = CursorGrabMode::Locked;window.cursor.visible = false;}for mouse_event in mouse_events.read() {mouse_delta += mouse_event.delta;}}if mouse_button_input.just_released(options.mouse_key_enable_mouse) {for mut window in &mut windows {window.cursor.grab_mode = CursorGrabMode::None;window.cursor.visible = true;}}if mouse_delta != Vec2::ZERO {options.pitch = (options.pitch - mouse_delta.y * RADIANS_PER_DOT * options.sensitivity).clamp(-PI / 2., PI / 2.);options.yaw -= mouse_delta.x * RADIANS_PER_DOT * options.sensitivity;transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, options.yaw, options.pitch);}}
}
再添加一个SceneSetup
,用于初始化场景和相机use bevy::prelude::*;
use bevy::app::{Plugin, App, Startup};use crate::camera::CameraController;pub struct SceneSetupPlugin;impl Plugin for SceneSetupPlugin {fn build(&self, app: &mut App) {app.add_systems(Startup, setup);}
}
fn setup(mut commands: Commands,mut meshes: ResMut<Assets<Mesh>>,mut materials: ResMut<Assets<StandardMaterial>>,
) {commands.spawn(PbrBundle {mesh: meshes.add(shape::Circle::new(4.0).into()),material: materials.add(Color::WHITE.into()),transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),..default()});commands.spawn(PbrBundle {mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),material: materials.add(Color::rgb_u8(124, 144, 255).into()),transform: Transform::from_xyz(0.0, 0.5, 0.0),..default()});commands.spawn(PointLightBundle {point_light: PointLight {intensity: 1500.0,shadows_enabled: true,..default()},transform: Transform::from_xyz(4.0, 8.0, 4.0),..default()});let camera_controller = CameraController::default();commands.spawn((Camera3dBundle {transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),..default()},camera_controller));
}