feat(starfield): capa parallax al fons del playfield amb tint blanc-cyan

This commit is contained in:
2026-05-22 19:46:57 +02:00
parent 88b002b277
commit 1db7368c9f
6 changed files with 248 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
// starfield_parallax.cpp - Implementació del starfield 2D amb parallax
// © 2026 JailDesigner
#include "core/graphics/starfield_parallax.hpp"
#include <cstdlib>
#include "core/defaults.hpp"
#include "core/rendering/line_renderer.hpp"
namespace Graphics {
namespace {
auto randUniform(float min_v, float max_v) -> float {
const float NORM = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
return min_v + (NORM * (max_v - min_v));
}
} // namespace
StarfieldParallax::StarfieldParallax(Rendering::Renderer* renderer)
: renderer_(renderer) {
buildStars();
}
void StarfieldParallax::buildStars() {
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
const float MIN_X = zona.x;
const float MAX_X = zona.x + zona.w;
const float MIN_Y = zona.y;
const float MAX_Y = zona.y + zona.h;
// Tint aleatori entre blanc (255,255,255) i cyan (0,255,255) per estrella.
// T ∈ [0,1]: 0 → blanc; 1 → cyan. R = 255·(1-T), G=B=255.
const auto FILL_LAYER = [&](int layer, int count, int& idx) {
for (int i = 0; i < count; i++) {
const float T = randUniform(0.0F, 1.0F);
stars_[idx++] = Star{
.x = randUniform(MIN_X, MAX_X),
.y = randUniform(MIN_Y, MAX_Y),
.layer = layer,
.color = SDL_Color{
.r = static_cast<Uint8>(255.0F * (1.0F - T)),
.g = 255,
.b = 255,
.a = 255}};
}
};
int idx = 0;
FILL_LAYER(0, Defaults::StarfieldParallax::Far::COUNT, idx);
FILL_LAYER(1, Defaults::StarfieldParallax::Mid::COUNT, idx);
FILL_LAYER(2, Defaults::StarfieldParallax::Near::COUNT, idx);
}
auto StarfieldParallax::layerBrightness(int layer) -> float {
switch (layer) {
case 0:
return Defaults::StarfieldParallax::Far::BRIGHTNESS;
case 1:
return Defaults::StarfieldParallax::Mid::BRIGHTNESS;
case 2:
return Defaults::StarfieldParallax::Near::BRIGHTNESS;
default:
return 0.0F;
}
}
auto StarfieldParallax::layerParallax(int layer) -> float {
switch (layer) {
case 0:
return Defaults::StarfieldParallax::Far::PARALLAX_FACTOR;
case 1:
return Defaults::StarfieldParallax::Mid::PARALLAX_FACTOR;
case 2:
return Defaults::StarfieldParallax::Near::PARALLAX_FACTOR;
default:
return 0.0F;
}
}
auto StarfieldParallax::layerSize(int layer) -> int {
switch (layer) {
case 0:
return Defaults::StarfieldParallax::Far::SIZE_PX;
case 1:
return Defaults::StarfieldParallax::Mid::SIZE_PX;
case 2:
return Defaults::StarfieldParallax::Near::SIZE_PX;
default:
return 1;
}
}
void StarfieldParallax::update(float delta_time, Vec2 world_velocity) {
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
const float MIN_X = zona.x;
const float MAX_X = zona.x + zona.w;
const float MIN_Y = zona.y;
const float MAX_Y = zona.y + zona.h;
const float W = zona.w;
const float H = zona.h;
for (auto& star : stars_) {
const float FACTOR = layerParallax(star.layer);
star.x += world_velocity.x * FACTOR * delta_time;
star.y += world_velocity.y * FACTOR * delta_time;
// Wraparound (PLAYAREA torica).
while (star.x < MIN_X) {
star.x += W;
}
while (star.x > MAX_X) {
star.x -= W;
}
while (star.y < MIN_Y) {
star.y += H;
}
while (star.y > MAX_Y) {
star.y -= H;
}
}
}
void StarfieldParallax::draw() const {
for (const auto& star : stars_) {
const float B = layerBrightness(star.layer);
const int SIZE = layerSize(star.layer);
const int X = static_cast<int>(star.x);
const int Y = static_cast<int>(star.y);
if (SIZE <= 1) {
// Punt d'1 px: línia degenerada horitzontal de 1 px.
Rendering::linea(renderer_, X, Y, X + 1, Y, B, 0.0F, star.color);
} else {
// Creu "+" amb extensió HALF des del centre en cada direcció.
const int HALF = SIZE - 1; // SIZE=2 → ±1 (creu 3x3); SIZE=3 → ±2 (creu 5x5)
Rendering::linea(renderer_, X - HALF, Y, X + HALF + 1, Y, B, 0.0F, star.color);
Rendering::linea(renderer_, X, Y - HALF, X, Y + HALF + 1, B, 0.0F, star.color);
}
}
}
} // namespace Graphics
@@ -0,0 +1,51 @@
// starfield_parallax.hpp - Capa més profunda del fons: estrelles 2D amb parallax
// © 2026 JailDesigner
//
// Estrelles 2D distribuïdes en 3 capes de profunditat. Cada capa té el seu
// factor parallax: el "món" es desplaça amb world_velocity i les estrelles
// d'una capa es mouen amb world_velocity * parallax_factor. Les més
// properes es mouen més (factor alt) → sensació de profunditat.
// Quan una estrella surt de PLAYAREA, reapareix per la banda oposada
// (wraparound).
#pragma once
#include <SDL3/SDL.h>
#include <array>
#include "core/defaults/starfield_parallax.hpp"
#include "core/rendering/render_context.hpp"
#include "core/types.hpp"
namespace Graphics {
class StarfieldParallax {
public:
explicit StarfieldParallax(Rendering::Renderer* renderer);
// Avança el desplaçament de les estrelles segons world_velocity (vector
// del moviment del món en px/s; típicament = -ship_velocity).
// world_velocity == {0, 0} → estrelles quietes.
void update(float delta_time, Vec2 world_velocity);
void draw() const;
private:
struct Star {
float x{0.0F};
float y{0.0F};
int layer{0}; // 0=Far, 1=Mid, 2=Near
SDL_Color color{}; // tint precomputat entre blanc i cyan
};
void buildStars();
static auto layerBrightness(int layer) -> float;
static auto layerParallax(int layer) -> float;
static auto layerSize(int layer) -> int;
Rendering::Renderer* renderer_;
std::array<Star, Defaults::StarfieldParallax::TOTAL_COUNT> stars_{};
};
} // namespace Graphics