can rotate view via TAB

This commit is contained in:
2025-03-19 21:03:06 +01:00
parent 7951d613c4
commit 3f4ffeb489
4 changed files with 73 additions and 25 deletions

View File

@@ -1,7 +1,7 @@
use avian3d::prelude::*; use avian3d::prelude::*;
use bevy::prelude::*; use bevy::prelude::*;
use crate::physics_layers::GameLayer; use crate::{controls::Controls, physics_layers::GameLayer, player::PlayerMovement};
#[derive(Component, Reflect, Debug)] #[derive(Component, Reflect, Debug)]
pub struct CameraTarget; pub struct CameraTarget;
@@ -9,38 +9,59 @@ pub struct CameraTarget;
#[derive(Component, Reflect, Debug)] #[derive(Component, Reflect, Debug)]
pub struct CameraArmRotation; pub struct CameraArmRotation;
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
pub struct CameraRotation(pub f32);
#[derive(Resource, Reflect, Debug, Default)]
#[reflect(Resource)]
pub struct CameraState {
pub cutscene: bool,
}
#[derive(Component, Reflect, Debug)] #[derive(Component, Reflect, Debug)]
pub struct MainCamera { pub struct MainCamera {
arm: Vec3, dir: Dir3,
distance: f32,
} }
impl MainCamera { impl MainCamera {
fn new(arm: Vec3) -> Self { fn new(arm: Vec3) -> Self {
Self { arm } let (dir, distance) = Dir3::new_and_length(arm).expect("invalid arm length");
Self { dir, distance }
} }
} }
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.register_type::<CameraRotation>();
app.register_type::<CameraState>();
app.init_resource::<CameraState>();
app.add_systems(Startup, startup); app.add_systems(Startup, startup);
app.add_systems(Update, update); app.add_systems(Update, (update, rotate_view));
} }
fn startup(mut commands: Commands) { fn startup(mut commands: Commands) {
commands.spawn(( commands.spawn((
Camera3d::default(), Camera3d::default(),
MainCamera::new(Vec3::new(0., 1., -12.)), MainCamera::new(Vec3::new(0., 1., -12.)),
CameraRotation(0.),
)); ));
} }
fn update( fn update(
mut cam: Query< mut cam: Query<
(&MainCamera, &mut Transform), (&MainCamera, &mut Transform, &CameraRotation),
(Without<CameraTarget>, Without<CameraArmRotation>), (Without<CameraTarget>, Without<CameraArmRotation>),
>, >,
target: Query<&GlobalTransform, (With<CameraTarget>, Without<CameraArmRotation>)>, target: Query<&GlobalTransform, (With<CameraTarget>, Without<CameraArmRotation>)>,
arm_rotation: Query<&Transform, With<CameraArmRotation>>, arm_rotation: Query<&Transform, With<CameraArmRotation>>,
spatial_query: SpatialQuery, spatial_query: SpatialQuery,
cam_state: Res<CameraState>,
) { ) {
if cam_state.cutscene {
return;
}
let Ok(target) = target.get_single().map(|t| t.translation()) else { let Ok(target) = target.get_single().map(|t| t.translation()) else {
return; return;
}; };
@@ -49,20 +70,13 @@ fn update(
return; return;
}; };
let Ok((camera, mut cam_transform)) = cam.get_single_mut() else { let Ok((camera, mut cam_transform, cam_rotation)) = cam.get_single_mut() else {
return; return;
}; };
let target_transform = Transform::from_translation(target) let direction = rotation.rotation * Quat::from_rotation_y(cam_rotation.0) * camera.dir;
* Transform::from_rotation(rotation.rotation)
* Transform::from_translation(camera.arm);
let ideal_cam_pos = target_transform.translation; let max_distance = camera.distance;
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 filter = SpatialQueryFilter::from_mask(LayerMask(GameLayer::Level.to_bits()));
let cam_pos = if let Some(first_hit) = let cam_pos = if let Some(first_hit) =
@@ -70,8 +84,29 @@ fn update(
{ {
target + (direction * first_hit.distance) target + (direction * first_hit.distance)
} else { } else {
ideal_cam_pos target + (direction * camera.distance)
}; };
*cam_transform = Transform::from_translation(cam_pos).looking_at(target, Vec3::Y); *cam_transform = Transform::from_translation(cam_pos).looking_at(target, Vec3::Y);
} }
fn rotate_view(
mut controls: ResMut<Controls>,
mut cam: Query<&mut CameraRotation>,
movement: Res<PlayerMovement>,
) {
let Ok(mut cam) = cam.get_single_mut() else {
return;
};
if !controls.keyboard_state.view_mode {
if movement.any_direction {
cam.0 = 0.;
}
return;
}
cam.0 += controls.keyboard_state.look_dir.x * -0.001;
controls.keyboard_state.look_dir = Vec2::ZERO;
}

View File

