diff --git a/CHANGELOG.md b/CHANGELOG.md index b0b22548d6..21513856b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Buy and sell prices in tooltips when trading with a merchant now have colors. - Attacks now emit sound effects from the target on hit. - Crafting menu tabs +- Auto camera setting, making the game easier to play with one hand ### Changed diff --git a/assets/voxygen/i18n/en/hud/hud_settings.ron b/assets/voxygen/i18n/en/hud/hud_settings.ron index 23f3b83899..88e8572bd3 100644 --- a/assets/voxygen/i18n/en/hud/hud_settings.ron +++ b/assets/voxygen/i18n/en/hud/hud_settings.ron @@ -49,6 +49,7 @@ "hud.settings.auto_walk_behavior": "Auto walk behavior", "hud.settings.camera_clamp_behavior": "Camera clamp behavior", "hud.settings.stop_auto_walk_on_input": "Stop auto walk on movement", + "hud.settings.auto_camera": "Auto camera", "hud.settings.reset_gameplay": "Reset to Defaults", "hud.settings.view_distance": "View Distance", @@ -77,7 +78,7 @@ "hud.settings.particles": "Particles", "hud.settings.resolution": "Resolution", "hud.settings.bit_depth": "Bit Depth", - "hud.settings.refresh_rate": "Refresh Rate", + "hud.settings.refresh_rate": "Refresh Rate", "hud.settings.lighting_rendering_mode": "Lighting Rendering Mode", "hud.settings.lighting_rendering_mode.ashikhmin": "Type A - High ", "hud.settings.lighting_rendering_mode.blinnphong": "Type B - Medium", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 61f79ba9f5..a62fb5fed8 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -434,6 +434,7 @@ pub enum Event { ChangeAutoWalkBehavior(PressBehavior), ChangeCameraClampBehavior(PressBehavior), ChangeStopAutoWalkOnInput(bool), + ChangeAutoCamera(bool), CraftRecipe(String), InviteMember(Uid), AcceptInvite, @@ -2663,6 +2664,9 @@ impl Hud { settings_window::Event::ChangeStopAutoWalkOnInput(state) => { events.push(Event::ChangeStopAutoWalkOnInput(state)); }, + settings_window::Event::ChangeAutoCamera(state) => { + events.push(Event::ChangeAutoCamera(state)); + }, settings_window::Event::ResetInterfaceSettings => { self.show.help = false; self.show.debug = false; diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index e94cac45de..12f70555c1 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -243,6 +243,8 @@ widget_ids! { camera_clamp_behavior_list, stop_auto_walk_on_input_button, stop_auto_walk_on_input_label, + auto_camera_button, + auto_camera_label, } } @@ -350,6 +352,7 @@ pub enum Event { ChangeAutoWalkBehavior(PressBehavior), ChangeCameraClampBehavior(PressBehavior), ChangeStopAutoWalkOnInput(bool), + ChangeAutoCamera(bool), } #[derive(Clone)] @@ -1703,7 +1706,7 @@ impl<'a> Widget for SettingsWindow<'a> { self.imgs.checkbox_checked, ) .w_h(18.0, 18.0) - .right_from(state.ids.auto_walk_behavior_text, 80.0) + .down_from(state.ids.smooth_pan_toggle_button, 8.0) .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) .set(state.ids.stop_auto_walk_on_input_button, ui); @@ -1728,6 +1731,32 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.stop_auto_walk_on_input_label, ui); + // Auto-camera toggle + let auto_camera_toggle = ToggleButton::new( + self.global_state.settings.gameplay.auto_camera, + self.imgs.checkbox, + self.imgs.checkbox_checked, + ) + .w_h(18.0, 18.0) + .down_from(state.ids.stop_auto_walk_on_input_button, 8.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.auto_camera_button, ui); + + if self.global_state.settings.gameplay.auto_camera != auto_camera_toggle { + events.push(Event::ChangeAutoCamera( + !self.global_state.settings.gameplay.auto_camera, + )); + } + + Text::new(&self.localized_strings.get("hud.settings.auto_camera")) + .right_from(state.ids.auto_camera_button, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .graphics_for(state.ids.auto_camera_button) + .color(TEXT_COLOR) + .set(state.ids.auto_camera_label, ui); + // Reset the gameplay settings to the default settings if Button::image(self.imgs.button) .w_h(RESET_BUTTONS_WIDTH, RESET_BUTTONS_HEIGHT) diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index ebd005a004..9f018445e5 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -52,6 +52,17 @@ pub struct Camera { frustum: Frustum, } +fn clamp_and_modulate(ori: Vec3) -> Vec3 { + Vec3 { + // Wrap camera yaw + x: ori.x.rem_euclid(2.0 * PI), + // Clamp camera pitch to the vertical limits + y: ori.y.min(PI / 2.0 - 0.0001).max(-PI / 2.0 + 0.0001), + // Wrap camera roll + z: ori.z.rem_euclid(2.0 * PI), + } +} + impl Camera { /// Create a new `Camera` with default parameters. pub fn new(aspect: f32, mode: CameraMode) -> Self { @@ -156,23 +167,12 @@ impl Camera { } /// Set the orientation of the camera about its focus. - pub fn set_orientation(&mut self, ori: Vec3) { - // Wrap camera yaw - self.tgt_ori.x = ori.x.rem_euclid(2.0 * PI); - // Clamp camera pitch to the vertical limits - self.tgt_ori.y = ori.y.min(PI / 2.0 - 0.0001).max(-PI / 2.0 + 0.0001); - // Wrap camera roll - self.tgt_ori.z = ori.z.rem_euclid(2.0 * PI); - } + pub fn set_orientation(&mut self, ori: Vec3) { self.tgt_ori = clamp_and_modulate(ori); } /// Set the orientation of the camera about its focus without lerping. - pub fn set_ori_instant(&mut self, ori: Vec3) { - // Wrap camera yaw - self.ori.x = ori.x.rem_euclid(2.0 * PI); - // Clamp camera pitch to the vertical limits - self.ori.y = ori.y.min(PI / 2.0 - 0.0001).max(-PI / 2.0 + 0.0001); - // Wrap camera roll - self.ori.z = ori.z.rem_euclid(2.0 * PI); + pub fn set_orientation_instant(&mut self, ori: Vec3) { + self.set_orientation(ori); + self.ori = self.tgt_ori; } /// Zoom the camera by the given delta, limiting the input accordingly. @@ -249,15 +249,16 @@ impl Camera { Lerp::lerp(a, b + *offs, rate) }; - if smoothing_enabled { - self.set_ori_instant(Vec3::new( + let ori = if smoothing_enabled { + Vec3::new( lerp_angle(self.ori.x, self.tgt_ori.x, LERP_ORI_RATE * dt), Lerp::lerp(self.ori.y, self.tgt_ori.y, LERP_ORI_RATE * dt), lerp_angle(self.ori.z, self.tgt_ori.z, LERP_ORI_RATE * dt), - )); + ) } else { - self.set_ori_instant(self.tgt_ori) + self.tgt_ori }; + self.ori = clamp_and_modulate(ori); } pub fn interp_time(&self) -> f32 { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 646c4f1239..ff7c946075 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -732,6 +732,27 @@ impl PlayState for SessionState { // Get the current state of movement related inputs let input_vec = self.key_state.dir_vec(); let (axis_right, axis_up) = (input_vec[0], input_vec[1]); + let dt = global_state.clock.get_stable_dt().as_secs_f32(); + + // Auto camera mode + if global_state.settings.gameplay.auto_camera + && matches!( + self.scene.camera().get_mode(), + camera::CameraMode::ThirdPerson | camera::CameraMode::FirstPerson + ) + && input_vec.magnitude_squared() > 0.0 + { + let camera = self.scene.camera_mut(); + let ori = camera.get_orientation(); + camera.set_orientation_instant(Vec3::new( + ori.x + + input_vec.x + * (3.0 - input_vec.y * 1.5 * if is_aiming { 1.5 } else { 1.0 }) + * dt, + std::f32::consts::PI * if is_aiming { 0.015 } else { 0.1 }, + 0.0, + )); + } match self.scene.camera().get_mode() { camera::CameraMode::FirstPerson | camera::CameraMode::ThirdPerson => { @@ -753,7 +774,6 @@ impl PlayState for SessionState { let right = self.scene.camera().right(); let dir = right * axis_right + forward * axis_up; - let dt = global_state.clock.get_stable_dt().as_secs_f32(); if self.freefly_vel.magnitude_squared() > 0.01 { let new_vel = self.freefly_vel - self.freefly_vel.normalized() * (FREEFLY_DAMPING * dt); @@ -1406,6 +1426,10 @@ impl PlayState for SessionState { global_state.settings.gameplay.stop_auto_walk_on_input = state; global_state.settings.save_to_file_warn(); }, + HudEvent::ChangeAutoCamera(state) => { + global_state.settings.gameplay.auto_camera = state; + global_state.settings.save_to_file_warn(); + }, HudEvent::CraftRecipe(r) => { self.client.borrow_mut().craft_recipe(&r); }, diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 66244a2e07..55dcd9851a 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -503,6 +503,7 @@ pub struct GameplaySettings { pub auto_walk_behavior: PressBehavior, pub camera_clamp_behavior: PressBehavior, pub stop_auto_walk_on_input: bool, + pub auto_camera: bool, } impl Default for GameplaySettings { @@ -518,6 +519,7 @@ impl Default for GameplaySettings { auto_walk_behavior: PressBehavior::Toggle, camera_clamp_behavior: PressBehavior::Toggle, stop_auto_walk_on_input: true, + auto_camera: false, } } }