From 80042807c51d178e06b13c0b2a1cbc485ffc175d Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Fri, 4 Sep 2015 22:36:25 -0500 Subject: [PATCH] Draw an animated star stream in the song menu. Star image was modified from XinCrin's "Fantasy's Skin". Signed-off-by: Jeffrey Han --- res/star2.png | Bin 0 -> 3583 bytes src/itdelatrisu/opsu/GameImage.java | 6 + src/itdelatrisu/opsu/states/SongMenu.java | 18 ++- src/itdelatrisu/opsu/ui/StarStream.java | 158 ++++++++++++++++++++++ 4 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 res/star2.png create mode 100644 src/itdelatrisu/opsu/ui/StarStream.java diff --git a/res/star2.png b/res/star2.png new file mode 100644 index 0000000000000000000000000000000000000000..c2160edce8e8330f5e9a2351dbe52b09acafdd68 GIT binary patch literal 3583 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 FV1n*y#qj_D literal 0 HcmV?d00001 diff --git a/src/itdelatrisu/opsu/GameImage.java b/src/itdelatrisu/opsu/GameImage.java index 22e20f0e..e40d3874 100644 --- a/src/itdelatrisu/opsu/GameImage.java +++ b/src/itdelatrisu/opsu/GameImage.java @@ -298,6 +298,12 @@ public enum GameImage { return img.getScaledCopy((MENU_BUTTON_BG.getImage().getHeight() * 0.16f) / img.getHeight()); } }, + STAR2 ("star2", "png", false, false) { + @Override + protected Image process_sub(Image img, int w, int h) { + return img.getScaledCopy((MENU_BUTTON_BG.getImage().getHeight() * 0.33f) / img.getHeight()); + } + }, // Music Player Buttons MUSIC_PLAY ("music-play", "png", false, false), diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index 50b373fa..8fbdf8b1 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -30,23 +30,24 @@ import itdelatrisu.opsu.audio.MultiClip; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundEffect; -import itdelatrisu.opsu.beatmap.BeatmapDifficultyCalculator; import itdelatrisu.opsu.beatmap.Beatmap; +import itdelatrisu.opsu.beatmap.BeatmapDifficultyCalculator; import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.BeatmapSet; import itdelatrisu.opsu.beatmap.BeatmapSetList; import itdelatrisu.opsu.beatmap.BeatmapSetNode; import itdelatrisu.opsu.beatmap.BeatmapSortOrder; import itdelatrisu.opsu.beatmap.BeatmapWatchService; +import itdelatrisu.opsu.beatmap.BeatmapWatchService.BeatmapWatchServiceListener; import itdelatrisu.opsu.beatmap.LRUCache; import itdelatrisu.opsu.beatmap.OszUnpacker; -import itdelatrisu.opsu.beatmap.BeatmapWatchService.BeatmapWatchServiceListener; import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.ScoreDB; import itdelatrisu.opsu.states.ButtonMenu.MenuState; import itdelatrisu.opsu.ui.Colors; import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.MenuButton; +import itdelatrisu.opsu.ui.StarStream; import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.animations.AnimatedValue; import itdelatrisu.opsu.ui.animations.AnimationEquation; @@ -245,6 +246,9 @@ public class SongMenu extends BasicGameState { } }; + /** The star stream. */ + private StarStream starStream; + // game-related variables private GameContainer container; private StateBasedGame game; @@ -336,6 +340,9 @@ public class SongMenu extends BasicGameState { } } }); + + // star stream + starStream = new StarStream(width, height); } @Override @@ -354,6 +361,9 @@ public class SongMenu extends BasicGameState { GameImage.PLAYFIELD.getImage().draw(); } + // star stream + starStream.draw(); + // song buttons BeatmapSetNode node = startNode; int songButtonIndex = 0; @@ -558,6 +568,9 @@ public class SongMenu extends BasicGameState { bgAlpha.update(delta); } + // star stream + starStream.update(delta); + // search search.setFocus(true); searchTimer += delta; @@ -1010,6 +1023,7 @@ public class SongMenu extends BasicGameState { searchTransitionTimer = SEARCH_TRANSITION_TIME; songInfo = null; bgAlpha.setTime(bgAlpha.getDuration()); + starStream.clear(); // reset song stack randomStack = new Stack(); diff --git a/src/itdelatrisu/opsu/ui/StarStream.java b/src/itdelatrisu/opsu/ui/StarStream.java new file mode 100644 index 00000000..3d79aa52 --- /dev/null +++ b/src/itdelatrisu/opsu/ui/StarStream.java @@ -0,0 +1,158 @@ +/* + * opsu! - an open-source osu! client + * Copyright (C) 2014, 2015 Jeffrey Han + * + * opsu! is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * opsu! is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with opsu!. If not, see . + */ + +package itdelatrisu.opsu.ui; + +import itdelatrisu.opsu.GameImage; +import itdelatrisu.opsu.Utils; +import itdelatrisu.opsu.ui.animations.AnimatedValue; +import itdelatrisu.opsu.ui.animations.AnimationEquation; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +import org.newdawn.slick.Image; + +/** + * Horizontal star stream. + */ +public class StarStream { + /** The container dimensions. */ + private final int containerWidth, containerHeight; + + /** The star image. */ + private final Image starImg; + + /** The current list of stars. */ + private final List stars; + + /** The maximum number of stars to draw at once. */ + private static final int MAX_STARS = 20; + + /** Random number generator instance. */ + private final Random random; + + /** Contains data for a single star. */ + private class Star { + /** The star animation progress. */ + private final AnimatedValue animatedValue; + + /** The star properties. */ + private final int distance, yOffset, angle; + + /** + * Creates a star with the given properties. + * @param duration the time, in milliseconds, to show the star + * @param distance the distance for the star to travel in {@code duration} + * @param yOffset the vertical offset from the center of the container + * @param angle the rotation angle + * @param eqn the animation equation to use + */ + public Star(int duration, int distance, int yOffset, int angle, AnimationEquation eqn) { + this.animatedValue = new AnimatedValue(duration, 0f, 1f, eqn); + this.distance = distance; + this.yOffset = yOffset; + this.angle = angle; + } + + /** + * Draws the star. + */ + public void draw() { + float t = animatedValue.getValue(); + starImg.setAlpha(Math.min((1 - t) * 5f, 1f)); + starImg.setRotation(angle); + starImg.draw(containerWidth - (distance * t), ((containerHeight - starImg.getHeight()) / 2) + yOffset); + } + + /** + * Updates the animation by a delta interval. + * @param delta the delta interval since the last call + * @return true if an update was applied, false if the animation was not updated + */ + public boolean update(int delta) { return animatedValue.update(delta); } + } + + /** + * Initializes the star stream. + * @param width the container width + * @param height the container height + */ + public StarStream(int width, int height) { + this.containerWidth = width; + this.containerHeight = height; + this.starImg = GameImage.STAR2.getImage().copy(); + this.stars = new ArrayList(); + this.random = new Random(); + } + + /** + * Draws the star stream. + */ + public void draw() { + for (Star star : stars) + star.draw(); + } + + /** + * Updates the stars in the stream by a delta interval. + * @param delta the delta interval since the last call + */ + public void update(int delta) { + // update current stars + Iterator iter = stars.iterator(); + while (iter.hasNext()) { + Star star = iter.next(); + if (!star.update(delta)) + iter.remove(); + } + + // create new stars + for (int i = stars.size(); i < MAX_STARS; i++) { + if (Math.random() < ((i < 5) ? 0.25 : 0.66)) + break; + + // generate star properties + float distanceRatio = Utils.clamp((float) getGaussian(0.65, 0.25), 0.2f, 0.925f); + int distance = (int) (containerWidth * distanceRatio); + int duration = (int) (distanceRatio * getGaussian(1300, 300)); + int yOffset = (int) getGaussian(0, containerHeight / 20); + int angle = (int) getGaussian(0, 22.5); + AnimationEquation eqn = random.nextBoolean() ? AnimationEquation.IN_OUT_QUAD : AnimationEquation.OUT_QUAD; + + stars.add(new Star(duration, distance, angle, yOffset, eqn)); + } + } + + /** + * Clears the stars currently in the stream. + */ + public void clear() { stars.clear(); } + + /** + * Returns the next pseudorandom, Gaussian ("normally") distributed {@code double} value + * with the given mean and standard deviation. + * @param mean the mean + * @param stdDev the standard deviation + */ + private double getGaussian(double mean, double stdDev) { + return mean + random.nextGaussian() * stdDev; + } +}