@@ -14,6 +14,7 @@ pub struct ControlState {
pub move_dir: Vec2, pub move_dir: Vec2,
pub look_dir: Vec2, pub look_dir: Vec2,
pub jump: bool, pub jump: bool,
pub view_mode: bool,
} }
#[derive(Resource, Debug, Default)] #[derive(Resource, Debug, Default)]
@@ -76,6 +77,7 @@ fn gamepad_controls(
move_dir: gamepad.left_stick(), move_dir: gamepad.left_stick(),
look_dir, look_dir,
jump: gamepad.pressed(GamepadButton::South), jump: gamepad.pressed(GamepadButton::South),
view_mode: gamepad.pressed(GamepadButton::East),
}; };
if gamepad.just_pressed(GamepadButton::North) { if gamepad.just_pressed(GamepadButton::North) {
@@ -120,6 +122,7 @@ fn keyboard_controls(keyboard: Res<ButtonInput<KeyCode>>, mut controls: ResMut<C
controls.keyboard_state.move_dir = direction; controls.keyboard_state.move_dir = direction;
controls.keyboard_state.jump = keyboard.pressed(KeyCode::Space); controls.keyboard_state.jump = keyboard.pressed(KeyCode::Space);
controls.keyboard_state.view_mode = keyboard.pressed(KeyCode::Tab);
} }
fn mouse_click(mut events: EventReader<MouseButtonInput>, mut commands: Commands) { fn mouse_click(mut events: EventReader<MouseButtonInput>, mut commands: Commands) {

View File

@@ -1,5 +1,5 @@
use crate::{ use crate::{
DebugVisuals, camera::CameraState,
tb_entities::{CameraTarget, CutsceneCamera, CutsceneCameraMovementEnd}, tb_entities::{CameraTarget, CutsceneCamera, CutsceneCameraMovementEnd},
}; };
use bevy::prelude::*; use bevy::prelude::*;
@@ -27,7 +27,7 @@ pub fn plugin(app: &mut App) {
fn on_start_cutscene( fn on_start_cutscene(
trigger: Trigger<StartCutscene>, trigger: Trigger<StartCutscene>,
mut res: ResMut<DebugVisuals>, mut cam_state: ResMut<CameraState>,
mut cutscene_state: ResMut<CutsceneState>, mut cutscene_state: ResMut<CutsceneState>,
cutscenes: Query<(&Transform, &CutsceneCamera, &Target), Without<Camera>>, cutscenes: Query<(&Transform, &CutsceneCamera, &Target), Without<Camera>>,
cutscene_movement: Query<(&Transform, &CutsceneCameraMovementEnd, &Target), Without<Camera>>, cutscene_movement: Query<(&Transform, &CutsceneCameraMovementEnd, &Target), Without<Camera>>,
@@ -35,7 +35,7 @@ fn on_start_cutscene(
) { ) {
let cutscene = trigger.event().0.clone(); let cutscene = trigger.event().0.clone();
res.cam_follow = false; cam_state.cutscene = true;
// asumes `name` and `targetname` are equal // asumes `name` and `targetname` are equal
let Some((t, _, target)) = cutscenes let Some((t, _, target)) = cutscenes
@@ -65,7 +65,7 @@ fn on_start_cutscene(
} }
fn update( fn update(
mut res: ResMut<DebugVisuals>, mut cam_state: ResMut<CameraState>,
mut cutscene_state: ResMut<CutsceneState>, mut cutscene_state: ResMut<CutsceneState>,
mut cam: Query<&mut Transform, With<Camera>>, mut cam: Query<&mut Transform, With<Camera>>,
time: Res<Time>, time: Res<Time>,
@@ -76,7 +76,7 @@ fn update(
camera_end, camera_end,
} = &mut *cutscene_state } = &mut *cutscene_state
{ {
res.cam_follow = false; cam_state.cutscene = true;
timer.tick(time.delta()); timer.tick(time.delta());
let t = Transform::from_translation( let t = Transform::from_translation(
@@ -93,7 +93,7 @@ fn update(
*cam.single_mut() = t; *cam.single_mut() = t;
if timer.finished() { if timer.finished() {
res.cam_follow = true; cam_state.cutscene = false;
*cutscene_state = CutsceneState::None; *cutscene_state = CutsceneState::None;
} }
} }

View File

@@ -34,14 +34,14 @@ struct PlayerSpawned {
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]
struct PlayerMovement { pub struct PlayerMovement {
any_direction: bool, pub any_direction: bool,
} }
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.init_resource::<PlayerSpawned>(); app.init_resource::<PlayerSpawned>();
app.init_resource::<PlayerMovement>(); app.init_resource::<PlayerMovement>();
app.add_systems(Startup, initial_grab_cursor); app.add_systems(Startup, (initial_grab_cursor, cursor_recenter));
app.add_systems( app.add_systems(
Update, Update,
( (
@@ -131,6 +131,10 @@ fn rotate_view(
// todo: Put the player head as a child of the rig to avoid this mess: // todo: Put the player head as a child of the rig to avoid this mess:
mut player: Query<&mut Transform, With<PlayerRig>>, mut player: Query<&mut Transform, With<PlayerRig>>,
) { ) {
if controls.keyboard_state.view_mode {
return;
}
for mut tr in &mut player { for mut tr in &mut player {
tr.rotate_y(controls.keyboard_state.look_dir.x * -0.001); tr.rotate_y(controls.keyboard_state.look_dir.x * -0.001);
@@ -142,6 +146,12 @@ fn rotate_view(
controls.keyboard_state.look_dir = Vec2::ZERO; controls.keyboard_state.look_dir = Vec2::ZERO;
} }
fn cursor_recenter(mut q_windows: Query<&mut Window, With<PrimaryWindow>>) {
let mut primary_window = q_windows.single_mut();
let center = Vec2::new(primary_window.width() / 2.0, primary_window.height() / 2.0);
primary_window.set_cursor_position(Some(center));
}
fn toggle_grab_cursor(window: &mut Window) { fn toggle_grab_cursor(window: &mut Window) {
match window.cursor_options.grab_mode { match window.cursor_options.grab_mode {
CursorGrabMode::None => { CursorGrabMode::None => {