feat: implemented camera shake

This commit is contained in:
Sara Gerretsen 2025-12-01 16:36:19 +01:00
parent d01652bb2a
commit 7b2663cbc2
5 changed files with 54 additions and 11 deletions

View file

@ -1,5 +1,6 @@
#include "player_camera.h"
#include "core/config/engine.h"
#include "core/math/math_funcs.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
@ -8,13 +9,36 @@ PlayerCamera *PlayerCamera::instance{ nullptr };
void PlayerCamera::_bind_methods() {
ClassDB::bind_static_method(get_class_static(), "get_instance", &self_type::get_instance);
ClassDB::bind_method(D_METHOD("impact_effect", "color", "freeze_time", "zoom", "shake_intensity"), &self_type::impact_effect);
BIND_PROPERTY(Variant::FLOAT, max_shake);
}
void PlayerCamera::end_effect() {
--this->active_effects;
if (this->active_effects > 0) {
this->active_shake -= this->active_shake / float(this->active_effects);
this->active_shake = this->active_shake > 0.f ? this->active_shake : 0.f;
return;
}
if (this->active_environment.is_valid()) {
this->active_environment->set_ambient_light_color(this->ambient_light_color);
}
Engine::get_singleton()->set_time_scale(1.0);
set_process(false);
set_global_position(get_global_position() - this->shake_offset);
this->shake_offset = {};
}
void PlayerCamera::process(double delta) {
shake_interval -= delta / Engine::get_singleton()->get_time_scale();
if (shake_interval < 0.0) {
shake_interval = 0.001;
Vector3 new_offset{ 0, this->active_shake < 0.5f ? this->active_shake : 0.5f, 0 };
new_offset.rotate({ 0, 0, 1 }, Math::random(0.0, Math::PI));
Vector3 new_location{ get_global_position() - this->shake_offset };
Basis const basis{ get_global_basis() };
new_location += (this->shake_offset = basis.get_column(0) * new_offset.x + basis.get_column(1) * new_offset.y);
set_global_position(new_location);
}
}
void PlayerCamera::_notification(int what) {
@ -27,17 +51,20 @@ void PlayerCamera::_notification(int what) {
case NOTIFICATION_ENTER_TREE:
instance = this;
return;
case NOTIFICATION_EXIT_TREE:
if (instance == this) {
instance = nullptr;
}
return;
case NOTIFICATION_READY:
this->active_environment = get_world_3d()->get_environment();
if (this->active_environment.is_valid()) {
this->ambient_light_color = this->active_environment->get_ambient_light_color();
}
return;
case NOTIFICATION_PROCESS:
process(get_process_delta_time());
return;
case NOTIFICATION_EXIT_TREE:
if (instance == this) {
instance = nullptr;
}
return;
}
}
@ -47,8 +74,11 @@ PlayerCamera *PlayerCamera::get_instance() {
Ref<SceneTreeTimer> PlayerCamera::impact_effect(Color color, float freeze_time, float zoom, float shake_intensity) {
this->active_environment->set_ambient_light_color(color);
Engine::get_singleton()->set_time_scale(0.00001);
Engine::get_singleton()->set_time_scale(0.000001);
Ref<SceneTreeTimer> timer{ get_tree()->create_timer(freeze_time, true, false, true) };
timer->connect("timeout", callable_mp(this, &self_type::end_effect));
this->active_shake += shake_intensity;
++this->active_effects;
set_process(true);
return timer;
}

View file

@ -1,12 +1,15 @@
#pragma once
#include "core/math/color.h"
#include "macros.h"
#include "scene/3d/camera_3d.h"
#include "scene/resources/environment.h"
class PlayerCamera : public Camera3D {
GDCLASS(PlayerCamera, Camera3D);
static void _bind_methods();
void end_effect();
void process(double delta);
protected:
void _notification(int what);
@ -16,7 +19,17 @@ public:
Ref<SceneTreeTimer> impact_effect(Color color, float freeze_time = 0.1, float zoom = 1.f, float shake_intensity = 2.f);
private:
static PlayerCamera *instance;
Color ambient_light_color{};
Ref<Environment> active_environment{};
static PlayerCamera *instance;
float max_shake{ 1.f };
int active_effects{ 0 };
float active_shake{};
Vector3 shake_offset{};
double shake_interval{ 0.001 };
public:
GET_SET_FNS(float, max_shake);
};

View file

@ -12,7 +12,7 @@ func _ready():
$ImpactFlash2.restart()
func freeze_frame():
await PlayerCamera.get_instance().impact_effect($DustCloud.process_material.color, 0.1, 0.0, 0.0).timeout
await PlayerCamera.get_instance().impact_effect($DustCloud.process_material.color, 0.1, 0.0, 0.5).timeout
$DustCloud.restart.call_deferred()
"

View file

@ -5,7 +5,7 @@
[sub_resource type="CurveTexture" id="CurveTexture_m6khk"]
[sub_resource type="Curve" id="Curve_m6khk"]
_data = [Vector2(0, 0.0391372), 0.0, 0.876395, 0, 0, Vector2(0.847203, 1), 0.178372, 0.178372, 0, 0, Vector2(1, 0.0232278), -10.3023, 0.0, 0, 0]
_data = [Vector2(0, 0), 0.0, 2.17614, 0, 0, Vector2(0.546074, 1), -0.130662, -0.130662, 0, 0, Vector2(1, 0.0232278), -2.0867, 0.0, 0, 0]
point_count = 3
[sub_resource type="CurveTexture" id="CurveTexture_ulfqf"]
@ -44,7 +44,7 @@ center_offset = Vector3(0, 0, 0.04)
[node name="DustCloud" type="GPUParticles3D"]
emitting = false
amount = 25
lifetime = 0.2
lifetime = 0.3
one_shot = true
explosiveness = 0.89
randomness = 1.0

View file

@ -16,7 +16,7 @@ func _ready():
$ImpactFlash2.restart()
func freeze_frame():
await PlayerCamera.get_instance().impact_effect($ImpactFlash2.process_material.color, 0.15, 0.0, 0.0).timeout
await PlayerCamera.get_instance().impact_effect($ImpactFlash2.process_material.color, 0.15, 0.0, 1.0).timeout
$DustCloud.restart()
$ImpactFlash.queue_free()
$ImpactFlash2.queue_free()