Updated game structure
This commit is contained in:
21
src/graphics/Font.cpp
Normal file
21
src/graphics/Font.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
// Font.cpp - implementation of FontAtlas (copied into src/graphics)
|
||||
#include "graphics/Font.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
bool FontAtlas::init(const std::string& path, int basePt) { fontPath = path; baseSize = basePt; return true; }
|
||||
|
||||
void FontAtlas::shutdown() { for (auto &kv : cache) if (kv.second) TTF_CloseFont(kv.second); cache.clear(); }
|
||||
|
||||
TTF_Font* FontAtlas::getSized(int ptSize) {
|
||||
auto it = cache.find(ptSize); if (it!=cache.end()) return it->second;
|
||||
TTF_Font* f = TTF_OpenFont(fontPath.c_str(), ptSize);
|
||||
if (!f) return nullptr; cache[ptSize] = f; return f;
|
||||
}
|
||||
|
||||
void FontAtlas::draw(SDL_Renderer* r, float x, float y, const std::string& text, float scale, SDL_Color color) {
|
||||
if (scale <= 0) return; int pt = int(baseSize * scale); if (pt < 8) pt = 8; TTF_Font* f = getSized(pt); if (!f) return;
|
||||
SDL_Surface* surf = TTF_RenderText_Blended(f, text.c_str(), text.length(), color); if (!surf) return;
|
||||
SDL_Texture* tex = SDL_CreateTextureFromSurface(r, surf);
|
||||
if (tex) { SDL_FRect dst{ x, y, (float)surf->w, (float)surf->h }; SDL_RenderTexture(r, tex, nullptr, &dst); SDL_DestroyTexture(tex); }
|
||||
SDL_DestroySurface(surf);
|
||||
}
|
||||
18
src/graphics/Font.h
Normal file
18
src/graphics/Font.h
Normal file
@ -0,0 +1,18 @@
|
||||
// Font.h - Font rendering abstraction with simple size cache
|
||||
#pragma once
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
struct SDL_Renderer;
|
||||
|
||||
class FontAtlas {
|
||||
public:
|
||||
bool init(const std::string& path, int basePt);
|
||||
void shutdown();
|
||||
void draw(SDL_Renderer* r, float x, float y, const std::string& text, float scale, SDL_Color color);
|
||||
private:
|
||||
std::string fontPath;
|
||||
int baseSize{24};
|
||||
std::unordered_map<int, TTF_Font*> cache; // point size -> font*
|
||||
TTF_Font* getSized(int ptSize);
|
||||
};
|
||||
41
src/graphics/Starfield.cpp
Normal file
41
src/graphics/Starfield.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Starfield.cpp - implementation (copied into src/graphics)
|
||||
#include "graphics/Starfield.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <random>
|
||||
|
||||
void Starfield::init(int count, int w, int h)
|
||||
{
|
||||
stars.clear();
|
||||
stars.reserve(count);
|
||||
std::mt19937 rng{std::random_device{}()};
|
||||
std::uniform_real_distribution<float> dx(0.f, (float)w), dy(0.f, (float)h), dz(0.2f, 1.f);
|
||||
for (int i = 0; i < count; ++i)
|
||||
stars.push_back({dx(rng), dy(rng), dz(rng), 15.f + 35.f * dz(rng)});
|
||||
lastW = w;
|
||||
lastH = h;
|
||||
}
|
||||
|
||||
void Starfield::update(float dt, int w, int h)
|
||||
{
|
||||
if (w != lastW || h != lastH || stars.empty())
|
||||
init((w * h) / 8000 + 120, w, h);
|
||||
for (auto &s : stars)
|
||||
{
|
||||
s.y += s.speed * dt;
|
||||
if (s.y > h)
|
||||
{
|
||||
s.y -= h;
|
||||
s.x = (float)(rand() % w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Starfield::draw(SDL_Renderer *r) const
|
||||
{
|
||||
SDL_SetRenderDrawColor(r, 255, 255, 255, 255);
|
||||
for (auto &s : stars)
|
||||
{
|
||||
SDL_FRect fr{s.x, s.y, 1.f * s.z, 1.f * s.z};
|
||||
SDL_RenderFillRect(r, &fr);
|
||||
}
|
||||
}
|
||||
15
src/graphics/Starfield.h
Normal file
15
src/graphics/Starfield.h
Normal file
@ -0,0 +1,15 @@
|
||||
// Starfield.h - Procedural starfield background effect
|
||||
#pragma once
|
||||
#include <vector>
|
||||
struct SDL_Renderer; // fwd
|
||||
|
||||
class Starfield {
|
||||
public:
|
||||
void init(int count, int w, int h);
|
||||
void update(float dt, int w, int h);
|
||||
void draw(SDL_Renderer* r) const;
|
||||
private:
|
||||
struct Star { float x,y,z,speed; };
|
||||
std::vector<Star> stars;
|
||||
int lastW{0}, lastH{0};
|
||||
};
|
||||
164
src/graphics/Starfield3D.cpp
Normal file
164
src/graphics/Starfield3D.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
// Starfield3D.cpp - 3D Parallax Starfield Implementation
|
||||
#include "Starfield3D.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
Starfield3D::Starfield3D() : rng(std::random_device{}()), width(800), height(600), centerX(400), centerY(300) {
|
||||
}
|
||||
|
||||
void Starfield3D::init(int w, int h, int starCount) {
|
||||
width = w;
|
||||
height = h;
|
||||
centerX = width * 0.5f;
|
||||
centerY = height * 0.5f;
|
||||
|
||||
stars.resize(starCount);
|
||||
createStarfield();
|
||||
}
|
||||
|
||||
void Starfield3D::resize(int w, int h) {
|
||||
width = w;
|
||||
height = h;
|
||||
centerX = width * 0.5f;
|
||||
centerY = height * 0.5f;
|
||||
}
|
||||
|
||||
float Starfield3D::randomFloat(float min, float max) {
|
||||
std::uniform_real_distribution<float> dist(min, max);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
int Starfield3D::randomRange(int min, int max) {
|
||||
std::uniform_int_distribution<int> dist(min, max - 1);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
void Starfield3D::setRandomDirection(Star3D& star) {
|
||||
star.targetVx = randomFloat(-MAX_VELOCITY, MAX_VELOCITY);
|
||||
star.targetVy = randomFloat(-MAX_VELOCITY, MAX_VELOCITY);
|
||||
|
||||
// Allow stars to move both toward and away from viewer
|
||||
if (randomFloat(0.0f, 1.0f) < REVERSE_PROBABILITY) {
|
||||
// Move away from viewer (positive Z)
|
||||
star.targetVz = STAR_SPEED * randomFloat(0.5f, 1.0f);
|
||||
} else {
|
||||
// Move toward viewer (negative Z)
|
||||
star.targetVz = -STAR_SPEED * randomFloat(0.7f, 1.3f);
|
||||
}
|
||||
|
||||
star.changing = true;
|
||||
star.changeTimer = randomFloat(30.0f, 120.0f); // Direction change lasts 30-120 frames
|
||||
}
|
||||
|
||||
void Starfield3D::updateStar(int index) {
|
||||
Star3D& star = stars[index];
|
||||
|
||||
star.x = randomFloat(-25.0f, 25.0f);
|
||||
star.y = randomFloat(-25.0f, 25.0f);
|
||||
star.z = randomFloat(1.0f, MAX_DEPTH);
|
||||
|
||||
// Give stars initial velocities in all possible directions
|
||||
if (randomFloat(0.0f, 1.0f) < 0.5f) {
|
||||
// Half stars start moving toward viewer
|
||||
star.vx = randomFloat(-0.1f, 0.1f);
|
||||
star.vy = randomFloat(-0.1f, 0.1f);
|
||||
star.vz = -STAR_SPEED * randomFloat(0.8f, 1.2f);
|
||||
} else {
|
||||
// Half stars start moving in random directions
|
||||
star.vx = randomFloat(-0.2f, 0.2f);
|
||||
star.vy = randomFloat(-0.2f, 0.2f);
|
||||
|
||||
// 30% chance to start moving away
|
||||
if (randomFloat(0.0f, 1.0f) < 0.3f) {
|
||||
star.vz = STAR_SPEED * randomFloat(0.5f, 0.8f);
|
||||
} else {
|
||||
star.vz = -STAR_SPEED * randomFloat(0.8f, 1.2f);
|
||||
}
|
||||
}
|
||||
|
||||
star.targetVx = star.vx;
|
||||
star.targetVy = star.vy;
|
||||
star.targetVz = star.vz;
|
||||
star.changing = false;
|
||||
star.changeTimer = 0.0f;
|
||||
star.type = randomRange(0, COLOR_COUNT);
|
||||
|
||||
// Give some stars initial direction variations
|
||||
if (randomFloat(0.0f, 1.0f) < 0.4f) {
|
||||
setRandomDirection(star);
|
||||
}
|
||||
}
|
||||
|
||||
void Starfield3D::createStarfield() {
|
||||
for (size_t i = 0; i < stars.size(); ++i) {
|
||||
updateStar(static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
|
||||
void Starfield3D::update(float deltaTime) {
|
||||
const float frameRate = 60.0f; // Target 60 FPS for consistency
|
||||
const float frameMultiplier = deltaTime * frameRate;
|
||||
|
||||
for (size_t i = 0; i < stars.size(); ++i) {
|
||||
Star3D& star = stars[i];
|
||||
|
||||
// Randomly change direction occasionally
|
||||
if (!star.changing && randomFloat(0.0f, 1.0f) < DIRECTION_CHANGE_PROBABILITY * frameMultiplier) {
|
||||
setRandomDirection(star);
|
||||
}
|
||||
|
||||
// Update velocities to approach target values
|
||||
if (star.changing) {
|
||||
// Smoothly transition to target velocities
|
||||
const float change = VELOCITY_CHANGE * frameMultiplier;
|
||||
star.vx += (star.targetVx - star.vx) * change;
|
||||
star.vy += (star.targetVy - star.vy) * change;
|
||||
star.vz += (star.targetVz - star.vz) * change;
|
||||
|
||||
// Decrement change timer
|
||||
star.changeTimer -= frameMultiplier;
|
||||
if (star.changeTimer <= 0.0f) {
|
||||
star.changing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update position using current velocity
|
||||
star.x += star.vx * frameMultiplier;
|
||||
star.y += star.vy * frameMultiplier;
|
||||
star.z += star.vz * frameMultiplier;
|
||||
|
||||
// Handle boundaries - reset star if it moves out of bounds, too close, or too far
|
||||
if (star.z <= MIN_Z ||
|
||||
star.z >= MAX_Z ||
|
||||
std::abs(star.x) > 50.0f ||
|
||||
std::abs(star.y) > 50.0f) {
|
||||
updateStar(static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Starfield3D::drawStar(SDL_Renderer* renderer, float x, float y, int type) {
|
||||
const SDL_Color& color = STAR_COLORS[type % COLOR_COUNT];
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
|
||||
// Draw star as a small rectangle (1x1 pixel)
|
||||
SDL_FRect rect{x, y, 1.0f, 1.0f};
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
}
|
||||
|
||||
void Starfield3D::draw(SDL_Renderer* renderer) {
|
||||
for (const Star3D& star : stars) {
|
||||
// Calculate perspective projection factor
|
||||
const float k = DEPTH_FACTOR / star.z;
|
||||
|
||||
// Calculate screen position with perspective
|
||||
const float px = star.x * k + centerX;
|
||||
const float py = star.y * k + centerY;
|
||||
|
||||
// Only draw stars that are within the viewport
|
||||
if (px >= 0.0f && px <= static_cast<float>(width) &&
|
||||
py >= 0.0f && py <= static_cast<float>(height)) {
|
||||
drawStar(renderer, px, py, star.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/graphics/Starfield3D.h
Normal file
63
src/graphics/Starfield3D.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Starfield3D.h - 3D Parallax Starfield Effect (canonical)
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <array>
|
||||
|
||||
class Starfield3D {
|
||||
public:
|
||||
Starfield3D();
|
||||
~Starfield3D() = default;
|
||||
|
||||
void init(int width, int height, int starCount = 160);
|
||||
void update(float deltaTime);
|
||||
void draw(SDL_Renderer* renderer);
|
||||
void resize(int width, int height);
|
||||
|
||||
private:
|
||||
struct Star3D {
|
||||
float x, y, z;
|
||||
float vx, vy, vz;
|
||||
float targetVx, targetVy, targetVz;
|
||||
float changeTimer;
|
||||
bool changing;
|
||||
int type;
|
||||
};
|
||||
|
||||
// Helpers used by the implementation
|
||||
void createStarfield();
|
||||
void updateStar(int index);
|
||||
void setRandomDirection(Star3D& star);
|
||||
float randomFloat(float min, float max);
|
||||
int randomRange(int min, int max);
|
||||
void drawStar(SDL_Renderer* renderer, float x, float y, int type);
|
||||
|
||||
std::vector<Star3D> stars;
|
||||
int width{0}, height{0};
|
||||
float centerX{0}, centerY{0};
|
||||
|
||||
// Random number generator
|
||||
std::mt19937 rng;
|
||||
|
||||
// Visual / behavioral constants (tweakable)
|
||||
inline static constexpr float MAX_VELOCITY = 0.5f;
|
||||
inline static constexpr float REVERSE_PROBABILITY = 0.12f;
|
||||
inline static constexpr float STAR_SPEED = 0.6f;
|
||||
inline static constexpr float MAX_DEPTH = 120.0f;
|
||||
inline static constexpr float DIRECTION_CHANGE_PROBABILITY = 0.002f;
|
||||
inline static constexpr float VELOCITY_CHANGE = 0.02f;
|
||||
inline static constexpr float MIN_Z = 0.1f;
|
||||
inline static constexpr float MAX_Z = MAX_DEPTH;
|
||||
inline static constexpr float DEPTH_FACTOR = 320.0f;
|
||||
|
||||
inline static constexpr int COLOR_COUNT = 5;
|
||||
inline static const std::array<SDL_Color, COLOR_COUNT> STAR_COLORS = {
|
||||
SDL_Color{255,255,255,255},
|
||||
SDL_Color{200,200,255,255},
|
||||
SDL_Color{255,220,180,255},
|
||||
SDL_Color{180,220,255,255},
|
||||
SDL_Color{255,180,200,255}
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user