transition effect when afvance level
This commit is contained in:
136
src/main.cpp
136
src/main.cpp
@ -108,15 +108,35 @@ static SDL_Texture* loadTextureFromImage(SDL_Renderer* renderer, const std::stri
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class LevelBackgroundPhase { Idle, ZoomOut, ZoomIn };
|
||||||
|
|
||||||
struct LevelBackgroundFader {
|
struct LevelBackgroundFader {
|
||||||
SDL_Texture* currentTex = nullptr;
|
SDL_Texture* currentTex = nullptr;
|
||||||
SDL_Texture* nextTex = nullptr;
|
SDL_Texture* nextTex = nullptr;
|
||||||
int currentLevel = -1;
|
int currentLevel = -1;
|
||||||
int queuedLevel = -1;
|
int queuedLevel = -1;
|
||||||
float fadeElapsedMs = 0.0f;
|
float phaseElapsedMs = 0.0f;
|
||||||
|
float phaseDurationMs = 0.0f;
|
||||||
float fadeDurationMs = Config::Gameplay::LEVEL_FADE_DURATION;
|
float fadeDurationMs = Config::Gameplay::LEVEL_FADE_DURATION;
|
||||||
|
LevelBackgroundPhase phase = LevelBackgroundPhase::Idle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static float getPhaseDurationMs(const LevelBackgroundFader& fader, LevelBackgroundPhase phase) {
|
||||||
|
const float total = std::max(1200.0f, fader.fadeDurationMs);
|
||||||
|
switch (phase) {
|
||||||
|
case LevelBackgroundPhase::ZoomOut: return total * 0.45f;
|
||||||
|
case LevelBackgroundPhase::ZoomIn: return total * 0.45f;
|
||||||
|
case LevelBackgroundPhase::Idle:
|
||||||
|
default: return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setPhase(LevelBackgroundFader& fader, LevelBackgroundPhase nextPhase) {
|
||||||
|
fader.phase = nextPhase;
|
||||||
|
fader.phaseDurationMs = getPhaseDurationMs(fader, nextPhase);
|
||||||
|
fader.phaseElapsedMs = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
static void destroyTexture(SDL_Texture*& tex) {
|
static void destroyTexture(SDL_Texture*& tex) {
|
||||||
if (tex) {
|
if (tex) {
|
||||||
SDL_DestroyTexture(tex);
|
SDL_DestroyTexture(tex);
|
||||||
@ -146,32 +166,90 @@ static bool queueLevelBackground(LevelBackgroundFader& fader, SDL_Renderer* rend
|
|||||||
destroyTexture(fader.nextTex);
|
destroyTexture(fader.nextTex);
|
||||||
fader.nextTex = newTexture;
|
fader.nextTex = newTexture;
|
||||||
fader.queuedLevel = level;
|
fader.queuedLevel = level;
|
||||||
fader.fadeElapsedMs = 0.0f;
|
|
||||||
|
|
||||||
if (!fader.currentTex) {
|
if (!fader.currentTex) {
|
||||||
|
// First background load happens instantly.
|
||||||
fader.currentTex = fader.nextTex;
|
fader.currentTex = fader.nextTex;
|
||||||
fader.currentLevel = fader.queuedLevel;
|
fader.currentLevel = fader.queuedLevel;
|
||||||
fader.nextTex = nullptr;
|
fader.nextTex = nullptr;
|
||||||
fader.queuedLevel = -1;
|
fader.queuedLevel = -1;
|
||||||
|
fader.phase = LevelBackgroundPhase::Idle;
|
||||||
|
fader.phaseElapsedMs = 0.0f;
|
||||||
|
fader.phaseDurationMs = 0.0f;
|
||||||
|
} else if (fader.phase == LevelBackgroundPhase::Idle) {
|
||||||
|
// Kick off fancy transition.
|
||||||
|
setPhase(fader, LevelBackgroundPhase::ZoomOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateLevelBackgroundFade(LevelBackgroundFader& fader, float frameMs) {
|
static void updateLevelBackgroundFade(LevelBackgroundFader& fader, float frameMs) {
|
||||||
if (!fader.currentTex || !fader.nextTex) {
|
if (fader.phase == LevelBackgroundPhase::Idle) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fader.fadeElapsedMs += frameMs;
|
// Guard against missing textures
|
||||||
if (fader.fadeElapsedMs >= fader.fadeDurationMs) {
|
if (!fader.currentTex && !fader.nextTex) {
|
||||||
|
fader.phase = LevelBackgroundPhase::Idle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fader.phaseElapsedMs += frameMs;
|
||||||
|
if (fader.phaseElapsedMs < std::max(1.0f, fader.phaseDurationMs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fader.phase) {
|
||||||
|
case LevelBackgroundPhase::ZoomOut:
|
||||||
|
// After zoom-out, swap textures then start zoom-in.
|
||||||
|
if (fader.nextTex) {
|
||||||
destroyTexture(fader.currentTex);
|
destroyTexture(fader.currentTex);
|
||||||
fader.currentTex = fader.nextTex;
|
fader.currentTex = fader.nextTex;
|
||||||
fader.currentLevel = fader.queuedLevel;
|
fader.currentLevel = fader.queuedLevel;
|
||||||
fader.nextTex = nullptr;
|
fader.nextTex = nullptr;
|
||||||
fader.queuedLevel = -1;
|
fader.queuedLevel = -1;
|
||||||
fader.fadeElapsedMs = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
setPhase(fader, LevelBackgroundPhase::ZoomIn);
|
||||||
|
break;
|
||||||
|
case LevelBackgroundPhase::ZoomIn:
|
||||||
|
fader.phase = LevelBackgroundPhase::Idle;
|
||||||
|
fader.phaseElapsedMs = 0.0f;
|
||||||
|
fader.phaseDurationMs = 0.0f;
|
||||||
|
break;
|
||||||
|
case LevelBackgroundPhase::Idle:
|
||||||
|
default:
|
||||||
|
fader.phase = LevelBackgroundPhase::Idle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renderScaledBackground(SDL_Renderer* renderer, SDL_Texture* tex, int winW, int winH, float scale, Uint8 alpha = 255) {
|
||||||
|
if (!renderer || !tex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scale = std::max(0.5f, scale);
|
||||||
|
SDL_FRect dest{
|
||||||
|
(winW - winW * scale) * 0.5f,
|
||||||
|
(winH - winH * scale) * 0.5f,
|
||||||
|
winW * scale,
|
||||||
|
winH * scale
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_SetTextureAlphaMod(tex, alpha);
|
||||||
|
SDL_RenderTexture(renderer, tex, nullptr, &dest);
|
||||||
|
SDL_SetTextureAlphaMod(tex, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawOverlay(SDL_Renderer* renderer, const SDL_FRect& rect, SDL_Color color, Uint8 alpha) {
|
||||||
|
if (!renderer || alpha == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||||
|
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, alpha);
|
||||||
|
SDL_RenderFillRect(renderer, &rect);
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Renderer* renderer, int winW, int winH) {
|
static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Renderer* renderer, int winW, int winH) {
|
||||||
@ -180,22 +258,36 @@ static void renderLevelBackgrounds(const LevelBackgroundFader& fader, SDL_Render
|
|||||||
}
|
}
|
||||||
|
|
||||||
SDL_FRect fullRect{0.f, 0.f, static_cast<float>(winW), static_cast<float>(winH)};
|
SDL_FRect fullRect{0.f, 0.f, static_cast<float>(winW), static_cast<float>(winH)};
|
||||||
|
const float duration = std::max(1.0f, fader.phaseDurationMs);
|
||||||
|
const float progress = (fader.phase == LevelBackgroundPhase::Idle) ? 0.0f : std::clamp(fader.phaseElapsedMs / duration, 0.0f, 1.0f);
|
||||||
|
|
||||||
if (fader.currentTex && fader.nextTex) {
|
switch (fader.phase) {
|
||||||
const float duration = std::max(1.0f, fader.fadeDurationMs);
|
case LevelBackgroundPhase::ZoomOut: {
|
||||||
const float alpha = std::clamp(fader.fadeElapsedMs / duration, 0.0f, 1.0f);
|
const float scale = 1.0f + progress * 0.15f;
|
||||||
|
if (fader.currentTex) {
|
||||||
SDL_SetTextureAlphaMod(fader.currentTex, Uint8((1.0f - alpha) * 255.0f));
|
renderScaledBackground(renderer, fader.currentTex, winW, winH, scale, Uint8((1.0f - progress * 0.4f) * 255.0f));
|
||||||
SDL_RenderTexture(renderer, fader.currentTex, nullptr, &fullRect);
|
drawOverlay(renderer, fullRect, SDL_Color{0, 0, 0, 255}, Uint8(progress * 200.0f));
|
||||||
SDL_SetTextureAlphaMod(fader.currentTex, 255);
|
}
|
||||||
|
break;
|
||||||
SDL_SetTextureAlphaMod(fader.nextTex, Uint8(alpha * 255.0f));
|
}
|
||||||
SDL_RenderTexture(renderer, fader.nextTex, nullptr, &fullRect);
|
case LevelBackgroundPhase::ZoomIn: {
|
||||||
SDL_SetTextureAlphaMod(fader.nextTex, 255);
|
const float scale = 1.10f - progress * 0.10f;
|
||||||
} else if (fader.currentTex) {
|
const Uint8 alpha = Uint8((0.4f + progress * 0.6f) * 255.0f);
|
||||||
SDL_RenderTexture(renderer, fader.currentTex, nullptr, &fullRect);
|
if (fader.currentTex) {
|
||||||
|
renderScaledBackground(renderer, fader.currentTex, winW, winH, scale, alpha);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LevelBackgroundPhase::Idle:
|
||||||
|
default:
|
||||||
|
if (fader.currentTex) {
|
||||||
|
renderScaledBackground(renderer, fader.currentTex, winW, winH, 1.0f, 255);
|
||||||
} else if (fader.nextTex) {
|
} else if (fader.nextTex) {
|
||||||
SDL_RenderTexture(renderer, fader.nextTex, nullptr, &fullRect);
|
renderScaledBackground(renderer, fader.nextTex, winW, winH, 1.0f, 255);
|
||||||
|
} else {
|
||||||
|
drawOverlay(renderer, fullRect, SDL_Color{0, 0, 0, 255}, 255);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +296,9 @@ static void resetLevelBackgrounds(LevelBackgroundFader& fader) {
|
|||||||
destroyTexture(fader.nextTex);
|
destroyTexture(fader.nextTex);
|
||||||
fader.currentLevel = -1;
|
fader.currentLevel = -1;
|
||||||
fader.queuedLevel = -1;
|
fader.queuedLevel = -1;
|
||||||
fader.fadeElapsedMs = 0.0f;
|
fader.phaseElapsedMs = 0.0f;
|
||||||
|
fader.phaseDurationMs = 0.0f;
|
||||||
|
fader.phase = LevelBackgroundPhase::Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hover state for level popup ( -1 = none, 0..19 = hovered level )
|
// Hover state for level popup ( -1 = none, 0..19 = hovered level )
|
||||||
|
|||||||
Reference in New Issue
Block a user