首先增加一个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));
}