相机系统(Camera)

概述

学习目标

  • 理解 Bevy 相机系统的基本概念
  • 掌握相机控制器的使用
  • 了解相机投影的使用
  • 学会使用相机轨道和第一人称相机

前置知识要求

  • Bevy 快速入门
  • ECS 基础
  • 3D 开发基础
  • 输入处理基础

核心概念

什么是相机系统?

相机系统是 Bevy 中用于控制视角的功能。相机系统提供了多种相机类型和控制方式,包括轨道相机、第一人称相机、自定义投影等。

为什么需要相机系统?

  1. 视角控制:相机系统可以控制游戏的视角
  2. 相机移动:相机系统可以实现相机的移动和旋转
  3. 相机投影:相机系统可以自定义相机的投影方式
  4. 相机效果:相机系统可以实现相机特效(如屏幕抖动)

相机系统的核心组件

Bevy 相机系统包含以下核心组件:

  • Camera3d:3D 相机组件
  • Camera2d:2D 相机组件
  • Projection:投影组件
  • Transform:变换组件,用于控制相机位置和旋转

基础用法

相机控制器

使用相机控制器实现自由相机。

源代码文件bevy/examples/helpers/camera_controller.rs

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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),
));
}

关键要点

  • 使用 CameraProjection trait 实现自定义投影
  • 使用 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. 视角控制:控制游戏的视角
  2. 相机移动:实现相机的移动和旋转
  3. 相机投影:自定义相机的投影方式
  4. 相机效果:实现相机特效(如屏幕抖动)
  5. 相机切换:实现不同相机之间的切换

常见问题

问题 1:如何控制相机移动?

解决方案

  • 使用 CameraController 组件控制相机
  • 使用键盘和鼠标控制相机移动和旋转
  • 使用 Transform 控制相机位置和旋转

问题 2:如何实现相机轨道?

解决方案

  • 使用 AccumulatedMouseMotion 获取鼠标移动
  • 使用 Transform 控制相机位置和旋转
  • 使用 Quat::from_euler 创建旋转四元数

问题 3:如何自定义相机投影?

解决方案

  • 使用 CameraProjection trait 实现自定义投影
  • 使用 Projection::custom() 设置自定义投影
  • 使用 get_clip_from_view() 获取投影矩阵

性能考虑

  1. 相机控制器:相机控制器更新是高效的,可以频繁使用
  2. 相机投影:自定义投影计算可能较慢,应谨慎使用
  3. 相机效果:相机效果应适度使用,避免影响性能

相关资源

相关源代码文件

  • 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 渲染基础
  • 学习输入处理,了解输入系统

索引返回上级目录