Delayed fade-out of UI after finishing a game.

All elements quickly fade out, and wait 2.5s before switching screens (instead of immediately).

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2016-12-24 01:55:43 -05:00
parent 9f2aa7c1fb
commit aa1babed3b
2 changed files with 91 additions and 34 deletions

View File

@ -552,10 +552,11 @@ public class GameData {
* @param x the starting x coordinate * @param x the starting x coordinate
* @param y the y coordinate * @param y the y coordinate
* @param scale the scale to apply * @param scale the scale to apply
* @param alpha the alpha level
* @param fixedsize the width to use for all symbols * @param fixedsize the width to use for all symbols
* @param rightAlign align right (true) or left (false) * @param rightAlign align right (true) or left (false)
*/ */
public void drawFixedSizeSymbolString(String str, float x, float y, float scale, float fixedsize, boolean rightAlign) { public void drawFixedSizeSymbolString(String str, float x, float y, float scale, float alpha, float fixedsize, boolean rightAlign) {
char[] c = str.toCharArray(); char[] c = str.toCharArray();
float cx = x; float cx = x;
if (rightAlign) { if (rightAlign) {
@ -564,14 +565,18 @@ public class GameData {
if (scale != 1.0f) if (scale != 1.0f)
digit = digit.getScaledCopy(scale); digit = digit.getScaledCopy(scale);
cx -= fixedsize; cx -= fixedsize;
digit.setAlpha(alpha);
digit.draw(cx + (fixedsize - digit.getWidth()) / 2, y); digit.draw(cx + (fixedsize - digit.getWidth()) / 2, y);
digit.setAlpha(1f);
} }
} else { } else {
for (int i = 0; i < c.length; i++) { for (int i = 0; i < c.length; i++) {
Image digit = getScoreSymbolImage(c[i]); Image digit = getScoreSymbolImage(c[i]);
if (scale != 1.0f) if (scale != 1.0f)
digit = digit.getScaledCopy(scale); digit = digit.getScaledCopy(scale);
digit.setAlpha(alpha);
digit.draw(cx + (fixedsize - digit.getWidth()) / 2, y); digit.draw(cx + (fixedsize - digit.getWidth()) / 2, y);
digit.setAlpha(1f);
cx += fixedsize; cx += fixedsize;
} }
} }
@ -584,9 +589,10 @@ public class GameData {
* @param g the graphics context * @param g the graphics context
* @param breakPeriod if true, will not draw scorebar and combo elements, and will draw grade * @param breakPeriod if true, will not draw scorebar and combo elements, and will draw grade
* @param firstObject true if the first hit object's start time has not yet passed * @param firstObject true if the first hit object's start time has not yet passed
* @param alpha the alpha level at which to render all elements (except the hit error bar)
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public void drawGameElements(Graphics g, boolean breakPeriod, boolean firstObject) { public void drawGameElements(Graphics g, boolean breakPeriod, boolean firstObject, float alpha) {
boolean relaxAutoPilot = (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()); boolean relaxAutoPilot = (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive());
int margin = (int) (width * 0.008f); int margin = (int) (width * 0.008f);
float uiScale = GameImage.getUIscale(); float uiScale = GameImage.getUIscale();
@ -594,14 +600,14 @@ public class GameData {
// score // score
if (!relaxAutoPilot) if (!relaxAutoPilot)
drawFixedSizeSymbolString((scoreDisplay < 100000000) ? String.format("%08d", scoreDisplay) : Long.toString(scoreDisplay), drawFixedSizeSymbolString((scoreDisplay < 100000000) ? String.format("%08d", scoreDisplay) : Long.toString(scoreDisplay),
width - margin, 0, 1.0f, getScoreSymbolImage('0').getWidth() - 2, true); width - margin, 0, 1f, alpha, getScoreSymbolImage('0').getWidth() - 2, true);
// score percentage // score percentage
int symbolHeight = getScoreSymbolImage('0').getHeight(); int symbolHeight = getScoreSymbolImage('0').getHeight();
if (!relaxAutoPilot) if (!relaxAutoPilot)
drawSymbolString( drawSymbolString(
String.format((scorePercentDisplay < 10f) ? "0%.2f%%" : "%.2f%%", scorePercentDisplay), String.format((scorePercentDisplay < 10f) ? "0%.2f%%" : "%.2f%%", scorePercentDisplay),
width - margin, symbolHeight, 0.60f, 1f, true); width - margin, symbolHeight, 0.60f, alpha, true);
// map progress circle // map progress circle
Beatmap beatmap = MusicController.getBeatmap(); Beatmap beatmap = MusicController.getBeatmap();
@ -615,23 +621,27 @@ public class GameData {
getScoreSymbolImage('%').getWidth() getScoreSymbolImage('%').getWidth()
) * 0.60f - circleDiameter); ) * 0.60f - circleDiameter);
if (!relaxAutoPilot) { if (!relaxAutoPilot) {
float oldWhiteAlpha = Colors.WHITE_ALPHA.a;
Colors.WHITE_ALPHA.a = alpha;
g.setAntiAlias(true); g.setAntiAlias(true);
g.setLineWidth(2f); g.setLineWidth(2f);
g.setColor(Color.white); g.setColor(Colors.WHITE_ALPHA);
g.drawOval(circleX, symbolHeight, circleDiameter, circleDiameter); g.drawOval(circleX, symbolHeight, circleDiameter, circleDiameter);
if (trackPosition > firstObjectTime) { if (trackPosition > firstObjectTime) {
// map progress (white) // map progress (white)
g.fillArc(circleX, symbolHeight, circleDiameter, circleDiameter, float progress = Math.min((float) (trackPosition - firstObjectTime) / (beatmap.endTime - firstObjectTime), 1f);
-90, -90 + (int) (360f * (trackPosition - firstObjectTime) / (beatmap.endTime - firstObjectTime)) g.fillArc(circleX, symbolHeight, circleDiameter, circleDiameter, -90, -90 + (int) (360f * progress));
);
} else { } else {
// lead-in time (yellow) // lead-in time (yellow)
float progress = (float) trackPosition / firstObjectTime;
float oldYellowAlpha = Colors.YELLOW_ALPHA.a;
Colors.YELLOW_ALPHA.a *= alpha;
g.setColor(Colors.YELLOW_ALPHA); g.setColor(Colors.YELLOW_ALPHA);
g.fillArc(circleX, symbolHeight, circleDiameter, circleDiameter, g.fillArc(circleX, symbolHeight, circleDiameter, circleDiameter, -90 + (int) (360f * progress), -90);
-90 + (int) (360f * trackPosition / firstObjectTime), -90 Colors.YELLOW_ALPHA.a = oldYellowAlpha;
);
} }
g.setAntiAlias(false); g.setAntiAlias(false);
Colors.WHITE_ALPHA.a = oldWhiteAlpha;
} }
// mod icons // mod icons
@ -641,10 +651,12 @@ public class GameData {
int modCount = 0; int modCount = 0;
for (GameMod mod : GameMod.VALUES_REVERSED) { for (GameMod mod : GameMod.VALUES_REVERSED) {
if (mod.isActive()) { if (mod.isActive()) {
mod.getImage().setAlpha(alpha);
mod.getImage().draw( mod.getImage().draw(
modX - (modCount * (modWidth / 2f)), modX - (modCount * (modWidth / 2f)),
symbolHeight + circleDiameter + 10 symbolHeight + circleDiameter + 10
); );
mod.getImage().setAlpha(1f);
modCount++; modCount++;
} }
} }
@ -692,8 +704,8 @@ public class GameData {
float tickWidth = 2 * uiScale; float tickWidth = 2 * uiScale;
for (HitErrorInfo info : hitErrorList) { for (HitErrorInfo info : hitErrorList) {
int time = info.time; int time = info.time;
float alpha = 1 - ((float) (trackPosition - time) / HIT_ERROR_FADE_TIME); float tickAlpha = 1 - ((float) (trackPosition - time) / HIT_ERROR_FADE_TIME);
white.a = alpha * hitErrorAlpha; white.a = tickAlpha * hitErrorAlpha;
g.setColor(white); g.setColor(white);
g.fillRect((hitErrorX + info.timeDiff - 1) * uiScale, tickY, tickWidth, tickHeight); g.fillRect((hitErrorX + info.timeDiff - 1) * uiScale, tickY, tickWidth, tickHeight);
} }
@ -716,9 +728,12 @@ public class GameData {
float colourX = 4 * uiScale, colourY = 15 * uiScale; float colourX = 4 * uiScale, colourY = 15 * uiScale;
Image colourCropped = colour.getSubImage(0, 0, (int) (645 * uiScale * healthRatio), colour.getHeight()); Image colourCropped = colour.getSubImage(0, 0, (int) (645 * uiScale * healthRatio), colour.getHeight());
scorebar.setAlpha(1f); scorebar.setAlpha(alpha);
scorebar.draw(0, 0); scorebar.draw(0, 0);
scorebar.setAlpha(1f);
colourCropped.setAlpha(alpha);
colourCropped.draw(colourX, colourY); colourCropped.draw(colourX, colourY);
colourCropped.setAlpha(1f);
Image ki = null; Image ki = null;
if (health >= 50f) if (health >= 50f)
@ -729,7 +744,9 @@ public class GameData {
ki = GameImage.SCOREBAR_KI_DANGER2.getImage(); ki = GameImage.SCOREBAR_KI_DANGER2.getImage();
if (comboPopTime < COMBO_POP_TIME) if (comboPopTime < COMBO_POP_TIME)
ki = ki.getScaledCopy(1f + (0.45f * (1f - (float) comboPopTime / COMBO_POP_TIME))); ki = ki.getScaledCopy(1f + (0.45f * (1f - (float) comboPopTime / COMBO_POP_TIME)));
ki.setAlpha(alpha);
ki.drawCentered(colourX + colourCropped.getWidth(), colourY); ki.drawCentered(colourX + colourCropped.getWidth(), colourY);
ki.setAlpha(1f);
// combo burst // combo burst
if (comboBurstIndex != -1 && comboBurstAlpha > 0f) { if (comboBurstIndex != -1 && comboBurstAlpha > 0f) {
@ -745,8 +762,8 @@ public class GameData {
float comboPopFront = 1 + comboPop * 0.08f; float comboPopFront = 1 + comboPop * 0.08f;
String comboString = String.format("%dx", combo); String comboString = String.format("%dx", combo);
if (comboPopTime != COMBO_POP_TIME) if (comboPopTime != COMBO_POP_TIME)
drawSymbolString(comboString, margin, height - margin - (symbolHeight * comboPopBack), comboPopBack, 0.5f, false); drawSymbolString(comboString, margin, height - margin - (symbolHeight * comboPopBack), comboPopBack, 0.5f * alpha, false);
drawSymbolString(comboString, margin, height - margin - (symbolHeight * comboPopFront), comboPopFront, 1f, false); drawSymbolString(comboString, margin, height - margin - (symbolHeight * comboPopFront), comboPopFront, alpha, false);
} }
} else if (!relaxAutoPilot) { } else if (!relaxAutoPilot) {
// grade // grade
@ -754,9 +771,9 @@ public class GameData {
if (grade != Grade.NULL) { if (grade != Grade.NULL) {
Image gradeImage = grade.getSmallImage(); Image gradeImage = grade.getSmallImage();
float gradeScale = symbolHeight * 0.75f / gradeImage.getHeight(); float gradeScale = symbolHeight * 0.75f / gradeImage.getHeight();
gradeImage.getScaledCopy(gradeScale).draw( gradeImage = gradeImage.getScaledCopy(gradeScale);
circleX - gradeImage.getWidth(), symbolHeight gradeImage.setAlpha(alpha);
); gradeImage.draw(circleX - gradeImage.getWidth(), symbolHeight);
} }
} }
} }
@ -781,7 +798,7 @@ public class GameData {
drawFixedSizeSymbolString( drawFixedSizeSymbolString(
(score < 100000000) ? String.format("%08d", score) : Long.toString(score), (score < 100000000) ? String.format("%08d", score) : Long.toString(score),
210 * uiScale, (rankingHeight + 50) * uiScale, 210 * uiScale, (rankingHeight + 50) * uiScale,
scoreTextScale, getScoreSymbolImage('0').getWidth() * scoreTextScale - 2, false scoreTextScale, 1f, getScoreSymbolImage('0').getWidth() * scoreTextScale - 2, false
); );
// result counts // result counts

View File

@ -52,6 +52,7 @@ import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.MenuButton; import itdelatrisu.opsu.ui.MenuButton;
import itdelatrisu.opsu.ui.StarStream; import itdelatrisu.opsu.ui.StarStream;
import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.UI;
import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation; import itdelatrisu.opsu.ui.animations.AnimationEquation;
import java.io.File; import java.io.File;
@ -99,6 +100,9 @@ public class Game extends BasicGameState {
/** Screen fade-out time, in milliseconds, when health hits zero. */ /** Screen fade-out time, in milliseconds, when health hits zero. */
private static final int LOSE_FADEOUT_TIME = 500; private static final int LOSE_FADEOUT_TIME = 500;
/** Game element fade-out time, in milliseconds, when the game ends. */
private static final int FINISHED_FADEOUT_TIME = 400;
/** Maximum rotation, in degrees, over fade out upon death. */ /** Maximum rotation, in degrees, over fade out upon death. */
private static final float MAX_ROTATION = 90f; private static final float MAX_ROTATION = 90f;
@ -281,6 +285,12 @@ public class Game extends BasicGameState {
/** The star stream shown when passing another score. */ /** The star stream shown when passing another score. */
private StarStream scoreboardStarStream; private StarStream scoreboardStarStream;
/** Whether the game is finished (last hit object passed). */
private boolean gameFinished = false;
/** Timer after game has finished, before changing states. */
private AnimatedValue gameFinishedTimer = new AnimatedValue(2500, 0, 1, AnimationEquation.LINEAR);
/** Music position bar background colors. */ /** Music position bar background colors. */
private static final Color private static final Color
MUSICBAR_NORMAL = new Color(12, 9, 10, 0.25f), MUSICBAR_NORMAL = new Color(12, 9, 10, 0.25f),
@ -373,7 +383,9 @@ public class Game extends BasicGameState {
autoMousePressed = false; autoMousePressed = false;
if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) { if (GameMod.AUTO.isActive() || GameMod.AUTOPILOT.isActive()) {
Vec2f autoPoint = null; Vec2f autoPoint = null;
if (isLeadIn()) { if (gameFinished) {
// game finished, do nothing
} else if (isLeadIn()) {
// lead-in // lead-in
float progress = Math.max((float) (leadInTime - beatmap.audioLeadIn) / approachTime, 0f); float progress = Math.max((float) (leadInTime - beatmap.audioLeadIn) / approachTime, 0f);
autoMousePosition.y = height / (2f - progress); autoMousePosition.y = height / (2f - progress);
@ -488,7 +500,7 @@ public class Game extends BasicGameState {
Colors.BLACK_ALPHA.a = a; Colors.BLACK_ALPHA.a = a;
} }
data.drawGameElements(g, true, objectIndex == 0); data.drawGameElements(g, true, objectIndex == 0, 1f);
if (breakLength >= 8000 && if (breakLength >= 8000 &&
trackPosition - breakTime > 2000 && trackPosition - breakTime > 2000 &&
@ -526,7 +538,13 @@ public class Game extends BasicGameState {
// non-break // non-break
else { else {
// game elements // game elements
data.drawGameElements(g, false, objectIndex == 0); float gameElementAlpha = 1f;
if (gameFinished) {
// game finished: fade everything out
float t = 1f - Math.min(gameFinishedTimer.getTime() / (float) FINISHED_FADEOUT_TIME, 1f);
gameElementAlpha = AnimationEquation.OUT_CUBIC.calc(t);
}
data.drawGameElements(g, false, objectIndex == 0, gameElementAlpha);
// skip beginning // skip beginning
if (objectIndex == 0 && if (objectIndex == 0 &&
@ -756,11 +774,11 @@ public class Game extends BasicGameState {
} }
// normal game update // normal game update
if (!isReplay) if (!isReplay && !gameFinished)
addReplayFrameAndRun(mouseX, mouseY, lastKeysPressed, trackPosition); addReplayFrameAndRun(mouseX, mouseY, lastKeysPressed, trackPosition);
// watching replay // watching replay
else { else if (!gameFinished) {
// out of frames, use previous data // out of frames, use previous data
if (replayIndex >= replay.frames.length) if (replayIndex >= replay.frames.length)
updateGame(replayX, replayY, delta, MusicController.getPosition(), lastKeysPressed); updateGame(replayX, replayY, delta, MusicController.getPosition(), lastKeysPressed);
@ -810,7 +828,8 @@ public class Game extends BasicGameState {
// update in-game scoreboard // update in-game scoreboard
if (previousScores != null && trackPosition > firstObjectTime) { if (previousScores != null && trackPosition > firstObjectTime) {
// show scoreboard if selected, and always in break // show scoreboard if selected, and always in break
if (scoreboardVisible || breakTime > 0) { // hide when game ends
if ((scoreboardVisible || breakTime > 0) && !gameFinished) {
currentScoreboardAlpha += 1f / SCOREBOARD_FADE_IN_TIME * delta; currentScoreboardAlpha += 1f / SCOREBOARD_FADE_IN_TIME * delta;
if (currentScoreboardAlpha > 1f) if (currentScoreboardAlpha > 1f)
currentScoreboardAlpha = 1f; currentScoreboardAlpha = 1f;
@ -822,6 +841,14 @@ public class Game extends BasicGameState {
} }
data.updateDisplays(delta); data.updateDisplays(delta);
// game finished: change state after timer expires
if (gameFinished && !gameFinishedTimer.update(delta)) {
if (checkpointLoaded) // if checkpoint used, skip ranking screen
game.closeRequested();
else // go to ranking screen
game.enterState(Opsu.STATE_GAMERANKING, new EasedFadeOutTransition(), new FadeInTransition());
}
} }
/** /**
@ -839,12 +866,8 @@ public class Game extends BasicGameState {
if (MusicController.trackEnded() && objectIndex < gameObjects.length) if (MusicController.trackEnded() && objectIndex < gameObjects.length)
gameObjects[objectIndex].update(true, delta, mouseX, mouseY, false, trackPosition); gameObjects[objectIndex].update(true, delta, mouseX, mouseY, false, trackPosition);
// if checkpoint used, skip ranking screen // save score and replay
if (checkpointLoaded) if (!checkpointLoaded) {
game.closeRequested();
// go to ranking screen
else {
boolean unranked = (GameMod.AUTO.isActive() || GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()); boolean unranked = (GameMod.AUTO.isActive() || GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive());
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data); ((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
if (isReplay) if (isReplay)
@ -865,9 +888,12 @@ public class Game extends BasicGameState {
// add score to database // add score to database
if (!unranked && !isReplay) if (!unranked && !isReplay)
ScoreDB.addScore(score); ScoreDB.addScore(score);
game.enterState(Opsu.STATE_GAMERANKING, new EasedFadeOutTransition(), new FadeInTransition());
} }
// start timer
gameFinished = true;
gameFinishedTimer.setTime(0);
return; return;
} }
@ -971,6 +997,9 @@ public class Game extends BasicGameState {
@Override @Override
public void keyPressed(int key, char c) { public void keyPressed(int key, char c) {
if (gameFinished)
return;
int trackPosition = MusicController.getPosition(); int trackPosition = MusicController.getPosition();
int mouseX = input.getMouseX(); int mouseX = input.getMouseX();
int mouseY = input.getMouseY(); int mouseY = input.getMouseY();
@ -1094,6 +1123,9 @@ public class Game extends BasicGameState {
@Override @Override
public void mousePressed(int button, int x, int y) { public void mousePressed(int button, int x, int y) {
if (gameFinished)
return;
// watching replay // watching replay
if (isReplay || GameMod.AUTO.isActive()) { if (isReplay || GameMod.AUTO.isActive()) {
if (button == Input.MOUSE_MIDDLE_BUTTON) if (button == Input.MOUSE_MIDDLE_BUTTON)
@ -1186,6 +1218,9 @@ public class Game extends BasicGameState {
@Override @Override
public void mouseReleased(int button, int x, int y) { public void mouseReleased(int button, int x, int y) {
if (gameFinished)
return;
if (Options.isMouseDisabled()) if (Options.isMouseDisabled())
return; return;
@ -1203,6 +1238,9 @@ public class Game extends BasicGameState {
@Override @Override
public void keyReleased(int key, char c) { public void keyReleased(int key, char c) {
if (gameFinished)
return;
int keys = ReplayFrame.KEY_NONE; int keys = ReplayFrame.KEY_NONE;
if (key == Options.getGameKeyLeft()) if (key == Options.getGameKeyLeft())
keys = ReplayFrame.KEY_K1; keys = ReplayFrame.KEY_K1;
@ -1582,6 +1620,8 @@ public class Game extends BasicGameState {
autoMousePressed = false; autoMousePressed = false;
flashlightRadius = container.getHeight() * 2 / 3; flashlightRadius = container.getHeight() * 2 / 3;
scoreboardStarStream.clear(); scoreboardStarStream.clear();
gameFinished = false;
gameFinishedTimer.setTime(0);
System.gc(); System.gc();
} }