From e49ca3c39bcca81aa97d0199995d3828c1cff65c Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Thu, 5 Jan 2017 18:25:45 -0500 Subject: [PATCH] Animate the game ranking screen elements. Signed-off-by: Jeffrey Han --- src/itdelatrisu/opsu/GameData.java | 199 +++++++++++++------ src/itdelatrisu/opsu/states/GameRanking.java | 15 +- 2 files changed, 154 insertions(+), 60 deletions(-) diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index 41b1beab..84286d05 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -348,7 +348,7 @@ public GameData(int width, int height) { /** * Constructor for score viewing. * This will initialize all parameters and images needed for the - * {@link #drawRankingElements(Graphics, Beatmap)} method. + * {@link #drawRankingElements(Graphics, Beatmap, int)} method. * @param s the ScoreData object * @param width container width * @param height container height @@ -782,86 +782,144 @@ else if (health >= 25f) * Draws ranking elements: score, results, ranking, game mods. * @param g the graphics context * @param beatmap the beatmap + * @param time the animation time */ - public void drawRankingElements(Graphics g, Beatmap beatmap) { + public void drawRankingElements(Graphics g, Beatmap beatmap, int time) { // TODO Version 2 skins - float rankingHeight = 96; - float scoreTextScale = 1.33f; float symbolTextScale = 1.15f; - float rankResultScale = 0.5f; float uiScale = GameImage.getUIscale(); + Image zeroImg = getScoreSymbolImage('0'); + + // animation timings + int animationTime = 400, offsetTime = 150, gradeAnimationTime = 1000, whiteAnimationTime = 2200; + int rankStart = 50, comboStart = 1800, perfectStart = 2700, gradeStart = 2800, whiteStart = 3800; // ranking panel GameImage.RANKING_PANEL.getImage().draw(0, (int) (102 * uiScale)); // score + float scoreTextScale = 1.33f; drawFixedSizeSymbolString( (score < 100000000) ? String.format("%08d", score) : Long.toString(score), 180 * uiScale, 120 * uiScale, - scoreTextScale, 1f, getScoreSymbolImage('0').getWidth() * scoreTextScale, false + scoreTextScale, 1f, zeroImg.getWidth() * scoreTextScale, false ); // result counts - float resultHitInitialX = 64; - float resultHitInitialY = 256; - float resultInitialX = 128; - float resultInitialY = resultHitInitialY - (getScoreSymbolImage('0').getHeight() * symbolTextScale) / 2f; - float resultOffsetX = 320; - float resultOffsetY = 96; - - int[] rankDrawOrder = { HIT_300, HIT_300G, HIT_100, HIT_100K, HIT_50, HIT_MISS }; - int[] rankResultOrder = { - hitResultCount[HIT_300], hitResultCount[HIT_300G], - hitResultCount[HIT_100], hitResultCount[HIT_100K] + hitResultCount[HIT_300K], - hitResultCount[HIT_50], hitResultCount[HIT_MISS] - }; - - for (int i = 0; i < rankDrawOrder.length; i += 2) { - float offsetY = (resultOffsetY * (i / 2)); - hitResults[rankDrawOrder[i]].getScaledCopy(rankResultScale).drawCentered( - resultHitInitialX * uiScale, (resultHitInitialY + offsetY) * uiScale - ); - hitResults[rankDrawOrder[i+1]].getScaledCopy(rankResultScale).drawCentered( - (resultHitInitialX + resultOffsetX) * uiScale, (resultHitInitialY + offsetY) * uiScale - ); - drawSymbolString(String.format("%dx", rankResultOrder[i]), - resultInitialX * uiScale, (resultInitialY + offsetY) * uiScale, symbolTextScale, 1f, false - ); - drawSymbolString(String.format("%dx", rankResultOrder[i+1]), - (resultInitialX + resultOffsetX) * uiScale, (resultInitialY + offsetY) * uiScale, symbolTextScale, 1f, false - ); + if (time >= rankStart) { + float rankResultScale = 0.5f; + float resultHitInitialX = 64, resultHitInitialY = 256; + float resultInitialX = 128; + float resultInitialY = resultHitInitialY - (zeroImg.getHeight() * symbolTextScale) / 2f; + float resultOffsetX = 320, resultOffsetY = 96; + int[] rankDrawOrder = { HIT_300, HIT_100, HIT_50, HIT_300G, HIT_100K, HIT_MISS }; + int[] rankResultOrder = { + hitResultCount[HIT_300], hitResultCount[HIT_100], + hitResultCount[HIT_50], hitResultCount[HIT_300G], + hitResultCount[HIT_100K] + hitResultCount[HIT_300K], hitResultCount[HIT_MISS] + }; + for (int i = 0; i < rankDrawOrder.length; i++) { + float offsetX = i < 3 ? 0 : resultOffsetX; + float offsetY = (resultOffsetY * (i % 3)); + int startTime = rankStart + i * animationTime; + if (time >= startTime) { + float t = Math.min((float) (time - startTime) / animationTime, 1f); + float tp = AnimationEquation.OUT_CUBIC.calc(t); + float scale = 2f - tp; + float alpha = tp; + Image img = hitResults[rankDrawOrder[i]].getScaledCopy(rankResultScale * scale); + img.setAlpha(alpha); + img.drawCentered( + (resultHitInitialX + offsetX) * uiScale, (resultHitInitialY + offsetY) * uiScale + ); + } + if (time >= startTime + offsetTime) { + float t = Math.min((float) (time - startTime) / animationTime, 1f); + float tp = AnimationEquation.OUT_CUBIC.calc(t); + float alpha = tp; + offsetX += -64f * (1f - tp); + drawSymbolString(String.format("%dx", rankResultOrder[i]), + (resultInitialX + offsetX) * uiScale, (resultInitialY + offsetY) * uiScale, symbolTextScale, alpha, false + ); + } + } } // combo and accuracy float accuracyX = 291; float textY = 480; float numbersY = textY + 48; - drawSymbolString( - String.format("%dx", comboMax), - 24 * uiScale, numbersY * uiScale, symbolTextScale, 1f, false - ); - drawSymbolString( - String.format("%02.2f%%", getScorePercent()), - (accuracyX + 20) * uiScale, numbersY * uiScale, symbolTextScale, 1f, false - ); - GameImage.RANKING_MAXCOMBO.getImage().draw(8 * uiScale, textY * uiScale); - GameImage.RANKING_ACCURACY.getImage().draw(accuracyX * uiScale, textY * uiScale); + if (time >= comboStart) { + float t = Math.min((float) (time - comboStart) / animationTime, 1f); + float alpha = t; + Image img = GameImage.RANKING_MAXCOMBO.getImage(); + img.setAlpha(alpha); + img.draw(8 * uiScale, textY * uiScale); + img.setAlpha(1f); + } + if (time >= comboStart + offsetTime) { + float t = Math.min((float) (time - (comboStart + offsetTime)) / animationTime, 1f); + float tp = AnimationEquation.OUT_CUBIC.calc(t); + float alpha = tp; + float offsetX = -15f * (1f - tp); + drawSymbolString( + String.format("%dx", comboMax), + (24 + offsetX) * uiScale, numbersY * uiScale, symbolTextScale, alpha, false + ); + } + if (time >= comboStart + animationTime) { + float t = Math.min((float) (time - (comboStart + animationTime)) / animationTime, 1f); + float alpha = t; + Image img = GameImage.RANKING_ACCURACY.getImage(); + img.setAlpha(alpha); + img.draw(accuracyX * uiScale, textY * uiScale); + img.setAlpha(1f); + } + if (time >= comboStart + animationTime + offsetTime) { + float t = Math.min((float) (time - (comboStart + animationTime + offsetTime)) / animationTime, 1f); + float tp = AnimationEquation.OUT_CUBIC.calc(t); + float alpha = tp; + float offsetX = -62f * (1f - tp); + drawSymbolString( + String.format("%02.2f%%", getScorePercent()), + (accuracyX + 20 + offsetX) * uiScale, numbersY * uiScale, symbolTextScale, alpha, false + ); + } // full combo - if (comboMax == fullObjectCount) - GameImage.RANKING_PERFECT.getImage().draw(177 * uiScale, 613 * uiScale); + if (time >= perfectStart) { + float t = Math.min((float) (time - perfectStart) / animationTime, 1f); + float tp = AnimationEquation.OUT_CUBIC.calc(t); + float scale = 1.1f - 0.1f * tp; + float alpha = tp; + if (comboMax == fullObjectCount) { + Image img = GameImage.RANKING_PERFECT.getImage().getScaledCopy(scale); + img.setAlpha(alpha); + img.drawCentered(416 * uiScale, 688 * uiScale); + } + } // grade - Grade grade = getGrade(); - if (grade != Grade.NULL) { - Image gradeImg = grade.getLargeImage(); - gradeImg.draw(width - 8 * uiScale - gradeImg.getWidth(), 100 * uiScale); + if (time >= gradeStart) { + float t = Math.min((float) (time - gradeStart) / gradeAnimationTime, 1f); + float tp = AnimationEquation.IN_CUBIC.calc(t); + float scale = 1.5f - 0.5f * tp; + float alpha = tp; + Grade grade = getGrade(); + if (grade != Grade.NULL) { + Image img = grade.getLargeImage(); + float x = width - 8 * uiScale - img.getWidth() / 2f; + float y = 100 * uiScale + img.getHeight() / 2f; + img = img.getScaledCopy(scale); + img.setAlpha(alpha); + img.drawCentered(x, y); + } } // header Image rankingTitle = GameImage.RANKING_TITLE.getImage(); g.setColor(Colors.BLACK_ALPHA); - g.fillRect(0, 0, width, rankingHeight * uiScale); + g.fillRect(0, 0, width, 96 * uiScale); rankingTitle.draw(width - 24 * uiScale - rankingTitle.getWidth(), 0); float marginX = width * 0.01f, marginY = height * 0.002f; Fonts.LARGE.drawString(marginX, marginY, @@ -873,15 +931,40 @@ public void drawRankingElements(Graphics g, Beatmap beatmap) { String.format("Played%s on %s.", player, scoreData.getTimeString()), Color.white); // mod icons - int modWidth = GameMod.AUTO.getImage().getWidth(); - float modX = (width * 0.98f) - modWidth; - int modCount = 0; - for (GameMod mod : GameMod.VALUES_REVERSED) { - if ((mod.getBit() & scoreData.mods) > 0) { - mod.getImage().draw(modX - (modCount * (modWidth / 2f)), height / 2f); - modCount++; + if (scoreData.mods != 0) { + int modWidth = GameMod.AUTO.getImage().getWidth(); + int modHeight = GameMod.AUTO.getImage().getHeight(); + float modX = (width * 0.98f) - modWidth; + int modCount = 0; + for (GameMod mod : GameMod.VALUES_REVERSED) { + if ((scoreData.mods & mod.getBit()) > 0) { + if (time >= animationTime * modCount) { + float t = Math.min((float) (time - animationTime * modCount) / animationTime, 1f); + float tp = AnimationEquation.OUT_CUBIC.calc(t); + float scale = 2f - tp; + float alpha = tp; + Image img = mod.getImage().getScaledCopy(scale); + img.setAlpha(alpha); + img.drawCentered( + modX - (modCount * (modWidth / 2f)) + modWidth / 2f, + height / 2f + modHeight / 2f + ); + modCount++; + } + } } } + + // white flash + if (time >= whiteStart && time < whiteStart + whiteAnimationTime) { + float t = (float) (time - whiteStart) / whiteAnimationTime; + float alpha = 0.75f - 0.75f * AnimationEquation.OUT_CUBIC.calc(t); + float oldWhiteAlpha = Colors.WHITE_FADE.a; + Colors.WHITE_FADE.a = alpha; + g.setColor(Colors.WHITE_FADE); + g.fillRect(0, 0, width, height); + Colors.WHITE_FADE.a = oldWhiteAlpha; + } } /** diff --git a/src/itdelatrisu/opsu/states/GameRanking.java b/src/itdelatrisu/opsu/states/GameRanking.java index 66c3aab6..c8ff609e 100644 --- a/src/itdelatrisu/opsu/states/GameRanking.java +++ b/src/itdelatrisu/opsu/states/GameRanking.java @@ -31,6 +31,8 @@ import itdelatrisu.opsu.replay.Replay; import itdelatrisu.opsu.ui.MenuButton; import itdelatrisu.opsu.ui.UI; +import itdelatrisu.opsu.ui.animations.AnimatedValue; +import itdelatrisu.opsu.ui.animations.AnimationEquation; import java.io.FileNotFoundException; import java.io.IOException; @@ -43,8 +45,8 @@ import org.newdawn.slick.SlickException; import org.newdawn.slick.state.BasicGameState; import org.newdawn.slick.state.StateBasedGame; -import org.newdawn.slick.state.transition.FadeInTransition; import org.newdawn.slick.state.transition.EasedFadeOutTransition; +import org.newdawn.slick.state.transition.FadeInTransition; import org.newdawn.slick.util.Log; /** @@ -64,6 +66,9 @@ public class GameRanking extends BasicGameState { /** Button coordinates. */ private float retryY, replayY; + /** Animation progress. */ + private AnimatedValue animationProgress = new AnimatedValue(6000, 0f, 1f, AnimationEquation.LINEAR); + // game-related variables private GameContainer container; private StateBasedGame game; @@ -108,7 +113,7 @@ public void render(GameContainer container, StateBasedGame game, Graphics g) GameImage.PLAYFIELD.getImage().draw(0,0); // ranking screen elements - data.drawRankingElements(g, beatmap); + data.drawRankingElements(g, beatmap, animationProgress.getTime()); // buttons replayButton.draw(); @@ -130,6 +135,7 @@ public void update(GameContainer container, StateBasedGame game, int delta) else MusicController.loopTrackIfEnded(true); UI.getBackButton().hoverUpdate(delta, mouseX, mouseY); + animationProgress.update(delta); } @Override @@ -209,6 +215,9 @@ else if (data.isGameplay() && game.enterState(Opsu.STATE_GAME, new EasedFadeOutTransition(), new FadeInTransition()); return; } + + // otherwise, finish the animation + animationProgress.setTime(animationProgress.getDuration()); } @Override @@ -220,10 +229,12 @@ public void enter(GameContainer container, StateBasedGame game) if (!MusicController.isTrackDimmed()) MusicController.toggleTrackDimmed(0.5f); replayButton.setY(retryY); + animationProgress.setTime(animationProgress.getDuration()); } else { SoundController.playSound(SoundEffect.APPLAUSE); retryButton.resetHover(); replayButton.setY(!GameMod.AUTO.isActive() ? replayY : retryY); + animationProgress.setTime(0); } replayButton.resetHover(); }