From 33bdcf0922e21943a4446427a122fcdbae0341b7 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Mon, 16 Sep 2024 18:07:12 +0200 Subject: [PATCH] refactor some modules --- src/player/camera/control.rs | 77 ++++++++++++++++++++ src/player/camera/mod.rs | 79 ++------------------- src/player/initialize.rs | 2 +- src/player/interactions/components.rs | 63 ---------------- src/player/interactions/interact.rs | 5 +- src/player/interactions/mod.rs | 69 +++++++++++++++--- src/player/interactions/prompt.rs | 3 +- src/player/interactions/update_available.rs | 5 +- 8 files changed, 147 insertions(+), 156 deletions(-) create mode 100644 src/player/camera/control.rs delete mode 100644 src/player/interactions/components.rs diff --git a/src/player/camera/control.rs b/src/player/camera/control.rs new file mode 100644 index 00000000..6d81b506 --- /dev/null +++ b/src/player/camera/control.rs @@ -0,0 +1,77 @@ +use std::f32::consts::FRAC_PI_2; + +use crate::{dialog::conditions::dialog_running, player::Player}; +use bevy::{app::RunFixedMainLoop, prelude::*, time::run_fixed_main_schedule}; +use leafwing_input_manager::prelude::*; + +use super::{PlayerCamera, PlayerCameraConfig}; + +pub(super) fn plugin(app: &mut App) { + app.add_plugins(InputManagerPlugin::::default()); + app.add_systems( + RunFixedMainLoop, + rotate_camera + .run_if(not(dialog_running)) + .before(run_fixed_main_schedule), + ); + app.add_systems(Update, follow_player); +} + +#[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)] +#[actionlike(DualAxis)] +pub enum CameraAction { + RotateCamera, +} + +impl CameraAction { + pub fn default_input_map() -> InputMap { + let mut input_map = InputMap::default(); + + // Default gamepad input bindings + input_map.insert_dual_axis(CameraAction::RotateCamera, GamepadStick::LEFT); + + // Default kbm input bindings + input_map.insert_dual_axis(CameraAction::RotateCamera, MouseMove::default()); + + input_map + } +} + +fn follow_player( + mut q_camera: Query<&mut Transform, With>, + q_player: Query<&Transform, (With, Without)>, +) { + // Use `Transform` instead of `Position`` because we want the camera to move + // smoothly, so we use the interpolated transform of the player. + let Ok(player_transform) = q_player.get_single() else { + return; + }; + let Ok(mut camera_transform) = q_camera.get_single_mut() else { + return; + }; + let height_offset = 0.5; + camera_transform.translation = + player_transform.translation + player_transform.up() * height_offset; +} + +fn rotate_camera( + mut character_query: Query<( + &mut Transform, + &ActionState, + &PlayerCameraConfig, + )>, +) { + for (mut transform, action_state, config) in &mut character_query { + let delta = action_state.axis_pair(&CameraAction::RotateCamera); + let delta_yaw = -delta.x * config.sensitivity.x; + let delta_pitch = -delta.y * config.sensitivity.y; + + let (yaw, pitch, roll) = transform.rotation.to_euler(EulerRot::YXZ); + let yaw = yaw + delta_yaw; + + const PITCH_LIMIT: f32 = FRAC_PI_2 - 0.01; + let pitch = (pitch + delta_pitch).clamp(-PITCH_LIMIT, PITCH_LIMIT); + + transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll); + } +} diff --git a/src/player/camera/mod.rs b/src/player/camera/mod.rs index 7454210b..a786c0da 100644 --- a/src/player/camera/mod.rs +++ b/src/player/camera/mod.rs @@ -1,24 +1,14 @@ -use std::f32::consts::FRAC_PI_2; - -use crate::{dialog::conditions::dialog_running, ui_camera::UiCamera}; -use bevy::{app::RunFixedMainLoop, prelude::*, time::run_fixed_main_schedule}; +use crate::ui_camera::UiCamera; +use bevy::prelude::*; +use control::CameraAction; use leafwing_input_manager::prelude::*; -use super::Player; - +mod control; mod on_dialog; pub(super) fn plugin(app: &mut App) { app.register_type::<(PlayerCamera, PlayerCameraConfig)>(); - app.add_plugins(InputManagerPlugin::::default()); - app.add_systems( - RunFixedMainLoop, - rotate_camera - .run_if(not(dialog_running)) - .before(run_fixed_main_schedule), - ); - app.add_systems(Update, follow_player); - app.add_plugins(on_dialog::plugin); + app.add_plugins((on_dialog::plugin, control::plugin)); } #[derive(Component, Default, Reflect)] @@ -48,26 +38,6 @@ impl Default for PlayerCameraConfig { } } -#[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)] -#[actionlike(DualAxis)] -pub enum CameraAction { - RotateCamera, -} - -impl CameraAction { - pub fn default_input_map() -> InputMap { - let mut input_map = InputMap::default(); - - // Default gamepad input bindings - input_map.insert_dual_axis(CameraAction::RotateCamera, GamepadStick::LEFT); - - // Default kbm input bindings - input_map.insert_dual_axis(CameraAction::RotateCamera, MouseMove::default()); - - input_map - } -} - pub fn spawn_player_camera(world: &mut World) { let ui_cameras: Vec<_> = world .query_filtered::>() @@ -85,42 +55,3 @@ pub fn spawn_player_camera(world: &mut World) { InputManagerBundle::with_map(CameraAction::default_input_map()), )); } - -fn follow_player( - mut q_camera: Query<&mut Transform, With>, - q_player: Query<&Transform, (With, Without)>, -) { - // Use `Transform` instead of `Position`` because we want the camera to move - // smoothly, so we use the interpolated transform of the player. - let Ok(player_transform) = q_player.get_single() else { - return; - }; - let Ok(mut camera_transform) = q_camera.get_single_mut() else { - return; - }; - let height_offset = 0.5; - camera_transform.translation = - player_transform.translation + player_transform.up() * height_offset; -} - -fn rotate_camera( - mut character_query: Query<( - &mut Transform, - &ActionState, - &PlayerCameraConfig, - )>, -) { - for (mut transform, action_state, config) in &mut character_query { - let delta = action_state.axis_pair(&CameraAction::RotateCamera); - let delta_yaw = -delta.x * config.sensitivity.x; - let delta_pitch = -delta.y * config.sensitivity.y; - - let (yaw, pitch, roll) = transform.rotation.to_euler(EulerRot::YXZ); - let yaw = yaw + delta_yaw; - - const PITCH_LIMIT: f32 = FRAC_PI_2 - 0.01; - let pitch = (pitch + delta_pitch).clamp(-PITCH_LIMIT, PITCH_LIMIT); - - transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll); - } -} diff --git a/src/player/initialize.rs b/src/player/initialize.rs index f9b3296a..68fc1c48 100644 --- a/src/player/initialize.rs +++ b/src/player/initialize.rs @@ -9,7 +9,7 @@ use crate::{ character::{action::CharacterAction, controller::OverrideForwardDirection}, }; -use super::{camera::PlayerCamera, interactions::components::AvailablePlayerInteraction, Player}; +use super::{camera::PlayerCamera, interactions::AvailablePlayerInteraction, Player}; pub(super) fn plugin(app: &mut App) { app.load_resource::(); diff --git a/src/player/interactions/components.rs b/src/player/interactions/components.rs deleted file mode 100644 index ae25aa52..00000000 --- a/src/player/interactions/components.rs +++ /dev/null @@ -1,63 +0,0 @@ -use bevy::{ - ecs::component::{ComponentHooks, StorageType}, - prelude::*, -}; - -pub(super) fn plugin(app: &mut App) { - app.register_type::<( - PlayerInteractionParameters, - AvailablePlayerInteraction, - PlayerInteraction, - )>(); -} - -#[derive(Debug, Component, PartialEq, Eq, Clone, Default, Deref, DerefMut, Reflect)] -#[reflect(Component, PartialEq, Default)] -pub struct AvailablePlayerInteraction(pub Option); - -/// The general idea is as follows: -/// This component sits on a collider for an interactable object, e.g. a door or a character. -/// Every update, we send a raycast from the camera's forward direction to see if it hits a -/// [`PotentialOpportunity`] collider. -/// If so, we have an interaction opportunity. -#[derive(Debug, Component, PartialEq, Clone, Reflect)] -#[reflect(Component, PartialEq)] -pub struct PlayerInteractionParameters { - /// The prompt to display when the opportunity is available. - pub prompt: String, - /// The maximum distance from the camera at which the opportunity can be interacted with. - pub max_distance: f32, -} - -impl PlayerInteractionParameters { - pub fn default(player_interaction: &PlayerInteraction) -> Self { - match player_interaction { - PlayerInteraction::Dialog(..) => Self { - prompt: "Talk".to_string(), - max_distance: 2.5, - }, - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Reflect)] -#[reflect(PartialEq, Component)] -pub enum PlayerInteraction { - /// A dialog opportunity with a Yarn Spinner dialogue node. - Dialog(String), -} - -impl Component for PlayerInteraction { - const STORAGE_TYPE: StorageType = StorageType::Table; - - fn register_component_hooks(hooks: &mut ComponentHooks) { - hooks.on_add(|mut world, entity, _component_id| { - if world.get::(entity).is_some() { - return; - } - let interaction = world.get::(entity).unwrap(); - let parameters = PlayerInteractionParameters::default(interaction); - world.commands().entity(entity).insert(parameters); - }); - } -} diff --git a/src/player/interactions/interact.rs b/src/player/interactions/interact.rs index 1a956bc4..28b44aba 100644 --- a/src/player/interactions/interact.rs +++ b/src/player/interactions/interact.rs @@ -4,10 +4,7 @@ use leafwing_input_manager::prelude::*; use crate::{character::action::CharacterAction, dialog::StartDialog, player::Player}; use super::{ - components::{ - AvailablePlayerInteraction, PlayerInteraction, - }, - OpportunitySystem, + OpportunitySystem, {AvailablePlayerInteraction, PlayerInteraction}, }; pub(super) fn plugin(app: &mut App) { diff --git a/src/player/interactions/mod.rs b/src/player/interactions/mod.rs index fe1b8975..c99bd07b 100644 --- a/src/player/interactions/mod.rs +++ b/src/player/interactions/mod.rs @@ -1,18 +1,20 @@ use crate::system_set::VariableGameSystem; -use bevy::prelude::*; +use bevy::{ + ecs::component::{ComponentHooks, StorageType}, + prelude::*, +}; -pub mod components; mod interact; mod prompt; mod update_available; pub(super) fn plugin(app: &mut App) { - app.add_plugins(( - components::plugin, - prompt::plugin, - interact::plugin, - update_available::plugin, - )); + app.register_type::<( + PlayerInteractionParameters, + AvailablePlayerInteraction, + PlayerInteraction, + )>(); + app.add_plugins((prompt::plugin, interact::plugin, update_available::plugin)); app.configure_sets( Update, ( @@ -37,3 +39,54 @@ enum OpportunitySystem { /// Handles the player interacting with the best available opportunity. Interact, } + +#[derive(Debug, Component, PartialEq, Eq, Clone, Default, Deref, DerefMut, Reflect)] +#[reflect(Component, PartialEq, Default)] +pub struct AvailablePlayerInteraction(pub Option); + +/// The general idea is as follows: +/// This component sits on a collider for an interactable object, e.g. a door or a character. +/// Every update, we send a raycast from the camera's forward direction to see if it hits a +/// [`PotentialOpportunity`] collider. +/// If so, we have an interaction opportunity. +#[derive(Debug, Component, PartialEq, Clone, Reflect)] +#[reflect(Component, PartialEq)] +pub struct PlayerInteractionParameters { + /// The prompt to display when the opportunity is available. + pub prompt: String, + /// The maximum distance from the camera at which the opportunity can be interacted with. + pub max_distance: f32, +} + +impl PlayerInteractionParameters { + pub fn default(player_interaction: &PlayerInteraction) -> Self { + match player_interaction { + PlayerInteraction::Dialog(..) => Self { + prompt: "Talk".to_string(), + max_distance: 2.5, + }, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Reflect)] +#[reflect(PartialEq, Component)] +pub enum PlayerInteraction { + /// A dialog opportunity with a Yarn Spinner dialogue node. + Dialog(String), +} + +impl Component for PlayerInteraction { + const STORAGE_TYPE: StorageType = StorageType::Table; + + fn register_component_hooks(hooks: &mut ComponentHooks) { + hooks.on_add(|mut world, entity, _component_id| { + if world.get::(entity).is_some() { + return; + } + let interaction = world.get::(entity).unwrap(); + let parameters = PlayerInteractionParameters::default(interaction); + world.commands().entity(entity).insert(parameters); + }); + } +} diff --git a/src/player/interactions/prompt.rs b/src/player/interactions/prompt.rs index 38c64f2a..da60bab5 100644 --- a/src/player/interactions/prompt.rs +++ b/src/player/interactions/prompt.rs @@ -3,8 +3,7 @@ use bevy::ui::Val::*; use sickle_ui::{prelude::*, ui_commands::SetTextExt as _}; use super::{ - components::{AvailablePlayerInteraction, PlayerInteractionParameters}, - OpportunitySystem, + OpportunitySystem, {AvailablePlayerInteraction, PlayerInteractionParameters}, }; pub(super) fn plugin(app: &mut App) { diff --git a/src/player/interactions/update_available.rs b/src/player/interactions/update_available.rs index 0747ac54..b16fb14d 100644 --- a/src/player/interactions/update_available.rs +++ b/src/player/interactions/update_available.rs @@ -7,10 +7,7 @@ use avian3d::prelude::*; use bevy::prelude::*; use super::{ - components::{ - AvailablePlayerInteraction, PlayerInteraction, PlayerInteractionParameters, - }, - OpportunitySystem, + OpportunitySystem, {AvailablePlayerInteraction, PlayerInteraction, PlayerInteractionParameters}, }; pub(super) fn plugin(app: &mut App) {