fade effect when switching states
This commit is contained in:
66
src/main.cpp
66
src/main.cpp
@ -913,6 +913,31 @@ int main(int, char **)
|
|||||||
running = false;
|
running = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto beginStateFade = [&](AppState targetState, bool armGameplayCountdown) {
|
||||||
|
if (!ctx.stateManager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == targetState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (menuFadePhase != MenuFadePhase::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
menuFadePhase = MenuFadePhase::FadeOut;
|
||||||
|
menuFadeClockMs = 0.0;
|
||||||
|
menuFadeAlpha = 0.0f;
|
||||||
|
menuFadeTarget = targetState;
|
||||||
|
menuPlayCountdownArmed = armGameplayCountdown;
|
||||||
|
gameplayCountdownActive = false;
|
||||||
|
gameplayCountdownIndex = 0;
|
||||||
|
gameplayCountdownElapsed = 0.0;
|
||||||
|
|
||||||
|
if (!armGameplayCountdown) {
|
||||||
|
game.setPaused(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
auto startMenuPlayTransition = [&]() {
|
auto startMenuPlayTransition = [&]() {
|
||||||
if (!ctx.stateManager) {
|
if (!ctx.stateManager) {
|
||||||
return;
|
return;
|
||||||
@ -922,20 +947,22 @@ int main(int, char **)
|
|||||||
ctx.stateManager->setState(state);
|
ctx.stateManager->setState(state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (menuFadePhase != MenuFadePhase::None) {
|
beginStateFade(AppState::Playing, true);
|
||||||
return;
|
|
||||||
}
|
|
||||||
menuFadePhase = MenuFadePhase::FadeOut;
|
|
||||||
menuFadeClockMs = 0.0;
|
|
||||||
menuFadeAlpha = 0.0f;
|
|
||||||
menuFadeTarget = AppState::Playing;
|
|
||||||
menuPlayCountdownArmed = true;
|
|
||||||
gameplayCountdownActive = false;
|
|
||||||
gameplayCountdownIndex = 0;
|
|
||||||
gameplayCountdownElapsed = 0.0;
|
|
||||||
};
|
};
|
||||||
ctx.startPlayTransition = startMenuPlayTransition;
|
ctx.startPlayTransition = startMenuPlayTransition;
|
||||||
|
|
||||||
|
auto requestStateFade = [&](AppState targetState) {
|
||||||
|
if (!ctx.stateManager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (targetState == AppState::Playing) {
|
||||||
|
startMenuPlayTransition();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
beginStateFade(targetState, false);
|
||||||
|
};
|
||||||
|
ctx.requestFadeTransition = requestStateFade;
|
||||||
|
|
||||||
// Instantiate state objects
|
// Instantiate state objects
|
||||||
auto loadingState = std::make_unique<LoadingState>(ctx);
|
auto loadingState = std::make_unique<LoadingState>(ctx);
|
||||||
auto menuState = std::make_unique<MenuState>(ctx);
|
auto menuState = std::make_unique<MenuState>(ctx);
|
||||||
@ -1100,11 +1127,9 @@ int main(int, char **)
|
|||||||
if (pointInRect(buttonRects[0])) {
|
if (pointInRect(buttonRects[0])) {
|
||||||
startMenuPlayTransition();
|
startMenuPlayTransition();
|
||||||
} else if (pointInRect(buttonRects[1])) {
|
} else if (pointInRect(buttonRects[1])) {
|
||||||
state = AppState::LevelSelector;
|
requestStateFade(AppState::LevelSelector);
|
||||||
stateMgr.setState(state);
|
|
||||||
} else if (pointInRect(buttonRects[2])) {
|
} else if (pointInRect(buttonRects[2])) {
|
||||||
state = AppState::Options;
|
requestStateFade(AppState::Options);
|
||||||
stateMgr.setState(state);
|
|
||||||
} else if (pointInRect(buttonRects[3])) {
|
} else if (pointInRect(buttonRects[3])) {
|
||||||
showExitConfirmPopup = true;
|
showExitConfirmPopup = true;
|
||||||
exitPopupSelectedButton = 1;
|
exitPopupSelectedButton = 1;
|
||||||
@ -1461,14 +1486,23 @@ int main(int, char **)
|
|||||||
menuFadeClockMs += frameMs;
|
menuFadeClockMs += frameMs;
|
||||||
menuFadeAlpha = std::min(1.0f, float(menuFadeClockMs / MENU_PLAY_FADE_DURATION_MS));
|
menuFadeAlpha = std::min(1.0f, float(menuFadeClockMs / MENU_PLAY_FADE_DURATION_MS));
|
||||||
if (menuFadeClockMs >= MENU_PLAY_FADE_DURATION_MS) {
|
if (menuFadeClockMs >= MENU_PLAY_FADE_DURATION_MS) {
|
||||||
if (menuFadeTarget == AppState::Playing) {
|
if (state != menuFadeTarget) {
|
||||||
state = menuFadeTarget;
|
state = menuFadeTarget;
|
||||||
stateMgr.setState(state);
|
stateMgr.setState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menuFadeTarget == AppState::Playing) {
|
||||||
menuPlayCountdownArmed = true;
|
menuPlayCountdownArmed = true;
|
||||||
gameplayCountdownActive = false;
|
gameplayCountdownActive = false;
|
||||||
gameplayCountdownIndex = 0;
|
gameplayCountdownIndex = 0;
|
||||||
gameplayCountdownElapsed = 0.0;
|
gameplayCountdownElapsed = 0.0;
|
||||||
game.setPaused(true);
|
game.setPaused(true);
|
||||||
|
} else {
|
||||||
|
menuPlayCountdownArmed = false;
|
||||||
|
gameplayCountdownActive = false;
|
||||||
|
gameplayCountdownIndex = 0;
|
||||||
|
gameplayCountdownElapsed = 0.0;
|
||||||
|
game.setPaused(false);
|
||||||
}
|
}
|
||||||
menuFadePhase = MenuFadePhase::FadeIn;
|
menuFadePhase = MenuFadePhase::FadeIn;
|
||||||
menuFadeClockMs = MENU_PLAY_FADE_DURATION_MS;
|
menuFadeClockMs = MENU_PLAY_FADE_DURATION_MS;
|
||||||
|
|||||||
@ -326,14 +326,18 @@ void LevelSelectorState::selectLevel(int level) {
|
|||||||
*ctx.startLevelSelection = level;
|
*ctx.startLevelSelection = level;
|
||||||
}
|
}
|
||||||
// Transition back to menu
|
// Transition back to menu
|
||||||
if (ctx.stateManager) {
|
if (ctx.requestFadeTransition) {
|
||||||
|
ctx.requestFadeTransition(AppState::Menu);
|
||||||
|
} else if (ctx.stateManager) {
|
||||||
ctx.stateManager->setState(AppState::Menu);
|
ctx.stateManager->setState(AppState::Menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelSelectorState::closePopup() {
|
void LevelSelectorState::closePopup() {
|
||||||
// Transition back to menu without changing level
|
// Transition back to menu without changing level
|
||||||
if (ctx.stateManager) {
|
if (ctx.requestFadeTransition) {
|
||||||
|
ctx.requestFadeTransition(AppState::Menu);
|
||||||
|
} else if (ctx.stateManager) {
|
||||||
ctx.stateManager->setState(AppState::Menu);
|
ctx.stateManager->setState(AppState::Menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,10 +126,18 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
triggerPlay();
|
triggerPlay();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ctx.stateManager->setState(AppState::LevelSelector);
|
if (ctx.requestFadeTransition) {
|
||||||
|
ctx.requestFadeTransition(AppState::LevelSelector);
|
||||||
|
} else if (ctx.stateManager) {
|
||||||
|
ctx.stateManager->setState(AppState::LevelSelector);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ctx.stateManager->setState(AppState::Options);
|
if (ctx.requestFadeTransition) {
|
||||||
|
ctx.requestFadeTransition(AppState::Options);
|
||||||
|
} else if (ctx.stateManager) {
|
||||||
|
ctx.stateManager->setState(AppState::Options);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
setExitPrompt(true);
|
setExitPrompt(true);
|
||||||
|
|||||||
@ -267,7 +267,9 @@ void OptionsState::toggleSoundFx() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OptionsState::exitToMenu() {
|
void OptionsState::exitToMenu() {
|
||||||
if (ctx.stateManager) {
|
if (ctx.requestFadeTransition) {
|
||||||
|
ctx.requestFadeTransition(AppState::Menu);
|
||||||
|
} else if (ctx.stateManager) {
|
||||||
ctx.stateManager->setState(AppState::Menu);
|
ctx.stateManager->setState(AppState::Menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ class Starfield;
|
|||||||
class Starfield3D;
|
class Starfield3D;
|
||||||
class FontAtlas;
|
class FontAtlas;
|
||||||
class LineEffect;
|
class LineEffect;
|
||||||
|
enum class AppState;
|
||||||
|
|
||||||
// Forward declare StateManager so StateContext can hold a pointer without
|
// Forward declare StateManager so StateContext can hold a pointer without
|
||||||
// including the StateManager header here.
|
// including the StateManager header here.
|
||||||
@ -58,6 +59,7 @@ struct StateContext {
|
|||||||
std::function<bool()> queryFullscreen; // Optional callback if fullscreenFlag is not reliable
|
std::function<bool()> queryFullscreen; // Optional callback if fullscreenFlag is not reliable
|
||||||
std::function<void()> requestQuit; // Allows menu/option states to close the app gracefully
|
std::function<void()> requestQuit; // Allows menu/option states to close the app gracefully
|
||||||
std::function<void()> startPlayTransition; // Optional fade hook when transitioning from menu to gameplay
|
std::function<void()> startPlayTransition; // Optional fade hook when transitioning from menu to gameplay
|
||||||
|
std::function<void(AppState)> requestFadeTransition; // Generic state fade requests (menu/options/level)
|
||||||
// Pointer to the application's StateManager so states can request transitions
|
// Pointer to the application's StateManager so states can request transitions
|
||||||
StateManager* stateManager = nullptr;
|
StateManager* stateManager = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user