custom camera rig

* should not go into level geometry anymore
This commit is contained in:
2025-03-19 20:23:48 +01:00
parent b6efa7e85d
commit 7951d613c4
7 changed files with 117 additions and 106 deletions

View File

@@ -1,38 +1,77 @@
use avian3d::prelude::*;
use bevy::prelude::*;
use bevy_dolly::prelude::*;
impl GameCameraRig {
pub fn new_with_arm(arm: Vec3) -> Self {
Self(
CameraRig::builder()
.with(Position::new(Vec3::ZERO))
.with(Rotation::new(Quat::IDENTITY))
.with(Smooth::new_position(1.25).predictive(true))
.with(Smooth::new_rotation(0.5))
.with(Arm::new(arm))
.with(
LookAt::new(Vec3::ZERO + Vec3::Y)
.tracking_smoothness(0.2)
.tracking_predictive(true),
)
.build(),
)
}
use crate::physics_layers::GameLayer;
pub fn set_position_target(&mut self, target_position: Vec3, target_rotation: Quat) {
self.driver_mut::<Position>().position = target_position;
self.driver_mut::<Rotation>().rotation = target_rotation;
self.driver_mut::<LookAt>().target = target_position + Vec3::Y;
#[derive(Component, Reflect, Debug)]
pub struct CameraTarget;
#[derive(Component, Reflect, Debug)]
pub struct CameraArmRotation;
#[derive(Component, Reflect, Debug)]
pub struct MainCamera {
arm: Vec3,
}
impl MainCamera {
fn new(arm: Vec3) -> Self {
Self { arm }
}
}
/// A custom camera rig which combines smoothed movement with a look-at driver.
#[derive(Component, Debug, Deref, DerefMut)]
pub struct GameCameraRig(CameraRig);
// Turn the nested rig into a driver, so it can be used in another rig.
impl RigDriver for GameCameraRig {
fn update(&mut self, params: RigUpdateParams) -> Transform {
self.0.update(params.delta_time_seconds)
}
pub fn plugin(app: &mut App) {
app.add_systems(Startup, startup);
app.add_systems(Update, update);
}
fn startup(mut commands: Commands) {
commands.spawn((
Camera3d::default(),
MainCamera::new(Vec3::new(0., 1., -12.)),
));
}
fn update(
mut cam: Query<
(&MainCamera, &mut Transform),
(Without<CameraTarget>, Without<CameraArmRotation>),
>,
target: Query<&GlobalTransform, (With<CameraTarget>, Without<CameraArmRotation>)>,
arm_rotation: Query<&Transform, With<CameraArmRotation>>,
spatial_query: SpatialQuery,
) {
let Ok(target) = target.get_single().map(|t| t.translation()) else {
return;
};
let Ok(rotation) = arm_rotation.get_single() else {
return;
};
let Ok((camera, mut cam_transform)) = cam.get_single_mut() else {
return;
};
let target_transform = Transform::from_translation(target)
* Transform::from_rotation(rotation.rotation)
* Transform::from_translation(camera.arm);
let ideal_cam_pos = target_transform.translation;
let max_distance = camera.arm.length();
let Ok(direction) = Dir3::new(ideal_cam_pos - target) else {
return;
};
let filter = SpatialQueryFilter::from_mask(LayerMask(GameLayer::Level.to_bits()));
let cam_pos = if let Some(first_hit) =
spatial_query.cast_ray(target, direction, max_distance, false, &filter)
{
target + (direction * first_hit.distance)
} else {
ideal_cam_pos
};
*cam_transform = Transform::from_translation(cam_pos).looking_at(target, Vec3::Y);
}