* fix jitter while moving character controller * added transform interpolation to character controller
162 lines
4.4 KiB
Rust
162 lines
4.4 KiB
Rust
use avian3d::prelude::*;
|
|
use bevy::prelude::*;
|
|
|
|
use crate::{
|
|
GameState, control::ControlState, loading_assets::UIAssets, physics_layers::GameLayer,
|
|
};
|
|
|
|
#[derive(Component, Reflect, Debug)]
|
|
pub struct CameraTarget;
|
|
|
|
#[derive(Component, Reflect, Debug)]
|
|
pub struct CameraArmRotation;
|
|
|
|
/// Requested camera rotation based on various input sources (keyboard, gamepad)
|
|
#[derive(Component, Reflect, Debug, Default, Deref, DerefMut)]
|
|
#[reflect(Component)]
|
|
pub struct CameraRotationInput(pub Vec2);
|
|
|
|
#[derive(Resource, Reflect, Debug, Default)]
|
|
#[reflect(Resource)]
|
|
pub struct CameraState {
|
|
pub cutscene: bool,
|
|
pub look_around: bool,
|
|
}
|
|
|
|
#[derive(Component, Reflect, Debug, Default)]
|
|
struct CameraUi;
|
|
|
|
#[derive(Component, Reflect, Debug)]
|
|
#[reflect(Component)]
|
|
pub struct MainCamera {
|
|
dir: Dir3,
|
|
distance: f32,
|
|
}
|
|
|
|
impl MainCamera {
|
|
fn new(arm: Vec3) -> Self {
|
|
let (dir, distance) = Dir3::new_and_length(arm).expect("invalid arm length");
|
|
Self { dir, distance }
|
|
}
|
|
}
|
|
|
|
pub fn plugin(app: &mut App) {
|
|
app.register_type::<CameraRotationInput>();
|
|
app.register_type::<CameraState>();
|
|
app.register_type::<MainCamera>();
|
|
|
|
app.init_resource::<CameraState>();
|
|
app.add_systems(OnEnter(GameState::Playing), startup);
|
|
app.add_systems(
|
|
RunFixedMainLoop,
|
|
(update, update_ui, update_look_around, rotate_view)
|
|
.after(RunFixedMainLoopSystem::AfterFixedMainLoop)
|
|
.run_if(in_state(GameState::Playing)),
|
|
);
|
|
}
|
|
|
|
fn startup(mut commands: Commands) {
|
|
commands.spawn((
|
|
Camera3d::default(),
|
|
MainCamera::new(Vec3::new(0., 2.5, -13.)),
|
|
CameraRotationInput::default(),
|
|
));
|
|
}
|
|
|
|
fn update_look_around(controls: Res<ControlState>, mut cam_state: ResMut<CameraState>) {
|
|
let look_around = controls.view_mode;
|
|
|
|
if look_around != cam_state.look_around {
|
|
cam_state.look_around = look_around;
|
|
}
|
|
}
|
|
|
|
fn update_ui(
|
|
mut commands: Commands,
|
|
cam_state: Res<CameraState>,
|
|
assets: Res<UIAssets>,
|
|
query: Query<Entity, With<CameraUi>>,
|
|
) {
|
|
if cam_state.is_changed() {
|
|
let show_ui = cam_state.look_around || cam_state.cutscene;
|
|
|
|
if show_ui {
|
|
commands.spawn((
|
|
CameraUi,
|
|
Node {
|
|
margin: UiRect::top(Val::Px(20.))
|
|
.with_left(Val::Auto)
|
|
.with_right(Val::Auto),
|
|
justify_content: JustifyContent::Center,
|
|
..default()
|
|
},
|
|
children![(
|
|
Node {
|
|
display: Display::Block,
|
|
position_type: PositionType::Absolute,
|
|
..default()
|
|
},
|
|
ImageNode::new(assets.camera.clone()),
|
|
)],
|
|
));
|
|
} else {
|
|
for entity in query.iter() {
|
|
commands.entity(entity).despawn();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn update(
|
|
mut cam: Query<
|
|
(&MainCamera, &mut Transform, &CameraRotationInput),
|
|
(Without<CameraTarget>, Without<CameraArmRotation>),
|
|
>,
|
|
target_q: Single<&Transform, (With<CameraTarget>, Without<CameraArmRotation>)>,
|
|
arm_rotation: Single<&Transform, With<CameraArmRotation>>,
|
|
spatial_query: SpatialQuery,
|
|
cam_state: Res<CameraState>,
|
|
) {
|
|
if cam_state.cutscene {
|
|
return;
|
|
}
|
|
|
|
let target = target_q.translation;
|
|
|
|
let arm_tf = arm_rotation;
|
|
|
|
let Ok((camera, mut cam_transform, cam_rotation_input)) = cam.single_mut() else {
|
|
return;
|
|
};
|
|
|
|
let direction = arm_tf.rotation * Quat::from_rotation_y(cam_rotation_input.x) * camera.dir;
|
|
|
|
let max_distance = camera.distance;
|
|
|
|
let filter = SpatialQueryFilter::from_mask(LayerMask(GameLayer::Level.to_bits()));
|
|
let cam_pos = if let Some(first_hit) = spatial_query.cast_shape(
|
|
&Collider::sphere(0.5),
|
|
target,
|
|
Quat::IDENTITY,
|
|
direction,
|
|
&ShapeCastConfig::from_max_distance(max_distance),
|
|
&filter,
|
|
) {
|
|
let distance = first_hit.distance;
|
|
target + (direction * distance)
|
|
} else {
|
|
target + (direction * camera.distance)
|
|
};
|
|
|
|
*cam_transform = Transform::from_translation(cam_pos).looking_at(target, Vec3::Y);
|
|
}
|
|
|
|
fn rotate_view(controls: Res<ControlState>, mut cam: Single<&mut CameraRotationInput>) {
|
|
if !controls.view_mode {
|
|
cam.x = 0.;
|
|
return;
|
|
}
|
|
|
|
cam.0 += controls.look_dir * -0.001;
|
|
}
|