feat(starfield): capa parallax al fons del playfield amb tint blanc-cyan
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user