From 597c4bfb1946b2206482b9766261058d911c5d47 Mon Sep 17 00:00:00 2001 From: fd Date: Sat, 14 Feb 2015 13:45:14 -0500 Subject: [PATCH] Visual Changes mostly personal preferences though Larger hit circles Circle overlay after drawing circle Fade in approach circles Fade in sliders (I don't think it looks that bad) Fade out hit lighting Hit Error Bar Probably need an option for this. Miss locations(I don't know) Reverse Slider ball rotation on repeats --- src/itdelatrisu/opsu/GameData.java | 80 ++++++++++++++++++++++++ src/itdelatrisu/opsu/Utils.java | 21 ++++++- src/itdelatrisu/opsu/objects/Circle.java | 26 +++++--- src/itdelatrisu/opsu/objects/Slider.java | 37 +++++++---- src/itdelatrisu/opsu/states/Game.java | 10 ++- 5 files changed, 151 insertions(+), 23 deletions(-) diff --git a/src/itdelatrisu/opsu/GameData.java b/src/itdelatrisu/opsu/GameData.java index 39ba2ac1..f2f32960 100644 --- a/src/itdelatrisu/opsu/GameData.java +++ b/src/itdelatrisu/opsu/GameData.java @@ -153,6 +153,23 @@ public class GameData { /** List of hit result objects associated with hit objects. */ private LinkedList hitResultList; + + class ErrorInfo { + int time, x, y, timeDiff; + + public ErrorInfo(int time, int x, int y, int timeDiff) { + super(); + this.time = time; + this.x = x; + this.y = y; + this.timeDiff = timeDiff; + } + + } + + private LinkedList errorRateList = new LinkedList(); + private LinkedList mouseMissList = new LinkedList(); + /** * Hit result helper class. */ @@ -225,6 +242,10 @@ public class GameData { /** Container dimensions. */ private int width, height; + /** Time offsets for obtaining each hit result (indexed by HIT_* constants). */ + private int[] hitResultOffset; + + /** * Constructor for gameplay. * @param width container width @@ -283,6 +304,8 @@ public class GameData { comboEnd = 0; comboBurstIndex = -1; scoreData = null; + errorRateList.clear(); + mouseMissList.clear(); } /** @@ -364,6 +387,10 @@ public class GameData { public void setDifficulty(float difficulty) { this.difficulty = difficulty; } public float getDifficulty() { return difficulty; } + /** Sets the HitResultOffset */ + public void setHitResultOffset(int[] hitResultOffset) {this.hitResultOffset = hitResultOffset; } + + /** * Draws a number with defaultSymbols. * @param n the number to draw @@ -528,6 +555,47 @@ public class GameData { ); } } + + + //* + //Draw Error bar + final int fadeDelay = 5000; + int hitErrorY = 30; + Iterator iter2 = errorRateList.iterator(); + g.setColor(Color.black); + g.fillRect(width/2f-3-hitResultOffset[HIT_50], height-marginX-hitErrorY-10,hitResultOffset[HIT_50]*2,20); + g.setColor(Utils.COLOR_LIGHT_ORANGE); + g.fillRect(width/2f-3-hitResultOffset[HIT_50], height-marginX-hitErrorY-3,hitResultOffset[HIT_50]*2,6); + g.setColor(Utils.COLOR_LIGHT_GREEN); + g.fillRect(width/2f-3-hitResultOffset[HIT_100], height-marginX-hitErrorY-3,hitResultOffset[HIT_100]*2,6); + g.setColor(Utils.COLOR_LIGHT_BLUE); + g.fillRect(width/2f-3-hitResultOffset[HIT_300], height-marginX-hitErrorY-3,hitResultOffset[HIT_300]*2,6); + g.setColor(Color.white); + g.drawRect(width/2f-3, height-marginX-hitErrorY-10, 6, 20); + while (iter2.hasNext()) { + ErrorInfo info = iter2.next(); + int time = info.time; + if (time + fadeDelay > trackPosition) { + float alpha = 1 - ((float) (trackPosition - time) / fadeDelay); + g.setColor(Color.white.scaleCopy(alpha)); + g.fillRect(width/2 + info.timeDiff-1, height-marginX-hitErrorY-10, 2, 20); + }else{ + iter2.remove(); + } + } + + iter2 = mouseMissList.iterator(); + while (iter2.hasNext()) { + ErrorInfo info = iter2.next(); + int time = info.time; + if (time + fadeDelay > trackPosition) { + //float alpha = 1 - ((float) (trackPosition - time) / fadeDelay); + //g.setColor(Color.green.scaleCopy(alpha)); + //g.fillRect(info.x-5, info.y-5, 10, 10); + }else{ + iter2.remove(); + } + }//*/ } /** @@ -649,6 +717,9 @@ public class GameData { float scale = 1f + ((trackPosition - hitResult.time) / (float) fadeDelay); Image scaledLighting = GameImage.LIGHTING.getImage().getScaledCopy(scale); Image scaledLighting1 = GameImage.LIGHTING1.getImage().getScaledCopy(scale); + scaledLighting.setAlpha(hitResult.alpha); + scaledLighting1.setAlpha(hitResult.alpha); + scaledLighting.draw(hitResult.x - (scaledLighting.getWidth() / 2f), hitResult.y - (scaledLighting.getHeight() / 2f), hitResult.color); scaledLighting1.draw(hitResult.x - (scaledLighting1.getWidth() / 2f), @@ -1008,4 +1079,13 @@ public class GameData { * @return true if gameplay, false if score viewing */ public boolean isGameplay() { return gameplay; } + + public void addMouseMissPoint(int time, int x, int y, int button) { + mouseMissList.add(new ErrorInfo(time, x, y, 0)); + } + + public void addErrorRate(int time, int x, int y, int timeDiff) { + errorRateList.add(new ErrorInfo(time, x, y, timeDiff)); + } + } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index 1914c9c2..4551ff99 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -79,7 +79,10 @@ public class Utils { COLOR_YELLOW_ALPHA = new Color(255, 255, 0, 0.4f), COLOR_WHITE_FADE = new Color(255, 255, 255, 1f), COLOR_RED_HOVER = new Color(255, 112, 112), - COLOR_GREEN = new Color(137, 201, 79); + COLOR_GREEN = new Color(137, 201, 79), + COLOR_LIGHT_ORANGE = new Color(255,192,128), + COLOR_LIGHT_GREEN = new Color(128,255,128), + COLOR_LIGHT_BLUE = new Color(128,128,255); /** The default map colors, used when a map does not provide custom colors. */ public static final Color[] DEFAULT_COMBO = { @@ -838,4 +841,20 @@ public class Utils { list.add(str); return list; } + + /** + * Clamps the value to be in between low and high + * @param val + * @param low + * @param high + * @return val clamped between low and high + * @author fluddokt (https://github.com/fluddokt) + */ + public static float clamp(float val, int low, int high) { + if(val < low) + return low; + if(val > high) + return high; + return val; + } } \ No newline at end of file diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java index 9a49a3af..f7c0b850 100644 --- a/src/itdelatrisu/opsu/objects/Circle.java +++ b/src/itdelatrisu/opsu/objects/Circle.java @@ -55,7 +55,7 @@ public class Circle implements HitObject { * @param circleSize the map's circleSize value */ public static void init(GameContainer container, float circleSize) { - int diameter = (int) (96 - (circleSize * 8)); + int diameter = (int) (104 - (circleSize * 8)); diameter = (int) (diameter * OsuHitObject.getXMultiplier()); // convert from Osupixels (640x480) GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameter, diameter)); GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(diameter, diameter)); @@ -83,15 +83,20 @@ public class Circle implements HitObject { int timeDiff = hitObject.getTime() - trackPosition; if (timeDiff >= 0) { - float x = hitObject.getX(), y = hitObject.getY(); - float approachScale = 1 + (timeDiff * 2f / game.getApproachTime()); - Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color); - float alpha = (approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f; float oldAlpha = color.a; + float x = hitObject.getX(), y = hitObject.getY(); + float scale = timeDiff / (float)game.getApproachTime(); + + float approachScale = 1 + scale * 3; + color.a = 1 - scale; + Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color); + + float alpha = Utils.clamp((1 - scale) * 2, 0, 1); color.a = alpha; Utils.COLOR_WHITE_FADE.a = alpha; - Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), x, y, Utils.COLOR_WHITE_FADE); Utils.drawCentered(GameImage.HITCIRCLE.getImage(), x, y, color); + Utils.drawCentered(GameImage.HITCIRCLE_OVERLAY.getImage(), x, y, Utils.COLOR_WHITE_FADE); + color.a = oldAlpha; Utils.COLOR_WHITE_FADE.a = 1f; data.drawSymbolNumber(hitObject.getComboNumber(), x, y, @@ -105,8 +110,7 @@ public class Circle implements HitObject { * @return the hit result (GameData.HIT_* constants) */ private int hitResult(int time) { - int trackPosition = MusicController.getPosition(); - int timeDiff = Math.abs(trackPosition - time); + int timeDiff = Math.abs(time); int[] hitResultOffset = game.getHitResultOffsets(); int result = -1; @@ -128,8 +132,12 @@ public class Circle implements HitObject { double distance = Math.hypot(hitObject.getX() - x, hitObject.getY() - y); int circleRadius = GameImage.HITCIRCLE.getImage().getWidth() / 2; if (distance < circleRadius) { - int result = hitResult(hitObject.getTime()); + int trackPosition = MusicController.getPosition(); + int timeDiff = trackPosition - hitObject.getTime(); + int result = hitResult(timeDiff); + if (result > -1) { + data.addErrorRate(hitObject.getTime(), x, y, timeDiff); data.hitResult( hitObject.getTime(), result, hitObject.getX(), hitObject.getY(), diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index 1e5de886..6c4029bc 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -98,7 +98,7 @@ public class Slider implements HitObject { * @param osu the associated OsuFile object */ public static void init(GameContainer container, float circleSize, OsuFile osu) { - int diameter = (int) (96 - (circleSize * 8)); + int diameter = (int) (104 - (circleSize * 8)); diameter = (int) (diameter * OsuHitObject.getXMultiplier()); // convert from Osupixels (640x480) // slider ball @@ -110,7 +110,7 @@ public class Slider implements HitObject { sliderBallImages = new Image[]{ GameImage.SLIDER_BALL.getImage() }; for (int i = 0; i < sliderBallImages.length; i++) sliderBallImages[i] = sliderBallImages[i].getScaledCopy(diameter * 118 / 128, diameter * 118 / 128); - sliderBall = new Animation(sliderBallImages, 60); + sliderBall = new Animation(sliderBallImages, 30); GameImage.SLIDER_FOLLOWCIRCLE.setImage(GameImage.SLIDER_FOLLOWCIRCLE.getImage().getScaledCopy(diameter * 259 / 128, diameter * 259 / 128)); GameImage.REVERSEARROW.setImage(GameImage.REVERSEARROW.getImage().getScaledCopy(diameter, diameter)); @@ -143,16 +143,21 @@ public class Slider implements HitObject { @Override public void draw(int trackPosition, boolean currentObject, Graphics g) { - float x = hitObject.getX(), y = hitObject.getY(); int timeDiff = hitObject.getTime() - trackPosition; - - float approachScale = (timeDiff >= 0) ? 1 + (timeDiff * 2f / game.getApproachTime()) : 1f; - float alpha = (approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f; + float oldAlpha = color.a; float oldAlphaFade = Utils.COLOR_WHITE_FADE.a; - color.a = alpha; + + float x = hitObject.getX(), y = hitObject.getY(); + float scale = timeDiff / (float)game.getApproachTime(); + + float approachScale = 1 + scale*3 ; + float alpha = (1 - scale);//(approachScale > 3.3f) ? 0f : 1f - (approachScale - 1f) / 2.7f; + color.a = Utils.clamp(alpha* 0.5f, 0, 1); + + alpha = Utils.clamp(alpha, 0, 1); Utils.COLOR_WHITE_FADE.a = alpha; - + // curve curve.draw(); @@ -164,6 +169,8 @@ public class Slider implements HitObject { tick.drawCentered(c[0], c[1]); } } + + color.a = alpha; Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); Image hitCircle = GameImage.HITCIRCLE.getImage(); @@ -174,8 +181,8 @@ public class Slider implements HitObject { Utils.drawCentered(hitCircleOverlay, endPos[0], endPos[1], Utils.COLOR_WHITE_FADE); // start circle - Utils.drawCentered(hitCircleOverlay, x, y, Utils.COLOR_WHITE_FADE); Utils.drawCentered(hitCircle, x, y, color); + Utils.drawCentered(hitCircleOverlay, x, y, Utils.COLOR_WHITE_FADE); if (sliderClicked) ; // don't draw current combo number if already clicked else @@ -208,22 +215,29 @@ public class Slider implements HitObject { if (timeDiff >= 0) { // approach circle + color.a = 1 - scale; + Utils.drawCentered(GameImage.APPROACHCIRCLE.getImage().getScaledCopy(approachScale), x, y, color); } else { float[] c = curve.pointAt(getT(trackPosition, false)); float[] c2 = curve.pointAt(getT(trackPosition, false) + 0.01f); // slider ball + sliderBall.updateNoDraw();//TODO ..... I can't thing of anything else + //It might be better to make your own animation class or something + //also it might be better to update the animation based on the distance traveled Image sliderBallFrame = sliderBall.getCurrentFrame(); - float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI); + float angle = (float) (Math.atan2(c2[1] - c[1], c2[0] - c[0]) * 180 / Math.PI) + + (currentRepeats % 2 == 1 ? 180 : 0); sliderBallFrame.setRotation(angle); sliderBallFrame.drawCentered(c[0], c[1]); - // follow circle if (followCircleActive) GameImage.SLIDER_FOLLOWCIRCLE.getImage().drawCentered(c[0], c[1]); } } + + /** * Calculates the slider hit result. @@ -275,6 +289,7 @@ public class Slider implements HitObject { //else not a hit if (result > -1) { + data.addErrorRate(hitObject.getTime(), x,y,trackPosition - hitObject.getTime()); sliderClicked = true; data.sliderTickResult(hitObject.getTime(), result, hitObject.getX(), hitObject.getY(), hitObject.getHitSoundType()); diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index a7689a0d..57529a8f 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -664,8 +664,13 @@ public class Game extends BasicGameState { objectIndex++; // circle hit // sliders - else if (hitObject.isSlider()) - hitObjects[objectIndex].mousePressed(x, y); + else if (hitObject.isSlider() && hitObjects[objectIndex].mousePressed(x, y)){ + + } + + //Nothing hit + else + data.addMouseMissPoint(MusicController.getPosition(), x,y,button); } @Override @@ -878,6 +883,7 @@ public class Game extends BasicGameState { // HPDrainRate (health change), overallDifficulty (scoring) data.setDrainRate(HPDrainRate); data.setDifficulty(overallDifficulty); + data.setHitResultOffset(hitResultOffset); } /**