Compare commits

...

2 commits

Author SHA1 Message Date
Sara 6c5a24e374 feat: player body now handles movement fov adjustment 2025-08-11 17:59:37 +02:00
Sara b29966ccf7 fix: revolver alt fire becomes inactive on switch 2025-08-11 17:59:12 +02:00
8 changed files with 63 additions and 43 deletions

View file

@ -35,4 +35,11 @@
set_##m_value(m_value); \ set_##m_value(m_value); \
} }
#define GETSETM(m_from, m_value, ...) \
{ \
auto m_value{ m_from->get_##m_value() }; \
__VA_ARGS__ \
m_from->set_##m_value(m_value); \
}
#endif // !GODOT_EXTRA_MACROS_H #endif // !GODOT_EXTRA_MACROS_H

View file

@ -1,6 +1,7 @@
#include "player_body.h" #include "player_body.h"
#include "health_status.h" #include "health_status.h"
#include "macros.h" #include "macros.h"
#include "player_camera.h"
#include "player_input.h" #include "player_input.h"
#include "weapon_base.h" #include "weapon_base.h"
#include "weapon_inventory.h" #include "weapon_inventory.h"
@ -11,6 +12,7 @@ void PlayerBody::_bind_methods() {
} }
void PlayerBody::on_child_entered(Node *node) { void PlayerBody::on_child_entered(Node *node) {
node->connect("child_entered_tree", callable_mp(this, &self_type::on_child_entered));
if (PlayerInput * input{ cast_to<PlayerInput>(node) }) { if (PlayerInput * input{ cast_to<PlayerInput>(node) }) {
input->connect(PlayerInput::sig_movement_input, callable_mp(this, &self_type::set_movement_input)); input->connect(PlayerInput::sig_movement_input, callable_mp(this, &self_type::set_movement_input));
input->connect(PlayerInput::sig_look_input, callable_mp(this, &self_type::on_look_input)); input->connect(PlayerInput::sig_look_input, callable_mp(this, &self_type::on_look_input));
@ -23,6 +25,16 @@ void PlayerBody::on_child_entered(Node *node) {
if (HealthStatus * health{ cast_to<HealthStatus>(node) }) { if (HealthStatus * health{ cast_to<HealthStatus>(node) }) {
this->health = health; this->health = health;
} }
if (PlayerCamera * camera{ cast_to<PlayerCamera>(node) }) {
this->camera = camera;
}
}
void PlayerBody::process(double delta) {
GETSETM(this->camera, fov_factor, {
float const target_fov{ get_is_running() ? 1.2f : 1.0f };
fov_factor = Math::move_toward(fov_factor, target_fov, float(delta));
});
} }
void PlayerBody::physics_process(double delta) { void PlayerBody::physics_process(double delta) {
@ -78,6 +90,9 @@ void PlayerBody::_notification(int what) {
set_process(true); set_process(true);
set_physics_process(true); set_physics_process(true);
return; return;
case NOTIFICATION_PROCESS:
process(get_process_delta_time());
return;
case NOTIFICATION_PHYSICS_PROCESS: case NOTIFICATION_PHYSICS_PROCESS:
physics_process(get_physics_process_delta_time()); physics_process(get_physics_process_delta_time());
return; return;

View file

@ -4,6 +4,7 @@
#include "scene/3d/physics/character_body_3d.h" #include "scene/3d/physics/character_body_3d.h"
class WeaponInventory; class WeaponInventory;
class HealthStatus; class HealthStatus;
class PlayerCamera;
class PlayerBody : public CharacterBody3D { class PlayerBody : public CharacterBody3D {
GDCLASS(PlayerBody, CharacterBody3D); GDCLASS(PlayerBody, CharacterBody3D);
@ -11,6 +12,7 @@ class PlayerBody : public CharacterBody3D {
static PlayerBody *singleton_instance; static PlayerBody *singleton_instance;
void on_child_entered(Node *node); void on_child_entered(Node *node);
void process(double delta);
void physics_process(double delta); void physics_process(double delta);
void set_movement_input(Vector2 state); void set_movement_input(Vector2 state);
@ -36,7 +38,8 @@ private:
float jump_strength{ 4.f }; float jump_strength{ 4.f };
Vector2 movement_input{ 0, 0 }; Vector2 movement_input{ 0, 0 };
WeaponInventory *weapons; PlayerCamera *camera{ nullptr };
WeaponInventory *weapons{ nullptr };
HealthStatus *health{ nullptr }; HealthStatus *health{ nullptr };
}; };

View file

@ -23,19 +23,31 @@ void PlayerCamera::ready() {
this->base_fov = get_fov(); this->base_fov = get_fov();
} }
void PlayerCamera::process(double delta) {
this->fov_factor = 1.0;
update_fov();
}
void PlayerCamera::_notification(int what) { void PlayerCamera::_notification(int what) {
if (Engine::get_singleton()->is_editor_hint()) { if (Engine::get_singleton()->is_editor_hint()) {
return; return;
} }
switch (what) { switch (what) {
default:
return;
case NOTIFICATION_READY: case NOTIFICATION_READY:
set_process(true);
set_process_priority(-1);
ready(); ready();
return; return;
case NOTIFICATION_PROCESS:
process(get_process_delta_time());
return;
} }
} }
void PlayerCamera::set_fov_factor(float value) { void PlayerCamera::set_fov_factor(float value) {
this->fov_factor = value; this->fov_factor *= value;
update_fov(); update_fov();
} }

View file

@ -190,6 +190,7 @@ void Revolver::notify_selected() {
} }
void Revolver::notify_deselected() { void Revolver::notify_deselected() {
this->alt_active = false;
get_input()->disconnect(PlayerInput::sig_primary_fire, callable_mp(this, &self_type::on_primary_fire)); get_input()->disconnect(PlayerInput::sig_primary_fire, callable_mp(this, &self_type::on_primary_fire));
get_input()->disconnect(PlayerInput::sig_alt_mode, callable_mp(this, &self_type::on_alt_mode)); get_input()->disconnect(PlayerInput::sig_alt_mode, callable_mp(this, &self_type::on_alt_mode));
get_input()->disconnect(PlayerInput::sig_reload, callable_mp(this, &self_type::on_reload)); get_input()->disconnect(PlayerInput::sig_reload, callable_mp(this, &self_type::on_reload));

View file

@ -9,7 +9,6 @@
void Rifle::_bind_methods() { void Rifle::_bind_methods() {
BIND_PROPERTY(Variant::FLOAT, ads_factor); BIND_PROPERTY(Variant::FLOAT, ads_factor);
BIND_PROPERTY(Variant::FLOAT, run_factor);
BIND_PROPERTY(Variant::FLOAT, recoil_force); BIND_PROPERTY(Variant::FLOAT, recoil_force);
BIND_PROPERTY(Variant::FLOAT, recoil_time); BIND_PROPERTY(Variant::FLOAT, recoil_time);
ClassDB::bind_method(D_METHOD("reload_full"), &self_type::reload_full); ClassDB::bind_method(D_METHOD("reload_full"), &self_type::reload_full);
@ -18,13 +17,11 @@ void Rifle::_bind_methods() {
void Rifle::queue_enter_alt() { void Rifle::queue_enter_alt() {
get_anim()->queue("hip_to_aim"); get_anim()->queue("hip_to_aim");
get_anim()->queue("aim"); get_anim()->queue("aim");
HeadsUpDisplay::get_singleton()->set_reticle_visibility(false);
} }
void Rifle::queue_exit_alt() { void Rifle::queue_exit_alt() {
get_anim()->queue("aim_to_hip"); get_anim()->queue("aim_to_hip");
get_anim()->queue("hip"); get_anim()->queue("hip");
HeadsUpDisplay::get_singleton()->set_reticle_visibility(true);
} }
void Rifle::queue_enter_run() { void Rifle::queue_enter_run() {
@ -90,12 +87,15 @@ void Rifle::on_reload() {
void Rifle::on_animation_changed(String new_animation) { void Rifle::on_animation_changed(String new_animation) {
if (new_animation == "aim") { if (new_animation == "aim") {
this->in_alt_mode = true; this->in_alt_mode = true;
get_camera()->set_fov_factor(this->ads_factor); this->fov = this->ads_factor;
HeadsUpDisplay::get_singleton()->set_reticle_visibility(false);
} else if (new_animation == "hip") { } else if (new_animation == "hip") {
this->in_alt_mode = false; this->in_alt_mode = false;
get_camera()->set_fov_factor(1.0); this->fov = 1.0;
} else if (new_animation == "run") { HeadsUpDisplay::get_singleton()->set_reticle_visibility(true);
get_camera()->set_fov_factor(this->run_factor); }
if (new_animation == "reload") {
HeadsUpDisplay::get_singleton()->set_reticle_visibility(false);
} }
} }
@ -109,22 +109,13 @@ void Rifle::process(double delta) {
bool run_requested{ this->run_requested() }; bool run_requested{ this->run_requested() };
// track animation progress // track animation progress
double const animation_time{ get_anim()->get_current_animation_position() }; double const animation_time{ get_anim()->get_current_animation_position() };
// percentually // animation progress percentually
float const progress{ float(CLAMP(animation_time / get_anim()->get_current_animation_length(), 0.0, 1.0)) }; float const progress{ float(CLAMP(animation_time / get_anim()->get_current_animation_length(), 0.0, 1.0)) };
// lerp the current FOV factor depending on the ongoing animation if (current == "hip_to_aim" || current == "run_to_aim") { // lerp the current FOV factor depending on the ongoing animation
if (current == "hip_to_aim") { this->fov = Math::lerp(1.f, this->ads_factor, progress);
get_camera()->set_fov_factor(Math::lerp(1.f, this->ads_factor, progress));
} else if (current == "aim_to_hip") { } else if (current == "aim_to_hip") {
get_camera()->set_fov_factor(Math::lerp(this->ads_factor, 1.f, progress)); this->fov = Math::lerp(this->ads_factor, 1.f, progress);
} else if (current == "run_to_aim") { } else if (this->request_alt_mode != this->in_alt_mode && !is_animating()) { // act on request flags that have yet to be fulfilled
get_camera()->set_fov_factor(Math::lerp(this->run_factor, this->ads_factor, progress));
} else if (current == "hip_to_run") {
get_camera()->set_fov_factor(Math::lerp(1.f, this->run_factor, progress));
} else if (current == "run_to_hip") {
get_camera()->set_fov_factor(Math::lerp(this->run_factor, 1.f, progress));
// animation is not one of the transitory ones ( x_to_y ),
// check if there is a request for a transitory animation
} else if (this->request_alt_mode != this->in_alt_mode && !is_animating()) {
if (this->request_alt_mode) { if (this->request_alt_mode) {
queue_enter_alt(); queue_enter_alt();
} else { } else {
@ -136,12 +127,13 @@ void Rifle::process(double delta) {
} else if (!run_requested) { } else if (!run_requested) {
exit_run(); exit_run();
} }
} } else if (current == "fire_hip" || current == "fire_aim") { // apply fire recoil
// apply fire recoil
else if (current == "fire_hip" || current == "fire_aim") {
double t{ animation_time / this->recoil_time }; double t{ animation_time / this->recoil_time };
get_camera()->recoil(Math::lerp((double)this->recoil_force, 0.0, CLAMP(t, 0.0, 1.0)) * delta); get_camera()->recoil(Math::lerp((double)this->recoil_force, 0.0, CLAMP(t, 0.0, 1.0)) * delta);
} }
if (this->fov != 1.0) {
get_camera()->set_fov_factor(this->fov);
}
} }
void Rifle::_notification(int what) { void Rifle::_notification(int what) {
@ -169,11 +161,6 @@ PackedStringArray Rifle::get_configuration_warnings() const {
return warnings; return warnings;
} }
bool Rifle::allows_swapping() const {
String const current{ get_anim()->get_current_animation() };
return !this->in_alt_mode && (current == "reload" || !is_animating());
}
bool Rifle::allows_running() const { bool Rifle::allows_running() const {
String const animation{ get_anim()->get_current_animation() }; String const animation{ get_anim()->get_current_animation() };
return animation == "run" && !this->request_alt_mode; return animation == "run" && !this->request_alt_mode;
@ -194,6 +181,10 @@ void Rifle::notify_deselected() {
get_input()->disconnect(PlayerInput::sig_primary_fire, callable_mp(this, &self_type::on_primary_fire)); get_input()->disconnect(PlayerInput::sig_primary_fire, callable_mp(this, &self_type::on_primary_fire));
get_input()->disconnect(PlayerInput::sig_alt_mode, callable_mp(this, &self_type::on_alt_mode)); get_input()->disconnect(PlayerInput::sig_alt_mode, callable_mp(this, &self_type::on_alt_mode));
get_input()->disconnect(PlayerInput::sig_reload, callable_mp(this, &self_type::on_reload)); get_input()->disconnect(PlayerInput::sig_reload, callable_mp(this, &self_type::on_reload));
get_camera()->set_fov_factor(1.0);
if (HeadsUpDisplay * hud{ HeadsUpDisplay::get_singleton() }) {
hud->set_reticle_visibility(true);
}
} }
void Rifle::reload_full() { void Rifle::reload_full() {
@ -209,14 +200,6 @@ float Rifle::get_ads_factor() const {
return this->ads_factor; return this->ads_factor;
} }
void Rifle::set_run_factor(float value) {
this->run_factor = value;
}
float Rifle::get_run_factor() const {
return this->run_factor;
}
void Rifle::set_recoil_force(float value) { void Rifle::set_recoil_force(float value) {
this->recoil_force = value; this->recoil_force = value;
} }

View file

@ -27,7 +27,6 @@ public:
void _notification(int what); void _notification(int what);
virtual PackedStringArray get_configuration_warnings() const override; virtual PackedStringArray get_configuration_warnings() const override;
virtual bool allows_swapping() const override;
virtual bool allows_running() const override; virtual bool allows_running() const override;
bool run_requested() const; bool run_requested() const;
virtual void notify_selected() override; virtual void notify_selected() override;
@ -36,8 +35,6 @@ public:
void set_ads_factor(float value); void set_ads_factor(float value);
float get_ads_factor() const; float get_ads_factor() const;
void set_run_factor(float value);
float get_run_factor() const;
void set_recoil_force(float value); void set_recoil_force(float value);
float get_recoil_force() const; float get_recoil_force() const;
void set_recoil_time(float value); void set_recoil_time(float value);
@ -45,7 +42,6 @@ public:
private: private:
float ads_factor{ 0.5f }; float ads_factor{ 0.5f };
float run_factor{ 1.5f };
bool request_alt_mode{ false }; bool request_alt_mode{ false };
bool in_alt_mode{ false }; bool in_alt_mode{ false };
bool running{ false }; bool running{ false };
@ -53,6 +49,8 @@ private:
float recoil_force{ 3.f }; float recoil_force{ 3.f };
float recoil_time{ 0.05f }; float recoil_time{ 0.05f };
double fov{ 1.0 };
HitscanMuzzle *muzzle{ nullptr }; HitscanMuzzle *muzzle{ nullptr };
}; };

View file

@ -23,6 +23,7 @@ wall_min_slide_angle = 0.0
[node name="PlayerCamera" type="PlayerCamera" parent="."] [node name="PlayerCamera" type="PlayerCamera" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
process_priority = -1
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0.27450943, 0) transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0.27450943, 0)
fov = 60.0 fov = 60.0