feat: improved player camera control
parent
3b94a7e0f3
commit
5246e47f85
|
@ -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
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue