diff --git a/source/core/defaults/playfield.hpp b/source/core/defaults/playfield.hpp index e8b572e..512722c 100644 --- a/source/core/defaults/playfield.hpp +++ b/source/core/defaults/playfield.hpp @@ -25,20 +25,32 @@ namespace Defaults::Playfield { constexpr float HEAD_LENGTH_PX = 8.0F; // longitud en píxels lògics del tram brillant constexpr float HEAD_BRIGHTNESS = 0.0F; // brillo del cap (= border) - // Orbit (oscil·lació transversal de la línia quan la nau hi passa a prop). - constexpr float ORBIT_AMPLITUDE_MAX_PX = 3.0F; // desplaçament transversal màxim - constexpr float ORBIT_DECAY_PER_S = 4.0F; // decaiment de l'amplitud (px/s) - constexpr float ORBIT_FREQ_HZ = 8.0F; // freqüència del sin - constexpr float ORBIT_PROXIMITY_PX = 12.0F; // distància max de la línia per excitar-la - constexpr float ORBIT_SHIP_SPEED_THRESHOLD = 60.0F; // velocitat mínima per excitar (px/s) + // Ripples: deformacions circulars que travessen la graella com ones d'aigua. + // Cada ripple desplaça radialment cap a fora els vèrtexs de les línies que + // travessa, amb una envoltant que decau a les vores de l'anell i amb el temps. + namespace Ripple { + constexpr int POOL_SIZE = 32; - // Pulse (reacció a fireworks: punt brillant que es propaga al llarg de la - // línia a partir del punt de spawn). - constexpr int MAX_PULSES_PER_LINE = 2; - constexpr float PULSE_LIFETIME_S = 1.0F; // temps total fins desaparèixer - constexpr float PULSE_SPREAD_PER_S = 300.0F; // px/s de propagació (cap a cada extrem) - constexpr unsigned char PULSE_COLOR_R = 180; - constexpr unsigned char PULSE_COLOR_G = 230; - constexpr unsigned char PULSE_COLOR_B = 255; + // Ones grans (explosions / fireworks). + constexpr float BIG_AMPLITUDE_PX = 10.0F; + constexpr float BIG_SPEED_PX_S = 320.0F; + constexpr float BIG_LIFETIME_S = 1.4F; + constexpr float BIG_THICKNESS_PX = 40.0F; + + // Ones petites (pas de nau, cadència estil trail). + constexpr float SMALL_AMPLITUDE_PX = 2.5F; + constexpr float SMALL_SPEED_PX_S = 160.0F; + constexpr float SMALL_LIFETIME_S = 0.55F; + constexpr float SMALL_THICKNESS_PX = 18.0F; + + // Cadència "soltar gotetes" per nau (patró TrailManager). + constexpr float SHIP_COOLDOWN_S = 0.10F; + constexpr float SHIP_COOLDOWN_JITTER_S = 0.03F; + constexpr float SHIP_SPEED_THRESHOLD_PX_S = 80.0F; + + // Subdivisió de línies quan estan dins una ripple. + constexpr int MAIN_SEGMENTS = 24; // línies principals + constexpr int SUB_SEGMENTS = 12; // sub-graella + } // namespace Ripple } // namespace Defaults::Playfield diff --git a/source/core/graphics/playfield.cpp b/source/core/graphics/playfield.cpp index 9e7f51f..8d74d42 100644 --- a/source/core/graphics/playfield.cpp +++ b/source/core/graphics/playfield.cpp @@ -5,8 +5,8 @@ #include #include +#include #include -#include #include "core/defaults.hpp" #include "core/rendering/line_renderer.hpp" @@ -21,20 +21,38 @@ namespace Graphics { return 1.0F - (INV * INV * INV); } - // Lerp del color base actual (oscil·lador) cap a un color destí en - // funció de f ∈ [0, 1]. Alpha > 0 perquè line_renderer l'usi directe. - auto lerpColor(SDL_Color target, float f) -> SDL_Color { - const float CLAMPED = std::clamp(f, 0.0F, 1.0F); - const SDL_Color BASE = Rendering::getLineColor(); - const auto LERP_U8 = [&](unsigned char a, unsigned char b) { - const float OUT = (static_cast(a) * (1.0F - CLAMPED)) + (static_cast(b) * CLAMPED); - return static_cast(OUT); - }; - return SDL_Color{ - .r = LERP_U8(BASE.r, target.r), - .g = LERP_U8(BASE.g, target.g), - .b = LERP_U8(BASE.b, target.b), - .a = 255}; + auto randUniform(float min_v, float max_v) -> float { + const float NORM = static_cast(std::rand()) / static_cast(RAND_MAX); + return min_v + (NORM * (max_v - min_v)); + } + + // Desplaçament radial acumulat al punt (px, py) sumant totes les ripples + // que el toquen. Retorna {dx, dy} a sumar a la posició original. + auto computeRippleDisplacement(float px, float py, const Playfield::Ripple* const* hits, int n_hits) -> Vec2 { + float dx_total = 0.0F; + float dy_total = 0.0F; + for (int i = 0; i < n_hits; i++) { + const auto& r = *hits[i]; + const float RADIUS = r.age_s * r.speed_px_s; + const float THICKNESS = r.thickness_px; + const float DX = px - r.center.x; + const float DY = py - r.center.y; + const float D = std::sqrt((DX * DX) + (DY * DY)); + if (D < 0.001F) { + continue; // centre exacte: no hi ha direcció radial + } + const float PHASE = (D - RADIUS) / THICKNESS; + if (std::fabs(PHASE) >= 1.0F) { + continue; // fora de l'anell d'aquesta ripple + } + const float ENVELOPE = std::cos(PHASE * Defaults::Math::PI * 0.5F); + const float AMP_EFF = r.amplitude_px * (1.0F - (r.age_s / r.lifetime_s)); + const float UX = DX / D; + const float UY = DY / D; + dx_total += UX * AMP_EFF * ENVELOPE; + dy_total += UY * AMP_EFF * ENVELOPE; + } + return Vec2{.x = dx_total, .y = dy_total}; } } // namespace @@ -46,95 +64,80 @@ namespace Graphics { void Playfield::update(float delta_time) { elapsed_s_ += delta_time; - - // Decau l'orbit i avança la fase del sin per cada línia. - const float ORBIT_DELTA_PHASE = Defaults::Playfield::ORBIT_FREQ_HZ * 2.0F * Defaults::Math::PI * delta_time; - const float ORBIT_DEC = Defaults::Playfield::ORBIT_DECAY_PER_S * delta_time; - for (auto& line : lines_) { - line.orbit_phase += ORBIT_DELTA_PHASE; - line.orbit_amplitude = std::max(0.0F, line.orbit_amplitude - ORBIT_DEC); - - // Avança els pulses; els desactiva quan acaben de vida. - for (auto& pulse : line.pulses) { - if (!pulse.active) { - continue; - } - pulse.age_s += delta_time; - if (pulse.age_s >= Defaults::Playfield::PULSE_LIFETIME_S) { - pulse.active = false; - } + for (auto& ripple : ripples_) { + if (!ripple.active) { + continue; + } + ripple.age_s += delta_time; + if (ripple.age_s >= ripple.lifetime_s) { + ripple.active = false; } } } - void Playfield::spawnPulseAt(Line& line, float center_t) { - for (auto& pulse : line.pulses) { - if (!pulse.active) { - pulse.active = true; - pulse.age_s = 0.0F; - pulse.center_t = std::clamp(center_t, 0.0F, 1.0F); - return; + auto Playfield::findFreeRipple() -> Ripple* { + Ripple* oldest = nullptr; + for (auto& ripple : ripples_) { + if (!ripple.active) { + return &ripple; + } + if (oldest == nullptr || ripple.age_s > oldest->age_s) { + oldest = &ripple; } } - // Cap slot lliure: substituïm el més vell. - Pulse* oldest = line.pulses.data(); - for (auto& pulse : line.pulses) { - if (pulse.age_s > oldest->age_s) { - oldest = &pulse; - } - } - oldest->active = true; - oldest->age_s = 0.0F; - oldest->center_t = std::clamp(center_t, 0.0F, 1.0F); + return oldest; // pool ple: substituïm la més vella } - void Playfield::notifyFireworkSpawn(Vec2 pos) { - // Línia vertical més propera (per posició x) i horitzontal més propera (per y). - Line* closest_v = nullptr; - Line* closest_h = nullptr; - float min_dx = std::numeric_limits::max(); - float min_dy = std::numeric_limits::max(); - for (auto& line : lines_) { - if (line.is_vertical) { - const float DX = std::abs(pos.x - line.start.x); - if (DX < min_dx) { - min_dx = DX; - closest_v = &line; - } - } else { - const float DY = std::abs(pos.y - line.start.y); - if (DY < min_dy) { - min_dy = DY; - closest_h = &line; - } - } - } - if (closest_v != nullptr) { - const float LINE_LEN = closest_v->end.y - closest_v->start.y; - const float CENTER_T = (LINE_LEN > 0.0F) ? (pos.y - closest_v->start.y) / LINE_LEN : 0.5F; - spawnPulseAt(*closest_v, CENTER_T); - } - if (closest_h != nullptr) { - const float LINE_LEN = closest_h->end.x - closest_h->start.x; - const float CENTER_T = (LINE_LEN > 0.0F) ? (pos.x - closest_h->start.x) / LINE_LEN : 0.5F; - spawnPulseAt(*closest_h, CENTER_T); - } - } - - void Playfield::notifyShipPass(Vec2 pos, float speed_px_s) { - if (speed_px_s < Defaults::Playfield::ORBIT_SHIP_SPEED_THRESHOLD) { + void Playfield::spawnBig(Vec2 pos) { + Ripple* r = findFreeRipple(); + if (r == nullptr) { return; } - const float MAX_DIST = Defaults::Playfield::ORBIT_PROXIMITY_PX; - for (auto& line : lines_) { - // Distància perpendicular del punt a la línia (que és horitzontal o vertical). - const float DIST = line.is_vertical - ? std::abs(pos.x - line.start.x) - : std::abs(pos.y - line.start.y); - if (DIST < MAX_DIST) { - line.orbit_amplitude = Defaults::Playfield::ORBIT_AMPLITUDE_MAX_PX; - } + r->center = pos; + r->age_s = 0.0F; + r->lifetime_s = Defaults::Playfield::Ripple::BIG_LIFETIME_S; + r->speed_px_s = Defaults::Playfield::Ripple::BIG_SPEED_PX_S; + r->amplitude_px = Defaults::Playfield::Ripple::BIG_AMPLITUDE_PX; + r->thickness_px = Defaults::Playfield::Ripple::BIG_THICKNESS_PX; + r->active = true; + } + + void Playfield::spawnSmall(Vec2 pos) { + Ripple* r = findFreeRipple(); + if (r == nullptr) { + return; } + r->center = pos; + r->age_s = 0.0F; + r->lifetime_s = Defaults::Playfield::Ripple::SMALL_LIFETIME_S; + r->speed_px_s = Defaults::Playfield::Ripple::SMALL_SPEED_PX_S; + r->amplitude_px = Defaults::Playfield::Ripple::SMALL_AMPLITUDE_PX; + r->thickness_px = Defaults::Playfield::Ripple::SMALL_THICKNESS_PX; + r->active = true; + } + + void Playfield::notifyExplosion(Vec2 pos) { + spawnBig(pos); + } + + void Playfield::notifyShipMoving(std::uint8_t player_id, Vec2 pos, float speed_px_s, float delta_time) { + if (player_id >= ship_ripple_cooldown_.size()) { + return; + } + if (speed_px_s < Defaults::Playfield::Ripple::SHIP_SPEED_THRESHOLD_PX_S) { + ship_ripple_cooldown_[player_id] = 0.0F; + return; + } + ship_ripple_cooldown_[player_id] -= delta_time; + if (ship_ripple_cooldown_[player_id] > 0.0F) { + return; + } + spawnSmall(pos); + const float JITTER = randUniform( + -Defaults::Playfield::Ripple::SHIP_COOLDOWN_JITTER_S, + Defaults::Playfield::Ripple::SHIP_COOLDOWN_JITTER_S); + ship_ripple_cooldown_[player_id] = + Defaults::Playfield::Ripple::SHIP_COOLDOWN_S + JITTER; } void Playfield::buildLines() { @@ -161,10 +164,7 @@ namespace Graphics { .end = {.x = X, .y = zona.y + zona.h}, .brightness = BRIGHTNESS, .spawn_time_s = 0.0F, - .is_vertical = true, - .orbit_amplitude = 0.0F, - .orbit_phase = 0.0F, - .pulses = {}}); + .is_vertical = true}); } // Horitzontals: posicions j ∈ [1, SUB_HORIZ-1]. @@ -179,10 +179,7 @@ namespace Graphics { .end = {.x = zona.x + zona.w, .y = Y}, .brightness = BRIGHTNESS, .spawn_time_s = 0.0F, - .is_vertical = false, - .orbit_amplitude = 0.0F, - .orbit_phase = 0.0F, - .pulses = {}}); + .is_vertical = false}); } // Ona diagonal: la línia esquerra/superior naix a t=0 i les següents @@ -215,90 +212,100 @@ namespace Graphics { } void Playfield::draw() const { + // Recollir ripples actives (punters per accés ràpid al hot loop). + std::array active{}; + int n_active = 0; + for (const auto& ripple : ripples_) { + if (ripple.active) { + active[n_active++] = &ripple; + } + } for (const auto& line : lines_) { - const float RAW_P = computeLineProgress(line); - if (RAW_P <= 0.0F) { + drawLine(line, active.data(), n_active); + } + } + + void Playfield::drawLine(const Line& line, const Ripple* const* active, int n_active) const { + const float RAW_P = computeLineProgress(line); + if (RAW_P <= 0.0F) { + return; + } + const float P = easeOutCubic(RAW_P); + + const float START_X = line.start.x; + const float START_Y = line.start.y; + const float DX = line.end.x - line.start.x; + const float DY = line.end.y - line.start.y; + const float END_X = START_X + (DX * P); + const float END_Y = START_Y + (DY * P); + + // AABB de la porció visible de la línia + filtre de ripples. + const float LINE_MIN_X = std::min(START_X, END_X); + const float LINE_MAX_X = std::max(START_X, END_X); + const float LINE_MIN_Y = std::min(START_Y, END_Y); + const float LINE_MAX_Y = std::max(START_Y, END_Y); + std::array hits{}; + int n_hits = 0; + for (int i = 0; i < n_active; i++) { + const auto& r = *active[i]; + const float R_MAX = (r.age_s * r.speed_px_s) + r.thickness_px; + if ((r.center.x + R_MAX) < LINE_MIN_X || (r.center.x - R_MAX) > LINE_MAX_X || + (r.center.y + R_MAX) < LINE_MIN_Y || (r.center.y - R_MAX) > LINE_MAX_Y) { continue; } - const float P = easeOutCubic(RAW_P); + hits[n_hits++] = &r; + } - // Desplaçament perpendicular per orbit (verticals → x, horitzontals → y). - const float ORBIT_OFFSET = line.orbit_amplitude * std::sin(line.orbit_phase); - const float ORBIT_DX = line.is_vertical ? ORBIT_OFFSET : 0.0F; - const float ORBIT_DY = line.is_vertical ? 0.0F : ORBIT_OFFSET; - - const float START_X = line.start.x + ORBIT_DX; - const float START_Y = line.start.y + ORBIT_DY; - const float DX = line.end.x - line.start.x; - const float DY = line.end.y - line.start.y; - const float CURRENT_X = START_X + (DX * P); - const float CURRENT_Y = START_Y + (DY * P); - - // Tram base (brillo de la línia). + if (n_hits == 0) { + // Camí ràpid: una sola crida com abans. Rendering::linea( renderer_, static_cast(START_X), static_cast(START_Y), - static_cast(CURRENT_X), - static_cast(CURRENT_Y), + static_cast(END_X), + static_cast(END_Y), line.brightness); - - // Cap brillant mentre creix: l'últim tram de la línia es repinta més brillant. + // Cap brillant mentre creix. if (P < 1.0F) { const float LENGTH = std::sqrt((DX * DX) + (DY * DY)); if (LENGTH > 0.0F) { const float HEAD_T = std::max(0.0F, P - (Defaults::Playfield::HEAD_LENGTH_PX / LENGTH)); - const float HEAD_X = START_X + (DX * HEAD_T); - const float HEAD_Y = START_Y + (DY * HEAD_T); Rendering::linea( renderer_, - static_cast(HEAD_X), - static_cast(HEAD_Y), - static_cast(CURRENT_X), - static_cast(CURRENT_Y), + static_cast(START_X + (DX * HEAD_T)), + static_cast(START_Y + (DY * HEAD_T)), + static_cast(END_X), + static_cast(END_Y), Defaults::Playfield::HEAD_BRIGHTNESS); } } + return; + } - // Pulses: cada un és un segment brillant centrat a center_t que - // s'expandeix amb el temps i s'apaga. - const float LINE_LENGTH = std::sqrt((DX * DX) + (DY * DY)); - if (LINE_LENGTH <= 0.0F) { - continue; - } - const SDL_Color PULSE_TARGET = { - .r = Defaults::Playfield::PULSE_COLOR_R, - .g = Defaults::Playfield::PULSE_COLOR_G, - .b = Defaults::Playfield::PULSE_COLOR_B, - .a = 255}; - for (const auto& pulse : line.pulses) { - if (!pulse.active) { - continue; - } - const float HALF_WIDTH_T = (pulse.age_s * Defaults::Playfield::PULSE_SPREAD_PER_S) / LINE_LENGTH; - const float INTENSITY = std::max( - 0.0F, - 1.0F - (pulse.age_s / Defaults::Playfield::PULSE_LIFETIME_S)); - const float T1 = std::clamp(pulse.center_t - HALF_WIDTH_T, 0.0F, 1.0F); - const float T2 = std::clamp(pulse.center_t + HALF_WIDTH_T, 0.0F, 1.0F); - if (T2 <= T1) { - continue; - } - const float P1_X = START_X + (DX * T1); - const float P1_Y = START_Y + (DY * T1); - const float P2_X = START_X + (DX * T2); - const float P2_Y = START_Y + (DY * T2); - const SDL_Color SEG_COLOR = lerpColor(PULSE_TARGET, INTENSITY); - Rendering::linea( - renderer_, - static_cast(P1_X), - static_cast(P1_Y), - static_cast(P2_X), - static_cast(P2_Y), - 1.0F, - 0.0F, - SEG_COLOR); - } + // Camí deformat: subdividir en N segments i desplaçar cada vèrtex. + const bool IS_MAIN = line.brightness >= Defaults::Playfield::GRID_BRIGHTNESS; + const int N = IS_MAIN + ? Defaults::Playfield::Ripple::MAIN_SEGMENTS + : Defaults::Playfield::Ripple::SUB_SEGMENTS; + const Vec2 D0 = computeRippleDisplacement(START_X, START_Y, hits.data(), n_hits); + float prev_x = START_X + D0.x; + float prev_y = START_Y + D0.y; + for (int i = 1; i <= N; i++) { + const float T = static_cast(i) / static_cast(N); + const float X = START_X + (DX * P * T); + const float Y = START_Y + (DY * P * T); + const Vec2 D = computeRippleDisplacement(X, Y, hits.data(), n_hits); + const float NX = X + D.x; + const float NY = Y + D.y; + Rendering::linea( + renderer_, + static_cast(prev_x), + static_cast(prev_y), + static_cast(NX), + static_cast(NY), + line.brightness); + prev_x = NX; + prev_y = NY; } } diff --git a/source/core/graphics/playfield.hpp b/source/core/graphics/playfield.hpp index 4a037be..ba0216b 100644 --- a/source/core/graphics/playfield.hpp +++ b/source/core/graphics/playfield.hpp @@ -5,13 +5,16 @@ // rep un `creation_progress` global ∈ [0, 1] i cada línia computa quina porció // li toca dibuixar segons el seu slot a la timeline. // -// Disseny preparat per a futures capacitats: -// - Línies "vives" que reaccionen a explosions / pas de la nau (reaction_intensity). -// - Capes addicionals al fons (estrelles, gradients, scanlines). +// Reaccions disponibles: +// - Ripples: deformacions circulars (ones d'aigua) que travessen la graella. +// Disparades per explosions (grans) i pas de la nau (petites, cadència estil +// trail). Cada vèrtex d'una línia afectada es desplaça radialment cap a fora +// amb una envoltant en cos(·) que decau a les vores de l'anell i amb el temps. #pragma once #include +#include #include #include "core/defaults/playfield.hpp" @@ -24,44 +27,51 @@ namespace Graphics { public: explicit Playfield(Rendering::Renderer* renderer); - // Avança timers interns (creació + reaccions). + // Avança timers interns (creació + ripples). void update(float delta_time); - // Pinta la graella. La porció dibuixada de cada línia depèn del timer intern. + // Pinta la graella. La porció dibuixada de cada línia depèn del timer intern, + // i s'aplica deformació radial per cada ripple activa que afecti la línia. void draw() const; - // Notifica que una nau ha passat per (pos) a velocitat (speed_px_s). - // Si està prop d'alguna línia i va prou ràpida, la línia entra en orbit. - void notifyShipPass(Vec2 pos, float speed_px_s); + // Notifica que una nau ha passat per (pos) a (speed_px_s). Genera ones + // petites darrere la nau a cadència regular amb jitter (estil TrailManager). + void notifyShipMoving(std::uint8_t player_id, Vec2 pos, float speed_px_s, float delta_time); - // Notifica el spawn d'un firework a (pos). Les línies V i H més properes - // generen un pulse brillant que es propaga. - void notifyFireworkSpawn(Vec2 pos); + // Notifica una explosió a (pos): genera una ripple gran centrada al punt. + void notifyExplosion(Vec2 pos); - private: - struct Pulse { - bool active{false}; - float center_t{0.5F}; // posició al llarg de la línia (0..1) + // Pública per accés des d'helpers a l'anonymous namespace del .cpp. + struct Ripple { + Vec2 center{}; float age_s{0.0F}; + float lifetime_s{0.0F}; + float speed_px_s{0.0F}; + float amplitude_px{0.0F}; + float thickness_px{0.0F}; + bool active{false}; }; + private: struct Line { - Vec2 start; // top (verticals) o left (horitzontals) - Vec2 end; // bottom (verticals) o right (horitzontals) - float brightness; // base (GRID_BRIGHTNESS o SUBGRID_BRIGHTNESS) - float spawn_time_s; // moment de naixement - bool is_vertical; // direcció (per saber el perpendicular de l'orbit) - float orbit_amplitude; // amplitud actual de l'orbit (px, ≥ 0) - float orbit_phase; // fase del sin (avança contínuament) - std::array pulses; + Vec2 start; // top (verticals) o left (horitzontals) + Vec2 end; // bottom (verticals) o right (horitzontals) + float brightness; // base (GRID_BRIGHTNESS o SUBGRID_BRIGHTNESS) + float spawn_time_s; // moment de naixement + bool is_vertical; // direcció }; void buildLines(); + void drawLine(const Line& line, const Ripple* const* active, int n_active) const; [[nodiscard]] auto computeLineProgress(const Line& line) const -> float; - static void spawnPulseAt(Line& line, float center_t); + void spawnBig(Vec2 pos); + void spawnSmall(Vec2 pos); + auto findFreeRipple() -> Ripple*; Rendering::Renderer* renderer_; std::vector lines_; + std::array ripples_{}; + std::array ship_ripple_cooldown_{}; float elapsed_s_{0.0F}; }; diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index b7b35ca..c9ea6cc 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -75,9 +75,9 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context) border_.bumpAt(hit.contact_point, STRENGTH); }); - // Fireworks generen un pulse a les línies V i H més properes del playfield. + // Fireworks generen una ripple gran al playfield (ona d'aigua centrada al burst). firework_manager_.setSpawnCallback([this](Vec2 origen) { - playfield_.notifyFireworkSpawn(origen); + playfield_.notifyExplosion(origen); }); // Explosions properes a una paret també generen bump (falloff lineal amb la distància). @@ -216,10 +216,13 @@ void GameScene::stepPhysics(float delta_time) { playfield_.update(delta_time); border_.update(delta_time); - // Notificar al playfield que la nau ha passat (per excitar línies properes). - for (const auto& ship : ships_) { - if (ship.isActive()) { - playfield_.notifyShipPass(ship.getCenter(), ship.getSpeed()); + // Notificar al playfield que la nau es mou (genera ripples petites a cadència). + for (std::size_t id = 0; id < ships_.size(); id++) { + if (ships_[id].isActive()) { + playfield_.notifyShipMoving(static_cast(id), + ships_[id].getCenter(), + ships_[id].getSpeed(), + delta_time); } } }