mapcreator git · master
SDL3 2.5D game and engine using assets, with map editor
C++ 99.3%git clone https://git.christianimmanuel.de/sdl-graphics/mapcreator.gitwget https://git.christianimmanuel.de/sdl-graphics/mapcreator/archive/mapcreator.tar.gzsrc/start_screen/start_screen.cpp raw
#include "start_screen.hpp"
#include "audio/audio.hpp"
StartScreen::StartScreen(SDL_Renderer* r) : renderer(r) { Init(); }
StartScreen::~StartScreen() {
if (char_texture) SDL_DestroyTexture(char_texture);
if (logo_surface) SDL_DestroySurface(logo_surface);
if (props_texture) SDL_DestroyTexture(props_texture);
if (props_surface) SDL_DestroySurface(props_surface);
}
void StartScreen::StartShowProps() {
if (!props_surface) return;
phase = Phase::SHOW_PROPS;
phase_start = SDL_GetTicks();
// Extract visible pixels
const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(props_surface->format);
Uint32* pixels = static_cast<Uint32*>(props_surface->pixels);
int pitch = props_surface->pitch / (int)sizeof(Uint32);
int w = props_surface->w, h = props_surface->h;
particles.clear();
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
Uint32 p = pixels[y * pitch + x];
Uint8 r,g,b,a;
SDL_GetRGBA(p, details, NULL, &r, &g, &b, &a);
if (a > 180) { // or check color if you want black only
particles.push_back({ {static_cast<float>(x), static_cast<float>(y)},
{screen_w/2.0f, screen_h/2.0f}, // start from center
{screen_w/2.0f, screen_h/2.0f} });
}
}
}
}
void StartScreen::Update() {
Uint64 now = SDL_GetTicks();
float dt = static_cast<float>(now - last_time) / 1000.0f;
last_time = now;
switch (phase) {
case Phase::DOT:
if (now - phase_start > 500) StartSpreadToLine();
break;
case Phase::SPREAD_TO_LINE: {
float p = (now - phase_start) / 280.0f;
if (p >= 1.0f) { p = 1.0f; phase = Phase::LINE_WAIT; phase_start = now; }
for (auto& pt : particles) {
SDL_FPoint from = { center_dot.x, center_dot.y };
pt.current.x = Lerp(from.x, pt.target.x, p);
pt.current.y = Lerp(from.y, pt.target.y, p);
}
break;
}
case Phase::LINE_WAIT:
if (now - phase_start > 300) {
phase = Phase::RUNNING;
run_start_time = now;
char_x = -300.0f;
// Re-apply dynamic speed (in case resized just before running)
char_speed = (static_cast<float>(screen_w) + extra_distance) / desired_cross_time;
}
break;
case Phase::RUNNING: {
char_x += char_speed * dt;
Uint64 frame_ticks = now - run_start_time;
char_frame = static_cast<int>((frame_ticks / 80ULL) % 6ULL);
// End when character is fully off-screen right
if (char_x > static_cast<float>(screen_w) + 1420.0f) {
StartFadeToLogo();
}
break;
}
case Phase::FADE_TO_LOGO: {
float p = (now - phase_start) / 420.0f;
if (p >= 1.0f) { p = 1.0f; phase = Phase::SHOW_LOGO; phase_start = now; }
for (auto& pt : particles) {
SDL_FPoint logo_screen = GetScreenOriginal(pt);
pt.current.x = Lerp(pt.target.x, logo_screen.x, p);
pt.current.y = Lerp(pt.target.y, logo_screen.y, p);
}
break;
}
case Phase::SHOW_LOGO: {
if (now - phase_start > static_cast<Uint64>(logo_display_time * 1000.0f)) {
StartShowProps();
// Time's up → quit the application
//running = false; // will break main loop
// or: SDL_Quit(); return; // but better to let main clean up
}
break;
}
case Phase::SHOW_PROPS: {
float p = (now - phase_start) / 1000.0f; // 1 second animation
if (p >= 1.0f) p = 1.0f;
props_anim_progress = p;
for (auto& pt : particles) {
pt.current.x = Lerp(pt.current.x, pt.original.x + (screen_w - props_surface->w)/2.0f, p);
pt.current.y = Lerp(pt.current.y, pt.original.y + (screen_h - props_surface->h)/2.0f, p);
}
// optionally, end scene after animation finishes
if (p >= 1.0f && now - phase_start > 3500) { // 1.5s after animation ends
running = false;
}
break;
}
}
}
void StartScreen::Render() {
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
for (const auto& p : particles) {
SDL_FRect r { p.current.x, p.current.y, 1.0f, 1.0f };
SDL_RenderFillRect(renderer, &r);
}
if (phase == Phase::RUNNING) {
float scale = 4.0f; // double size as requested
float sw = char_frame_width * scale;
float sh = char_height * scale;
float dy = static_cast<float>(bottom_y) - sh + 46.0f; // feet on line
SDL_FRect src { static_cast<float>(char_frame * char_frame_width), 0.0f,
static_cast<float>(char_frame_width), static_cast<float>(char_height) };
SDL_FRect dst { char_x - (sw - char_frame_width) / 2.0f, dy, sw, sh };
SDL_RenderTexture(renderer, char_texture, &src, &dst);
}
else
if (phase == Phase::SHOW_PROPS) {
if (props_anim_progress < 2.0f) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
for (const auto& p : particles) {
SDL_FRect r { p.current.x, p.current.y, 1.0f, 1.0f };
SDL_RenderFillRect(renderer, &r);
}
} else {
SDL_FRect dst { (screen_w - props_surface->w)/2.0f,
(screen_h - props_surface->h)/2.0f,
static_cast<float>(props_surface->w),
static_cast<float>(props_surface->h) };
SDL_SetTextureAlphaMod(props_texture, 255);
SDL_RenderTexture(renderer, props_texture, nullptr, &dst);
}
}
SDL_RenderPresent(renderer);
}
void StartScreen::UpdateScreen() {
if (!logo_surface) return;
SDL_GetRenderOutputSize(renderer, &screen_w, &screen_h);
// Logo centered
int lw = logo_surface->w;
int lh = logo_surface->h;
logo_dest.x = (screen_w - lw) / 2;
logo_dest.y = (screen_h - lh) / 2 - 100;
logo_offset.x = static_cast<float>(logo_dest.x);
logo_offset.y = static_cast<float>(logo_dest.y);
// Central dot
center_dot.x = static_cast<float>(screen_w) / 2.0f;
center_dot.y = static_cast<float>(screen_h) * 0.65f;
// Ground bar — edge-to-edge friendly
int margin = 0;
int usable = screen_w - 2 * margin;
int pixels_per_full_row = usable / ground_row_height;
int total = static_cast<int>(particles.size());
int full_rows = total / pixels_per_full_row;
int rem = total % pixels_per_full_row;
std::vector<SDL_FPoint> line_targets;
line_targets.reserve(static_cast<size_t>(total));
bottom_y = static_cast<float>(screen_h) * 0.68f;
// Full rows — packed densely (as before)
for (int r = 0; r < full_rows; ++r) {
float y = bottom_y - static_cast<float>(r) * static_cast<float>(ground_row_height);
float row_start_x = (screen_w - pixels_per_full_row * ground_row_height) / 2.0f;
for (int c = 0; c < pixels_per_full_row; ++c) {
float x = row_start_x + static_cast<float>(c) * static_cast<float>(ground_row_height);
line_targets.push_back({x, y});
}
}
// Remainder row — evenly spaced across full width
if (rem > 0) {
float y = bottom_y - static_cast<float>(full_rows) * static_cast<float>(ground_row_height);
// Spread evenly with equal gaps
float spacing = static_cast<float>(usable) / static_cast<float>(rem + 1);
float first_x = static_cast<float>(margin) + spacing / 2.0f;
for (int c = 0; c < rem; ++c) {
float x = first_x + static_cast<float>(c) * spacing;
line_targets.push_back({x, y});
}
}
line_positions = std::move(line_targets);
// Character position above top of bar
float top_bar = bottom_y - static_cast<float>(full_rows + (rem > 0 ? 1 : 0)) * static_cast<float>(ground_row_height);
line_y = static_cast<int>(top_bar - static_cast<float>(char_height) * 2.0f - 20.0f);
}
void StartScreen::Init() {
logo_surface = IMG_Load("logo/logo.png");
char_texture = IMG_LoadTexture(renderer, "logo/Run_Side-Sheet.png");
props_surface = IMG_Load("logo/props.png");
props_texture = SDL_CreateTextureFromSurface(renderer, props_surface);
SDL_GetRenderOutputSize(renderer, &screen_w, &screen_h);
// Extract black pixels (local coords)
const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(logo_surface->format);
Uint32* pixels = static_cast<Uint32*>(logo_surface->pixels);
int pitch = logo_surface->pitch / (int)sizeof(Uint32);
int w = logo_surface->w, h = logo_surface->h;
particles.clear();
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
Uint32 p = pixels[y * pitch + x];
Uint8 r,g,b,a;
SDL_GetRGBA(p, details, NULL, &r, &g, &b, &a);
if (r < 20 && g < 20 && b < 20 && a > 180) {
particles.push_back({ {static_cast<float>(x), static_cast<float>(y)},
{0,0}, {0,0} });
}
}
}
UpdateScreen(); // compute dot + line positions
// Set all particles to center dot for first phase
for (auto& p : particles) {
p.current.x = center_dot.x;
p.current.y = center_dot.y;
p.target = center_dot;
}
rng.seed(std::random_device{}());
last_time = SDL_GetTicks();
phase_start = last_time;
phase = Phase::DOT;
char_speed = (static_cast<float>(screen_w) + extra_distance) / desired_cross_time;
//char_speed = 520.0f;
char_frame_width = 64;
char_height = 64;
}
void StartScreen::StartSpreadToLine() {
phase = Phase::SPREAD_TO_LINE;
phase_start = SDL_GetTicks();
int N = static_cast<int>(particles.size());
std::vector<size_t> perm((size_t)N);
std::iota(perm.begin(), perm.end(), 0);
std::shuffle(perm.begin(), perm.end(), rng);
for (size_t i = 0; i < (size_t)N; ++i) {
particles[i].target = line_positions[perm[i]];
}
}
void StartScreen::StartFadeToLogo() {
phase = Phase::FADE_TO_LOGO;
phase_start = SDL_GetTicks();
// targets stay at line positions → lerp back to logo in Update()
}
SDL_FPoint StartScreen::GetScreenOriginal(const Particle& p) const {
return { p.original.x + logo_offset.x, p.original.y + logo_offset.y };
}
float StartScreen::Lerp(float a, float b, float t) {
return a + (b - a) * t;
}
namespace StartScreen_Event {
void event_Loop(SDL_Renderer* renderer) {
Audio audio;
audio.RegisterSound("intro", "music/intro.wav");
StartScreen* scene = new StartScreen(renderer);
audio.Play("intro");
while (scene->running) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) scene->running = false;
if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_SPACE) {
scene->~StartScreen();
scene = new StartScreen(renderer); // restart from DOT
}
if (e.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED ||
e.type == SDL_EVENT_WINDOW_RESIZED) {
scene->UpdateScreen();
}
}
scene->Update();
scene->Render();
}
}
}