From b3ebfd0910ea0d44a2e544606a47fa7c81e7ad07 Mon Sep 17 00:00:00 2001 From: fabs Date: Sun, 8 Jun 2025 04:09:40 +0100 Subject: [PATCH] pause button/pause screen rework --- source/funkin/play/PauseSubState.hx | 128 +++++++++++++++++++++++++--- source/funkin/play/PlayState.hx | 57 ++++++++++--- 2 files changed, 161 insertions(+), 24 deletions(-) diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index 916b1a6a7..6fc8fe80d 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -17,7 +17,10 @@ import funkin.ui.freeplay.FreeplayState; import funkin.graphics.FunkinSprite; import funkin.play.cutscene.VideoCutscene; import funkin.ui.AtlasText; +import flixel.util.FlxTimer; import funkin.ui.MusicBeatSubState; +import funkin.util.HapticUtil; +import funkin.ui.FullScreenScaleMode; import funkin.ui.transition.stickers.StickerSubState; import funkin.util.SwipeUtil; import funkin.util.TouchUtil; @@ -74,7 +77,9 @@ class PauseSubState extends MusicBeatSubState * Pause menu entries for when the game is paused during a video cutscene. */ static final PAUSE_MENU_ENTRIES_VIDEO_CUTSCENE:Array = [ + #if !mobile {text: 'Resume', callback: resume}, + #end {text: 'Skip Cutscene', callback: skipVideoCutscene}, {text: 'Restart Cutscene', callback: restartVideoCutscene}, {text: 'Exit to Menu', callback: quitToMenu}, @@ -149,6 +154,18 @@ class PauseSubState extends MusicBeatSubState // Graphics Variables // =============== + #if mobile + /** + * The pause button for the game, only appears in Mobile targets. Shows up breifly to finish the pause animation. + */ + var pauseButton:FunkinSprite; + + /** + * The pause circle for the game, only appears in Mobile targets. Shows up breifly to finish the pause animation. + */ + var pauseCircle:FunkinSprite; + #end + /** * The placeholder sprite displayed when an advertisement fails to load or display. */ @@ -253,6 +270,10 @@ class PauseSubState extends MusicBeatSubState super.destroy(); charterFadeTween.cancel(); charterFadeTween = null; + dataFadeTimer.cancel(); + dataFadeTimer = null; + hapticTimer.cancel(); + hapticTimer = null; pauseMusic.stop(); } @@ -321,6 +342,29 @@ class PauseSubState extends MusicBeatSubState background.scrollFactor.set(0, 0); background.updateHitbox(); add(background); + + #if mobile + pauseButton = FunkinSprite.createSparrow(0, 0, "pauseButton"); + pauseButton.animation.addByIndices('idle', 'pause', [0], "", 24, false); + pauseButton.animation.addByIndices('hold', 'pause', [5], "", 24, false); + pauseButton.animation.addByIndices('confirm', 'pause', [ + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + ], "", 24, false); + pauseButton.scale.set(0.8, 0.8); + pauseButton.updateHitbox(); + pauseButton.animation.play("confirm"); + pauseButton.setPosition((FlxG.width - pauseButton.width) - 35, 35); + + pauseCircle = FunkinSprite.create(0, 0, 'pauseCircle'); + pauseCircle.scale.set(0.84, 0.8); + pauseCircle.updateHitbox(); + pauseCircle.x = ((pauseButton.x + (pauseButton.width / 2)) - (pauseCircle.width / 2)); + pauseCircle.y = ((pauseButton.y + (pauseButton.height / 2)) - (pauseCircle.height / 2)); + pauseCircle.alpha = 0.1; + + add(pauseCircle); + add(pauseButton); + #end } /** @@ -332,7 +376,7 @@ class PauseSubState extends MusicBeatSubState metadata.scrollFactor.set(0, 0); add(metadata); - var metadataSong:FlxText = new FlxText(20, 580, camera.width - Math.max(40, funkin.ui.FullScreenScaleMode.gameNotchSize.x), 'Song Name'); + var metadataSong:FlxText = new FlxText(20, 15, camera.width - Math.max(40, funkin.ui.FullScreenScaleMode.gameNotchSize.x), 'Song Name'); metadataSong.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT); if (PlayState.instance?.currentChart != null) { @@ -373,6 +417,12 @@ class PauseSubState extends MusicBeatSubState metadataPractice.scrollFactor.set(0, 0); metadata.add(metadataPractice); + metadataArtist.alpha = 0; + metadataPractice.alpha = 0; + metadataSong.alpha = 0; + metadataDifficulty.alpha = 0; + metadataDeaths.alpha = 0; + updateMetadataText(); } @@ -432,6 +482,9 @@ class PauseSubState extends MusicBeatSubState }); } + var dataFadeTimer = new FlxTimer(); + var hapticTimer = new FlxTimer(); + /** * Perform additional animations to transition the pause menu in when it is first displayed. */ @@ -439,11 +492,35 @@ class PauseSubState extends MusicBeatSubState { FlxTween.tween(background, {alpha: 0.6}, 0.8, {ease: FlxEase.quartOut}); + #if mobile + HapticUtil.vibrate(0, 0.05, 0.5); + + pauseButton.animation.play("confirm"); + pauseCircle.scale.set(0.84 * 1.4, 0.8 * 1.4); + pauseCircle.alpha = 0.4; + FlxTween.tween(pauseCircle.scale, {x: 0.84 * 0.8, y: 0.8 * 0.8}, 0.4, {ease: FlxEase.backInOut}); + FlxTween.tween(pauseCircle, {alpha: 0}, 0.6, {ease: FlxEase.quartOut}); + + hapticTimer.start(0.2, function(_) { + HapticUtil.vibrate(0, 0.01, 0.2); + }); + + dataFadeTimer.start(0.3, function(_) { + transitionMetadataIn(); + FlxTween.tween(pauseButton, {alpha: 0}, 0.6, {ease: FlxEase.quartOut}); + }); + #else + transitionMetadataIn(); + #end + } + + function transitionMetadataIn():Void + { // Animate each element a little bit downwards. var delay:Float = 0.1; for (child in metadata.members) { - FlxTween.tween(child, {alpha: 1, y: child.y - 5}, 1.8, {ease: FlxEase.quartOut, startDelay: delay}); + FlxTween.tween(child, {alpha: 1, y: child.y + 5}, 1.8, {ease: FlxEase.quartOut, startDelay: delay}); delay += 0.1; } } @@ -460,8 +537,8 @@ class PauseSubState extends MusicBeatSubState if (!allowInput) return; // Doing this just so it'd look better i guess. - final upP:Bool = controls.UI_UP_P || SwipeUtil.swipeUp; - final downP:Bool = controls.UI_DOWN_P || SwipeUtil.swipeDown; + final upP:Bool = controls.UI_UP_P; + final downP:Bool = controls.UI_DOWN_P; if (upP) { @@ -481,10 +558,12 @@ class PauseSubState extends MusicBeatSubState if (i == currentEntry) { currentMenuEntries[currentEntry].callback(this); + HapticUtil.vibrate(0, 0.05, 0.5); break; } changeSelection(i - currentEntry); + HapticUtil.vibrate(0, 0.01, 0.2); break; } @@ -498,16 +577,13 @@ class PauseSubState extends MusicBeatSubState { resume(this); } - // we only want justOpened to be true for 1 single frame, when we first get into the pause menu substate justOpened = false; - #if FEATURE_DEBUG_FUNCTIONS // to pause the game and get screenshots easy, press H on pause menu! if (FlxG.keys.justPressed.H) { var visible = !metadata.visible; - metadata.visible = visible; menuEntryText.visible = visible; background.visible = visible; @@ -545,11 +621,20 @@ class PauseSubState extends MusicBeatSubState // Set the transparency. text.alpha = isCurrent ? 1.0 : 0.6; + #if mobile // Set the position. + if (isCurrent && currentEntry != prevEntry) + { + FlxTween.globalManager.cancelTweensOf(text); + text.x = 165; + FlxTween.tween(text, {x: 150}, 0.2, {ease: FlxEase.backInOut}); + } + #else var targetX = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 20 + Math.max(90, funkin.ui.FullScreenScaleMode.gameNotchSize.x); var targetY = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 120 + (camera.height * 0.48); FlxTween.globalManager.cancelTweensOf(text); FlxTween.tween(text, {x: targetX, y: targetY}, 0.33, {ease: FlxEase.quartOut}); + #end } } @@ -649,6 +734,26 @@ class PauseSubState extends MusicBeatSubState else { // Handle visible entries. + #if mobile + // var yPos:Float = (150 * entryIndex) + 100; + + // var yPos:Float = (140 * entryIndex) + 150; + var yPos:Float = (105 * entryIndex) + 150; + + var text:AtlasText = new AtlasText(110, yPos, entry.text, AtlasFont.BOLD); + text.scrollFactor.set(0, 0); + text.alpha = 0; + for (letter in text) + { + letter.width *= 1.2; + letter.height *= 1.4; + } + menuEntryText.add(text); + + FlxTween.tween(text, {x: 150}, 0.4 * (entryIndex + 1), {ease: FlxEase.expoOut}); + + entry.sprite = text; + #else var yPos:Float = 70 * entryIndex + 30; var text:AtlasText = new AtlasText(0, yPos, entry.text, AtlasFont.BOLD); text.scrollFactor.set(0, 0); @@ -661,6 +766,7 @@ class PauseSubState extends MusicBeatSubState menuEntryText.add(text); entry.sprite = text; + #end entryIndex++; } @@ -681,13 +787,7 @@ class PauseSubState extends MusicBeatSubState function updateMetadataText():Void { metadataPractice.visible = PlayState.instance?.isPracticeMode ?? false; - if (metadata.members[0].y != 548 && metadataPractice.visible) - { - for (text in metadata) - { - text.y -= 32; - } - } + switch (this.currentMode) { case Standard | Difficulty: diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index adfc28c3e..1726f7eec 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -10,6 +10,7 @@ import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.text.FlxText; import flixel.tweens.FlxTween; +import flixel.tweens.FlxEase; import flixel.ui.FlxBar; import flixel.util.FlxColor; import flixel.util.FlxStringUtil; @@ -535,6 +536,11 @@ class PlayState extends MusicBeatSubState * The pause button for the game, only appears in Mobile targets. */ var pauseButton:FunkinSprite; + + /** + * The pause circle for the game, only appears in Mobile targets. + */ + var pauseCircle:FunkinSprite; #end /** @@ -780,17 +786,27 @@ class PlayState extends MusicBeatSubState // Create the pause button. #if mobile - pauseButton = FunkinSprite.createSparrow(0, 0, "fonts/bold"); - pauseButton.animation.addByPrefix("idle", "(", 24, true); - pauseButton.animation.play("idle"); - pauseButton.color = FlxColor.WHITE; - pauseButton.alpha = 0.65; + pauseButton = FunkinSprite.createSparrow(0, 0, "pauseButton"); + pauseButton.animation.addByIndices('idle', 'back', [0], "", 24, false); + pauseButton.animation.addByIndices('hold', 'back', [5], "", 24, false); + pauseButton.animation.addByIndices('confirm', 'back', [ + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + ], "", 24, false); + pauseButton.scale.set(0.8, 0.8); pauseButton.updateHitbox(); - pauseButton.setPosition((FlxG.width - pauseButton.width) - Math.max(40, FullScreenScaleMode.gameNotchSize.x), 3); + pauseButton.animation.play("idle"); + pauseButton.setPosition((FlxG.width - pauseButton.width) - 35, 35); pauseButton.cameras = [camControls]; - pauseButton.width *= 2; - pauseButton.height *= 2; - pauseButton.offset.set(-(pauseButton.width / 4), -(pauseButton.height / 4)); + + pauseCircle = FunkinSprite.create(0, 0, 'pauseCircle'); + pauseCircle.scale.set(0.84, 0.8); + pauseCircle.updateHitbox(); + pauseCircle.cameras = [camControls]; + pauseCircle.x = ((pauseButton.x + (pauseButton.width / 2)) - (pauseCircle.width / 2)); + pauseCircle.y = ((pauseButton.y + (pauseButton.height / 2)) - (pauseCircle.height / 2)); + pauseCircle.alpha = 0.1; + + add(pauseCircle); add(pauseButton); hitbox.forEachAlive(function(hint:FunkinHint) { hint.deadZones.push(pauseButton); @@ -1019,7 +1035,14 @@ class PlayState extends MusicBeatSubState #if mobile if (justUnpaused) { - pauseButton.alpha = 0.65; + // pauseButton.alpha = 1; + // pauseCircle.alpha = 0.1; + + FlxTween.cancelTweensOf(pauseButton); + FlxTween.cancelTweensOf(pauseCircle); + + FlxTween.tween(pauseButton, {alpha: 1}, 0.25, {ease: FlxEase.quartOut}); + FlxTween.tween(pauseCircle, {alpha: 0.1}, 0.25, {ease: FlxEase.quartOut}); if (!startingSong) hitbox.visible = true; } @@ -1140,6 +1163,7 @@ class PlayState extends MusicBeatSubState #if mobile if ((VideoCutscene.isPlaying() || isInCutscene) && !pauseButton.visible) pauseButton.visible = true; + pauseCircle.visible = pauseButton.visible; #end justUnpaused = false; @@ -1155,7 +1179,11 @@ class PlayState extends MusicBeatSubState Countdown.pauseCountdown(); #if mobile + FlxTween.cancelTweensOf(pauseButton); + FlxTween.cancelTweensOf(pauseCircle); + pauseButton.alpha = 0; + pauseCircle.alpha = 0; hitbox.visible = false; #end var event = new PauseScriptEvent(FlxG.random.bool((1 / 1000) * 100)); @@ -3018,7 +3046,11 @@ class PlayState extends MusicBeatSubState { currentConversation.pauseMusic(); #if mobile + FlxTween.cancelTweensOf(pauseButton); + FlxTween.cancelTweensOf(pauseCircle); + pauseButton.alpha = 0; + pauseCircle.alpha = 0; hitbox.visible = false; #end @@ -3038,7 +3070,11 @@ class PlayState extends MusicBeatSubState { VideoCutscene.pauseVideo(); #if mobile + FlxTween.cancelTweensOf(pauseButton); + FlxTween.cancelTweensOf(pauseCircle); + pauseButton.alpha = 0; + pauseCircle.alpha = 0; hitbox.visible = false; #end @@ -3080,6 +3116,7 @@ class PlayState extends MusicBeatSubState #if mobile // Hide the buttons while the song is ending. hitbox.visible = pauseButton.visible = false; + pauseCircle.visible = false; #end // Check if any events want to prevent the song from ending.