feat: Implement fizzle fade transition effects for menus and intros, enhancing visual transitions
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -13,6 +13,10 @@ enum WolfMenuScreen {
|
||||
|
||||
enum WolfIntroSlide { retailWarning, pg13, title }
|
||||
|
||||
enum WolfTransitionEffect { none, normalFade, fizzleFade }
|
||||
|
||||
enum WolfTransitionPhase { idle, covering, revealing }
|
||||
|
||||
enum _WolfIntroPhase { fadeIn, hold, fadeOut }
|
||||
|
||||
enum WolfMenuMainAction {
|
||||
@@ -102,9 +106,11 @@ class MenuManager {
|
||||
|
||||
WolfMenuScreen _activeMenu = WolfMenuScreen.difficultySelect;
|
||||
WolfMenuScreen? _transitionTarget;
|
||||
WolfTransitionEffect _transitionEffect = WolfTransitionEffect.normalFade;
|
||||
int _transitionElapsedMs = 0;
|
||||
bool _transitionSwappedMenu = false;
|
||||
WolfMenuScreen _introLandingMenu = WolfMenuScreen.mainMenu;
|
||||
WolfTransitionEffect _introEffect = WolfTransitionEffect.normalFade;
|
||||
int _introSlideIndex = 0;
|
||||
int _introElapsedMs = 0;
|
||||
_WolfIntroPhase _introPhase = _WolfIntroPhase.fadeIn;
|
||||
@@ -182,6 +188,34 @@ class MenuManager {
|
||||
}
|
||||
}
|
||||
|
||||
WolfTransitionEffect get introOverlayEffect {
|
||||
if (!isIntroSplashActive || _introPhase == _WolfIntroPhase.hold) {
|
||||
return WolfTransitionEffect.none;
|
||||
}
|
||||
return _introEffect;
|
||||
}
|
||||
|
||||
WolfTransitionPhase get introOverlayPhase {
|
||||
if (!isIntroSplashActive) {
|
||||
return WolfTransitionPhase.idle;
|
||||
}
|
||||
switch (_introPhase) {
|
||||
case _WolfIntroPhase.fadeIn:
|
||||
return WolfTransitionPhase.revealing;
|
||||
case _WolfIntroPhase.hold:
|
||||
return WolfTransitionPhase.idle;
|
||||
case _WolfIntroPhase.fadeOut:
|
||||
return WolfTransitionPhase.covering;
|
||||
}
|
||||
}
|
||||
|
||||
double get introOverlayPhaseProgress {
|
||||
if (!isIntroSplashActive || _introPhase == _WolfIntroPhase.hold) {
|
||||
return 0.0;
|
||||
}
|
||||
return (_introElapsedMs / introFadeDurationMs).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
/// Returns the fade alpha during transitions (0.0..1.0).
|
||||
double get transitionAlpha {
|
||||
if (!isTransitioning) {
|
||||
@@ -195,6 +229,35 @@ class MenuManager {
|
||||
return (1.0 - (fadeInElapsed / half)).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
WolfTransitionEffect get transitionEffect {
|
||||
if (!isTransitioning) {
|
||||
return WolfTransitionEffect.none;
|
||||
}
|
||||
return _transitionEffect;
|
||||
}
|
||||
|
||||
WolfTransitionPhase get transitionPhase {
|
||||
if (!isTransitioning) {
|
||||
return WolfTransitionPhase.idle;
|
||||
}
|
||||
final int half = transitionDurationMs ~/ 2;
|
||||
if (_transitionElapsedMs < half) {
|
||||
return WolfTransitionPhase.covering;
|
||||
}
|
||||
return WolfTransitionPhase.revealing;
|
||||
}
|
||||
|
||||
double get transitionPhaseProgress {
|
||||
if (!isTransitioning) {
|
||||
return 0.0;
|
||||
}
|
||||
final int half = transitionDurationMs ~/ 2;
|
||||
if (_transitionElapsedMs < half) {
|
||||
return (_transitionElapsedMs / half).clamp(0.0, 1.0);
|
||||
}
|
||||
return ((_transitionElapsedMs - half) / half).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
int get selectedMainIndex => _selectedMainIndex;
|
||||
|
||||
int get selectedGameIndex => _selectedGameIndex;
|
||||
@@ -272,6 +335,7 @@ class MenuManager {
|
||||
Difficulty? initialDifficulty,
|
||||
bool hasResumableGame = false,
|
||||
bool initialGameIsRetail = false,
|
||||
WolfTransitionEffect introEffect = WolfTransitionEffect.normalFade,
|
||||
}) {
|
||||
_gameCount = gameCount;
|
||||
_showResumeOption = hasResumableGame;
|
||||
@@ -286,6 +350,7 @@ class MenuManager {
|
||||
_introLandingMenu = WolfMenuScreen.mainMenu;
|
||||
if (gameCount > 1) {
|
||||
_activeMenu = WolfMenuScreen.gameSelect;
|
||||
_introEffect = introEffect;
|
||||
_introElapsedMs = 0;
|
||||
_introPhase = _WolfIntroPhase.fadeIn;
|
||||
_introSlideIndex = 0;
|
||||
@@ -294,9 +359,13 @@ class MenuManager {
|
||||
WolfIntroSlide.title,
|
||||
];
|
||||
} else {
|
||||
_startIntroSequence(includeRetailWarning: initialGameIsRetail);
|
||||
_startIntroSequence(
|
||||
includeRetailWarning: initialGameIsRetail,
|
||||
effect: introEffect,
|
||||
);
|
||||
}
|
||||
_transitionTarget = null;
|
||||
_transitionEffect = WolfTransitionEffect.normalFade;
|
||||
_transitionElapsedMs = 0;
|
||||
_transitionSwappedMenu = false;
|
||||
_resetEdgeState();
|
||||
@@ -306,12 +375,17 @@ class MenuManager {
|
||||
void beginIntroSplash({
|
||||
WolfMenuScreen landingMenu = WolfMenuScreen.mainMenu,
|
||||
bool includeRetailWarning = false,
|
||||
WolfTransitionEffect effect = WolfTransitionEffect.normalFade,
|
||||
}) {
|
||||
_introLandingMenu = landingMenu;
|
||||
_transitionTarget = null;
|
||||
_transitionEffect = WolfTransitionEffect.normalFade;
|
||||
_transitionElapsedMs = 0;
|
||||
_transitionSwappedMenu = false;
|
||||
_startIntroSequence(includeRetailWarning: includeRetailWarning);
|
||||
_startIntroSequence(
|
||||
includeRetailWarning: includeRetailWarning,
|
||||
effect: effect,
|
||||
);
|
||||
_resetEdgeState();
|
||||
}
|
||||
|
||||
@@ -336,8 +410,10 @@ class MenuManager {
|
||||
}
|
||||
_activeMenu = WolfMenuScreen.mainMenu;
|
||||
_transitionTarget = null;
|
||||
_transitionEffect = WolfTransitionEffect.normalFade;
|
||||
_transitionElapsedMs = 0;
|
||||
_transitionSwappedMenu = false;
|
||||
_introEffect = WolfTransitionEffect.normalFade;
|
||||
_introElapsedMs = 0;
|
||||
_resetEdgeState();
|
||||
}
|
||||
@@ -449,6 +525,7 @@ class MenuManager {
|
||||
_isSelectableChangeViewIndex,
|
||||
);
|
||||
_transitionTarget = null;
|
||||
_transitionEffect = WolfTransitionEffect.normalFade;
|
||||
_transitionElapsedMs = 0;
|
||||
_transitionSwappedMenu = false;
|
||||
_resetEdgeState();
|
||||
@@ -464,6 +541,7 @@ class MenuManager {
|
||||
_isSelectableRendererOptionIndex,
|
||||
);
|
||||
_transitionTarget = null;
|
||||
_transitionEffect = WolfTransitionEffect.normalFade;
|
||||
_transitionElapsedMs = 0;
|
||||
_transitionSwappedMenu = false;
|
||||
_resetEdgeState();
|
||||
@@ -473,11 +551,15 @@ class MenuManager {
|
||||
///
|
||||
/// Hosts can reuse this fade timing for future pre-menu splash/image
|
||||
/// sequences so transitions feel consistent across the whole app.
|
||||
void startTransition(WolfMenuScreen target) {
|
||||
void startTransition(
|
||||
WolfMenuScreen target, {
|
||||
WolfTransitionEffect effect = WolfTransitionEffect.normalFade,
|
||||
}) {
|
||||
if (_activeMenu == target) {
|
||||
return;
|
||||
}
|
||||
_transitionTarget = target;
|
||||
_transitionEffect = effect;
|
||||
_transitionElapsedMs = 0;
|
||||
_transitionSwappedMenu = false;
|
||||
_resetEdgeState();
|
||||
@@ -501,6 +583,7 @@ class MenuManager {
|
||||
}
|
||||
if (_transitionElapsedMs >= transitionDurationMs) {
|
||||
_transitionTarget = null;
|
||||
_transitionEffect = WolfTransitionEffect.normalFade;
|
||||
_transitionElapsedMs = 0;
|
||||
_transitionSwappedMenu = false;
|
||||
}
|
||||
@@ -527,8 +610,12 @@ class MenuManager {
|
||||
_consumeEdgeState(input);
|
||||
}
|
||||
|
||||
void _startIntroSequence({required bool includeRetailWarning}) {
|
||||
void _startIntroSequence({
|
||||
required bool includeRetailWarning,
|
||||
required WolfTransitionEffect effect,
|
||||
}) {
|
||||
_activeMenu = WolfMenuScreen.introSplash;
|
||||
_introEffect = effect;
|
||||
_introSlides = includeRetailWarning
|
||||
? <WolfIntroSlide>[
|
||||
WolfIntroSlide.retailWarning,
|
||||
|
||||
Reference in New Issue
Block a user