From 5246e47f854f4e610639ba66f7efbc8270f936e5 Mon Sep 17 00:00:00 2001 From: Sara Date: Tue, 17 Dec 2024 14:12:39 +0100 Subject: [PATCH] feat: improved player camera control --- godot/objects/player.tscn | 6 +++--- godot/project.godot | 20 ++++++++------------ src/camera_effects.cpp | 32 +++++++++++++++++++++++++++++++- src/camera_effects.hpp | 10 ++++++++++ src/player.cpp | 29 +++++++++++++++++++++++++---- src/player.hpp | 6 ++++++ src/register_types.cpp | 2 +- 7 files changed, 84 insertions(+), 21 deletions(-) diff --git a/godot/objects/player.tscn b/godot/objects/player.tscn index f4e014a..b2c9e03 100644 --- a/godot/objects/player.tscn +++ b/godot/objects/player.tscn @@ -103,16 +103,16 @@ shape = SubResource("CapsuleShape3D_ewsvd") [node name="CameraParent" type="Node3D" parent="."] unique_name_in_owner = true -top_level = true +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.60962, 0) [node name="CameraSpring" type="SpringArm3D" parent="CameraParent"] unique_name_in_owner = true -transform = Transform3D(-0.964368, 0.00651711, -0.264483, 0.0126107, 0.999693, -0.0213483, 0.264263, -0.0239229, -0.964154, 0, 1.71904, 0) +transform = Transform3D(-0.96363, 0, -0.267238, 0, 1, 0, 0.267238, 0, -0.96363, 0, 0, 0) shape = SubResource("SphereShape3D_v7ajo") spring_length = 1.3 [node name="Camera" type="CameraEffects" parent="CameraParent/CameraSpring"] -transform = Transform3D(0.960689, 0.0368572, -0.27517, 0.00642303, 0.987932, 0.154751, 0.277554, -0.150435, 0.948859, -1.09896e-07, -8.34465e-07, 1.3) +transform = Transform3D(1, 0, 0, 0, 0.999999, 0, 0, 0, 1, -1.09896e-07, -8.34465e-07, 1.3) fov = 41.9276 far = 100.0 diff --git a/godot/project.godot b/godot/project.godot index 3f35f9d..097a950 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -59,24 +59,14 @@ dir_right={ , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":3,"axis_value":1.0,"script":null) ] } -look_right={ -"deadzone": 0.2, -"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":2,"axis_value":1.0,"script":null) -] -} -look_left={ -"deadzone": 0.2, -"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":2,"axis_value":-1.0,"script":null) -] -} look_up={ "deadzone": 0.2, -"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":3,"axis_value":-1.0,"script":null) +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":4,"axis_value":-1.0,"script":null) ] } look_down={ "deadzone": 0.2, -"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":3,"axis_value":1.0,"script":null) +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":4,"axis_value":1.0,"script":null) ] } fire={ @@ -91,6 +81,12 @@ run={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) ] } +switch_shoulder={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null) +] +} [physics] diff --git a/src/camera_effects.cpp b/src/camera_effects.cpp index 852dea1..b2e9c7d 100644 --- a/src/camera_effects.cpp +++ b/src/camera_effects.cpp @@ -1,28 +1,50 @@ #include "camera_effects.hpp" +#include "utils/godot_macros.hpp" #include "utils/util_functions.hpp" #include -void CameraEffects::_bind_methods() {} + +void CameraEffects::_bind_methods() { +#define CLASSNAME CameraEffects +} void CameraEffects::_ready() { + if(gd::Engine::get_singleton()->is_editor_hint()) + return; this->home = this->get_position(); this->target = this->home; this->base_fov = this->target_fov = this->get_fov(); + this->spring = gd::Object::cast_to(this->get_parent()); + this->pivot = gd::Object::cast_to(this->spring->get_parent()); + this->target_rotation = this->spring->get_rotation().y; + this->rotation_range = gd::Math::abs(this->target_rotation); } void CameraEffects::_process(double delta [[maybe_unused]]) { + if(gd::Engine::get_singleton()->is_editor_hint()) + return; this->home = this->get_position(); if(utils::time_seconds() > this->end_time) { intensity = 0.f; this->set_position(this->home); } + if(intensity != 0.f) { gd::Vector3 pos{this->get_position()}; pos = this->target; this->set_position(this->home + pos); this->select_target(); } + + float const current_y{this->spring->get_rotation().y}; + float diff{gd::Math::wrapf(this->target_rotation - current_y, -M_2_PIf, M_2_PI)}; + float const step(gd::Math::sign(diff) * delta * this->shoulder_switch_speed); + this->spring->rotate_y(gd::Math::abs(step) < gd::Math::abs(diff) ? step : diff); + this->set_fov(gd::Math::move_toward(this->get_fov(), this->target_fov, float(this->fov_speed * delta))); + gd::Vector3 const forward{this->get_global_position() - (this->pivot->get_global_position() + this->pivot->get_global_basis().get_column(2) * this->target_range)}; + gd::Vector3 const left{gd::Vector3{0.f, 1.f, 0.f}.cross(forward)}; + this->set_global_basis({left, forward.cross(left), forward}); } void CameraEffects::push_shake_effect(float time, float intensity) { @@ -37,6 +59,14 @@ void CameraEffects::push_zoom_effect(float factor, float speed) { this->fov_speed = speed; } +void CameraEffects::set_shoulder(bool weapon_shoulder) { + this->target_rotation = weapon_shoulder ? -this->rotation_range : this->rotation_range; +} + +bool CameraEffects::is_weapon_shoulder() const { + return this->target_rotation < 0.f; +} + void CameraEffects::select_target() { float const intensity_mod{this->intensity * 0.01f}; this->target = { diff --git a/src/camera_effects.hpp b/src/camera_effects.hpp index 69c0581..a29c554 100644 --- a/src/camera_effects.hpp +++ b/src/camera_effects.hpp @@ -2,6 +2,7 @@ #define CAMERA_EFFECTS_HPP #include +#include #include namespace gd = godot; @@ -13,15 +14,24 @@ public: virtual void _process(double delta) override; void push_shake_effect(float time, float intensity); void push_zoom_effect(float factor, float adjust_speed); + void set_shoulder(bool weapon_shoulder); + bool is_weapon_shoulder() const; void select_target(); + gd::Vector3 get_radians() const { return this->get_rotation(); } private: + float shoulder_switch_speed{1.f}; double end_time{0.5f}; float intensity{1.f}; float base_fov{60.f}; float target_fov{60.f}; float fov_speed{0.f}; + float target_range{10.f}; + float rotation_range{0.f}; + float target_rotation{0.f}; gd::Vector3 target{0.f, 0.f, 0.f}; gd::Vector3 home{0.f, 0.f, 0.f}; + gd::Node3D *pivot{nullptr}; + gd::SpringArm3D *spring{nullptr}; }; #endif // !CAMERA_EFFECTS_HPP diff --git a/src/player.cpp b/src/player.cpp index d702088..c0404c4 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -16,8 +16,10 @@ void Player::_ready() { this->input = this->get_node("%PlayerInput"); this->input->listen_to(utils::PlayerInput::Listener("dir_left", "dir_right", callable_mp(this, &Player::_on_dir_horizontal))); this->input->listen_to(utils::PlayerInput::Listener("dir_backward", "dir_forward", callable_mp(this, &Player::_on_dir_vertical))); + this->input->listen_to(utils::PlayerInput::Listener("look_down", "look_up", callable_mp(this, &Player::_on_look_vertical))); this->input->listen_to(utils::PlayerInput::Listener("fire", callable_mp(this, &Player::_on_fire))); this->input->listen_to(utils::PlayerInput::Listener("run", callable_mp(this, &Player::_on_run))); + this->input->listen_to(utils::PlayerInput::Listener("switch_shoulder", callable_mp(this, &Player::_on_switch_shoulder))); // get components this->anim_tree = this->get_node("CharacterModel/AnimationTree"); this->model_node = this->get_node("%CharacterModel"); @@ -25,6 +27,8 @@ void Player::_ready() { this->camera_parent = this->get_node("%CameraParent"); this->camera_parent->set_global_rotation(this->get_global_rotation()); this->camera = gd::Object::cast_to(this->get_viewport()->get_camera_3d()); + this->camera_height = this->camera_parent->get_global_position().y - this->get_global_position().y; + this->camera_parent->set_as_top_level(true); } void Player::_process(double delta) { @@ -40,8 +44,8 @@ void Player::_process(double delta) { gd::Vector3 const motion { local_motion.x * model_basis.get_column(0) + local_motion.y * model_basis.get_column(1) + - local_motion.z * model_basis.get_column(2) - + (this->is_on_floor() ? gd::Vector3{} : gd::Vector3{0.f, -0.05f, 0.f}) // add some gravity if required + local_motion.z * model_basis.get_column(2) + + (this->is_on_floor() ? gd::Vector3{} : gd::Vector3{0.f, -0.05f, 0.f}) // add some gravity if required }; this->set_velocity(motion / delta); // velocity has to be in m/s, root motion is framerate-dependent. meters/second=distance/time. this->camera->push_zoom_effect(this->anim_tree->match_tags(PlayerAnimTree::Tags::Aim) ? .7f : 1.f, 100.f); @@ -58,9 +62,17 @@ void Player::damage() { } void Player::process_transform_camera(double delta) { - this->camera_parent->set_global_position(this->get_global_position()); + this->camera_parent->set_global_position(this->get_global_position() + gd::Vector3{0.f, this->camera_height, 0.f}); float const camera_speed{float(delta) * (this->anim_tree->match_tags(PlayerAnimTree::Aim) ? this->AIMING_CAMERA_ROTATION_SPEED : this->CAMERA_ROTATION_SPEED)}; - this->camera_parent->rotate_y(this->input_directions.x * -camera_speed); + this->camera_parent->global_rotate({0.f, 1.f, 0.f}, this->input_directions.x * -camera_speed); + gd::Basis const basis{this->camera_parent->get_global_basis()}; + gd::Vector3 const camera_forward{basis.get_column(2)}; + gd::Vector3 const left{basis.get_column(0)}; + float const v_angle{camera_forward.signed_angle_to(gd::Vector3{camera_forward.x, 0.f, camera_forward.z}.normalized(), left)}; + float const max_frame_movement(gd::Math::abs(v_angle) / delta); + float const counter_speed(gd::Math::clamp((v_angle / this->CAMERA_VERTICAL_LIMIT) * this->CAMERA_VERTICAL_ROTATION_SPEED, -max_frame_movement, max_frame_movement)); + float const input_speed{this->input_look_vertical * this->CAMERA_VERTICAL_ROTATION_SPEED}; + this->camera_parent->global_rotate(left, (input_speed + counter_speed) * delta); } void Player::process_rotate(double delta) { @@ -87,6 +99,10 @@ void Player::_on_dir_vertical(gd::Ref, float value) { this->anim_tree->set_is_walking(value > WALK_INPUT_THRESHOLD); } +void Player::_on_look_vertical(gd::Ref, float value) { + this->input_look_vertical = -value; +} + void Player::_on_fire(gd::Ref event, float) { if(event->is_pressed()) { this->anim_tree->set_fire_weapon(); @@ -99,6 +115,11 @@ void Player::_on_run(gd::Ref event, float) { this->anim_tree->set_is_running(); } +void Player::_on_switch_shoulder(gd::Ref event, float) { + if(event->is_pressed()) + this->camera->set_shoulder(!this->camera->is_weapon_shoulder()); +} + gd::Vector2 Player::get_input_directions() const { return this->input_directions; } diff --git a/src/player.hpp b/src/player.hpp index 5536493..70a741e 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -23,20 +23,26 @@ public: void _on_dir_horizontal(gd::Ref, float value); void _on_dir_vertical(gd::Ref, float value); + void _on_look_vertical(gd::Ref, float value); void _on_fire(gd::Ref event, float); void _on_run(gd::Ref event, float); + void _on_switch_shoulder(gd::Ref event, float); gd::Vector2 get_input_directions() const; private: PlayerAnimTree *anim_tree{nullptr}; gd::Node3D *camera_parent{nullptr}; CameraEffects *camera{}; + float camera_height{0.f}; utils::PlayerInput *input{nullptr}; gd::Node3D *model_node{nullptr}; gd::Vector2 input_directions{0.f, 0.f}; + float input_look_vertical{0.f}; float const ROTATION_SPEED{1.8f}; float const CAMERA_ROTATION_SPEED{2.f}; + float const CAMERA_VERTICAL_LIMIT{0.1}; + float const CAMERA_VERTICAL_ROTATION_SPEED{1.5f}; float const AIMING_CAMERA_ROTATION_SPEED{1.f}; float const AIM_INPUT_THRESHOLD{-0.9f}; float const WALK_INPUT_THRESHOLD{0.5f}; diff --git a/src/register_types.cpp b/src/register_types.cpp index 1d33ec0..21eaa9e 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -25,7 +25,7 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) GDREGISTER_CLASS(PlayerAnimTree); GDREGISTER_RUNTIME_CLASS(Enemy); GDREGISTER_CLASS(HitscanMuzzle); - GDREGISTER_RUNTIME_CLASS(CameraEffects); + GDREGISTER_CLASS(CameraEffects); GDREGISTER_RUNTIME_CLASS(CameraEffectSource); GDREGISTER_RUNTIME_CLASS(ArtilleryTarget); }