相机系统(Camera)
概述
学习目标:
- 理解 Bevy 相机系统的基本概念
- 掌握相机控制器的使用
- 了解相机投影的使用
- 学会使用相机轨道和第一人称相机
前置知识要求:
- Bevy 快速入门
- ECS 基础
- 3D 开发基础
- 输入处理基础
核心概念
什么是相机系统?
相机系统是 Bevy 中用于控制视角的功能。相机系统提供了多种相机类型和控制方式,包括轨道相机、第一人称相机、自定义投影等。
为什么需要相机系统?
- 视角控制:相机系统可以控制游戏的视角
- 相机移动:相机系统可以实现相机的移动和旋转
- 相机投影:相机系统可以自定义相机的投影方式
- 相机效果:相机系统可以实现相机特效(如屏幕抖动)
相机系统的核心组件
Bevy 相机系统包含以下核心组件:
- Camera3d:3D 相机组件
- Camera2d:2D 相机组件
- Projection:投影组件
- Transform:变换组件,用于控制相机位置和旋转
基础用法
相机控制器
使用相机控制器实现自由相机。
源代码文件:bevy/examples/helpers/camera_controller.rs
代码示例:
use bevy::{
input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseScrollUnit},
prelude::*,
window::{CursorGrabMode, CursorOptions},
};
use std::{f32::consts::*, fmt};
/// 自由相机风格的相机控制器插件。
pub struct CameraControllerPlugin;
impl Plugin for CameraControllerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, run_camera_controller);
}
}
/// 相机控制器组件。
#[derive(Component)]
pub struct CameraController {
/// 当为 `true` 时启用此 [`CameraController`]。
pub enabled: bool,
/// 指示此控制器是否已由 [`CameraControllerPlugin`] 初始化。
pub initialized: bool,
/// 俯仰和偏航旋转速度的乘数。
pub sensitivity: f32,
/// 向前平移的 [`KeyCode`]。
pub key_forward: KeyCode,
/// 向后平移的 [`KeyCode`]。
pub key_back: KeyCode,
/// 向左平移的 [`KeyCode`]。
pub key_left: KeyCode,
/// 向右平移的 [`KeyCode`]。
pub key_right: KeyCode,
/// 向上平移的 [`KeyCode`]。
pub key_up: KeyCode,
/// 向下平移的 [`KeyCode`]。
pub key_down: KeyCode,
/// 使用 [`run_speed`](cameracontroller::run_speed) 而不是
/// [`walk_speed`](cameracontroller::walk_speed) 进行平移的 [`KeyCode`]。
pub key_run: KeyCode,
/// 用于抓取鼠标焦点的 [`MouseButton`]。
pub mouse_key_cursor_grab: MouseButton,
/// 用于抓取键盘焦点的 [`KeyCode`]。
pub keyboard_key_toggle_cursor_grab: KeyCode,
/// 未修改平移速度的乘数。
pub walk_speed: f32,
/// 运行平移速度的乘数。
pub run_speed: f32,
/// 鼠标滚轮修改 [`walk_speed`](cameracontroller::walk_speed)
/// 和 [`run_speed`](cameracontroller::run_speed) 的乘数。
pub scroll_factor: f32,
/// 用于随时间指数衰减 [`velocity`](cameracontroller::velocity) 的摩擦因子。
pub friction: f32,
/// 此 [`CameraController`] 的俯仰旋转。
pub pitch: f32,
/// 此 [`CameraController`] 的偏航旋转。
pub yaw: f32,
/// 此 [`CameraController`] 的平移速度。
pub velocity: Vec3,
}
impl Default for CameraController {
fn default() -> Self {
Self {
enabled: true,
initialized: false,
sensitivity: 1.0,
key_forward: KeyCode::KeyW,
key_back: KeyCode::KeyS,
key_left: KeyCode::KeyA,
key_right: KeyCode::KeyD,
key_up: KeyCode::KeyE,
key_down: KeyCode::KeyQ,
key_run: KeyCode::ShiftLeft,
mouse_key_cursor_grab: MouseButton::Left,
keyboard_key_toggle_cursor_grab: KeyCode::KeyM,
walk_speed: 5.0,
run_speed: 15.0,
scroll_factor: 0.1,
friction: 0.5,
pitch: 0.0,
yaw: 0.0,
velocity: Vec3::ZERO,
}
}
}关键要点:
- 使用
CameraController组件控制相机 - 使用
CameraControllerPlugin插件添加相机控制器系统 - 使用键盘和鼠标控制相机移动和旋转
- 使用
CursorGrabMode控制鼠标抓取模式
说明: 相机控制器是相机系统的基础。通过使用相机控制器,可以实现自由相机,控制游戏的视角。
相机轨道
使用相机轨道实现轨道相机。
源代码文件:bevy/examples/camera/camera_orbit.rs
代码示例:
use std::{f32::consts::FRAC_PI_2, ops::Range};
use bevy::{input::mouse::AccumulatedMouseMotion, prelude::*};
#[derive(Debug, Resource)]
struct CameraSettings {
pub orbit_distance: f32,
pub pitch_speed: f32,
// 将俯仰限制在此范围内
pub pitch_range: Range<f32>,
pub roll_speed: f32,
pub yaw_speed: f32,
}
impl Default for CameraSettings {
fn default() -> Self {
// 限制俯仰可以防止超过 90° 向上或向下的意外旋转。
let pitch_limit = FRAC_PI_2 - 0.01;
Self {
// 这些值完全是任意的,选择它们是因为它们似乎为这个示例产生"合理"的结果。
// 根据需要调整。
orbit_distance: 20.0,
pitch_speed: 0.003,
pitch_range: -pitch_limit..pitch_limit,
roll_speed: 1.0,
yaw_speed: 0.004,
}
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<CameraSettings>()
.add_systems(Startup, (setup, instructions))
.add_systems(Update, orbit)
.run();
}
fn orbit(
mut camera: Single<&mut Transform, With<Camera>>,
settings: Res<CameraSettings>,
mouse_motion: Res<AccumulatedMouseMotion>,
mouse_buttons: Res<ButtonInput<MouseButton>>,
) {
// 根据鼠标移动更新俯仰和偏航
let mut pitch_delta = 0.0;
let mut yaw_delta = 0.0;
let mut roll_delta = 0.0;
if mouse_motion.is_changed() {
let delta = mouse_motion.delta();
pitch_delta -= delta.y * settings.pitch_speed;
yaw_delta -= delta.x * settings.yaw_speed;
}
// 根据鼠标按钮更新滚动
if mouse_buttons.pressed(MouseButton::Left) {
roll_delta += settings.roll_speed;
}
if mouse_buttons.pressed(MouseButton::Right) {
roll_delta -= settings.roll_speed;
}
// 更新相机变换
let mut pitch = camera.rotation.to_euler(EulerRot::YXZ).1;
let mut yaw = camera.rotation.to_euler(EulerRot::YXZ).0;
let mut roll = camera.rotation.to_euler(EulerRot::YXZ).2;
pitch += pitch_delta;
pitch = pitch.clamp(settings.pitch_range.start, settings.pitch_range.end);
yaw += yaw_delta;
roll += roll_delta;
// 计算相机位置
let rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
let position = rotation * Vec3::new(0.0, 0.0, settings.orbit_distance);
camera.translation = position;
camera.rotation = rotation;
}关键要点:
- 使用
AccumulatedMouseMotion获取鼠标移动 - 使用
ButtonInput<MouseButton>获取鼠标按钮状态 - 使用
Transform控制相机位置和旋转 - 使用
Quat::from_euler创建旋转四元数
说明: 相机轨道是相机系统的重要功能。通过使用相机轨道,可以实现轨道相机,围绕场景旋转相机。
自定义投影
使用自定义投影实现特殊投影效果。
源代码文件:bevy/examples/camera/custom_projection.rs
代码示例:
use bevy::camera::CameraProjection;
use bevy::prelude::*;
/// 类似于透视投影,但消失点不居中。
#[derive(Debug, Clone)]
struct ObliquePerspectiveProjection {
horizontal_obliqueness: f32,
vertical_obliqueness: f32,
perspective: PerspectiveProjection,
}
/// 为我们的自定义投影实现 [`CameraProjection`] trait:
impl CameraProjection for ObliquePerspectiveProjection {
fn get_clip_from_view(&self) -> Mat4 {
let mut mat = self.perspective.get_clip_from_view();
mat.col_mut(2)[0] = self.horizontal_obliqueness;
mat.col_mut(2)[1] = self.vertical_obliqueness;
mat
}
fn get_clip_from_view_for_sub(&self, sub_view: &bevy::camera::SubCameraView) -> Mat4 {
let mut mat = self.perspective.get_clip_from_view_for_sub(sub_view);
mat.col_mut(2)[0] = self.horizontal_obliqueness;
mat.col_mut(2)[1] = self.vertical_obliqueness;
mat
}
fn update(&mut self, width: f32, height: f32) {
self.perspective.update(width, height);
}
fn far(&self) -> f32 {
self.perspective.far
}
fn get_frustum_corners(&self, z_near: f32, z_far: f32) -> [Vec3A; 8] {
self.perspective.get_frustum_corners(z_near, z_far)
}
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Camera3d::default(),
// 使用我们的自定义投影:
Projection::custom(ObliquePerspectiveProjection {
horizontal_obliqueness: 0.2,
vertical_obliqueness: 0.6,
perspective: PerspectiveProjection::default(),
}),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
));
}关键要点:
- 使用
CameraProjectiontrait 实现自定义投影 - 使用
Projection::custom()设置自定义投影 - 使用
get_clip_from_view()获取投影矩阵 - 使用
update()更新投影参数
说明: 自定义投影是相机系统的高级功能。通过使用自定义投影,可以实现特殊的投影效果,如斜投影。
进阶用法
屏幕抖动
使用屏幕抖动实现相机特效。
源代码文件:bevy/examples/camera/2d_screen_shake.rs
关键信息:
- 使用
Transform控制相机位置 - 使用随机数生成抖动效果
- 使用时间控制抖动持续时间
- 使用衰减函数平滑抖动
说明: 屏幕抖动是相机系统的重要功能。通过使用屏幕抖动,可以实现相机特效,增强游戏体验。
第一人称相机
使用第一人称相机实现第一人称视角。
源代码文件:bevy/examples/camera/first_person_view_model.rs
关键信息:
- 使用
CameraController控制相机 - 使用鼠标控制视角
- 使用键盘控制移动
- 使用
CursorGrabMode控制鼠标抓取
说明: 第一人称相机是相机系统的重要功能。通过使用第一人称相机,可以实现第一人称视角,增强游戏沉浸感。
实际应用
在游戏开发中的应用场景
相机系统在游戏开发中有广泛的应用:
- 视角控制:控制游戏的视角
- 相机移动:实现相机的移动和旋转
- 相机投影:自定义相机的投影方式
- 相机效果:实现相机特效(如屏幕抖动)
- 相机切换:实现不同相机之间的切换
常见问题
问题 1:如何控制相机移动?
解决方案:
- 使用
CameraController组件控制相机 - 使用键盘和鼠标控制相机移动和旋转
- 使用
Transform控制相机位置和旋转
问题 2:如何实现相机轨道?
解决方案:
- 使用
AccumulatedMouseMotion获取鼠标移动 - 使用
Transform控制相机位置和旋转 - 使用
Quat::from_euler创建旋转四元数
问题 3:如何自定义相机投影?
解决方案:
- 使用
CameraProjectiontrait 实现自定义投影 - 使用
Projection::custom()设置自定义投影 - 使用
get_clip_from_view()获取投影矩阵
性能考虑
- 相机控制器:相机控制器更新是高效的,可以频繁使用
- 相机投影:自定义投影计算可能较慢,应谨慎使用
- 相机效果:相机效果应适度使用,避免影响性能
相关资源
相关源代码文件:
bevy/examples/helpers/camera_controller.rs- 相机控制器示例bevy/examples/camera/camera_orbit.rs- 相机轨道示例bevy/examples/camera/custom_projection.rs- 自定义投影示例bevy/examples/camera/2d_screen_shake.rs- 屏幕抖动示例bevy/examples/camera/first_person_view_model.rs- 第一人称相机示例
官方文档链接:
进一步学习建议:
- 学习 3D 开发,了解 3D 渲染基础
- 学习输入处理,了解输入系统
索引:返回上级目录