feat: improved player camera control

main
Sara 2024-12-17 14:12:39 +01:00
parent 3b94a7e0f3
commit 5246e47f85
7 changed files with 84 additions and 21 deletions

View File

@ -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

View File

@ -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]

View File

@ -1,28 +1,50 @@
#include "camera_effects.hpp"
#include "utils/godot_macros.hpp"
#include "utils/util_functions.hpp"
#include <godot_cpp/variant/utility_functions.hpp>
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<gd::SpringArm3D>(this->get_parent());
this->pivot = gd::Object::cast_to<gd::Node3D>(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 = {

View File

@ -2,6 +2,7 @@
#define CAMERA_EFFECTS_HPP
#include <godot_cpp/classes/camera3d.hpp>
#include <godot_cpp/classes/spring_arm3d.hpp>
#include <godot_cpp/templates/vector.hpp>
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

View File

@ -16,8 +16,10 @@ void Player::_ready() {
this->input = this->get_node<utils::PlayerInput>("%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<PlayerAnimTree>("CharacterModel/AnimationTree");
this->model_node = this->get_node<gd::Node3D>("%CharacterModel");
@ -25,6 +27,8 @@ void Player::_ready() {
this->camera_parent = this->get_node<gd::Node3D>("%CameraParent");
this->camera_parent->set_global_rotation(this->get_global_rotation());
this->camera = gd::Object::cast_to<CameraEffects>(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<gd::InputEvent>, float value) {
this->anim_tree->set_is_walking(value > WALK_INPUT_THRESHOLD);
}
void Player::_on_look_vertical(gd::Ref<gd::InputEvent>, float value) {
this->input_look_vertical = -value;
}
void Player::_on_fire(gd::Ref<gd::InputEvent> event, float) {
if(event->is_pressed()) {
this->anim_tree->set_fire_weapon();
@ -99,6 +115,11 @@ void Player::_on_run(gd::Ref<gd::InputEvent> event, float) {
this->anim_tree->set_is_running();
}
void Player::_on_switch_shoulder(gd::Ref<gd::InputEvent> 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;
}

View File

@ -23,20 +23,26 @@ public:
void _on_dir_horizontal(gd::Ref<gd::InputEvent>, float value);
void _on_dir_vertical(gd::Ref<gd::InputEvent>, float value);
void _on_look_vertical(gd::Ref<gd::InputEvent>, float value);
void _on_fire(gd::Ref<gd::InputEvent> event, float);
void _on_run(gd::Ref<gd::InputEvent> event, float);
void _on_switch_shoulder(gd::Ref<gd::InputEvent> 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};

View File

@ -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);
}