Added pretty much everything suggested to hit animations

This commit is contained in:
Drew Lemmy 2015-04-07 06:24:09 +01:00
parent c3bca9a39e
commit 8e8fda7e58
6 changed files with 103 additions and 36 deletions

View File

@ -23,6 +23,7 @@ import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.objects.HitResultType;
import itdelatrisu.opsu.replay.Replay;
import itdelatrisu.opsu.replay.ReplayFrame;
@ -48,7 +49,7 @@ public class GameData {
public static final int HITRESULT_FADE_TIME = 500;
/** Time, in milliseconds, for a hit circle to fade. */
public static final int HITCIRCLE_FADE_TIME = 233;
public static final int HITCIRCLE_FADE_TIME = 300;
/** Duration, in milliseconds, of a combo pop effect. */
private static final int COMBO_POP_TIME = 250;
@ -56,6 +57,18 @@ public class GameData {
/** Time, in milliseconds, for a hit error tick to fade. */
private static final int HIT_ERROR_FADE_TIME = 5000;
/** Size of a hit circle at the end of the hit animation. */
private static final float HITCIRCLE_ANIM_SCALE = 1.38f;
/** Size of the hit result text at the end of its animation. */
private static final float HITCIRCLE_TEXT_ANIM_SCALE = 1.28f;
/** Time, in milliseconds, for the hit result text to bounce. */
private static final int HITCIRCLE_TEXT_BOUNCE_TIME = 100;
/** Time, in milliseconds, for the hit result text to fade. */
private static final int HITCIRCLE_TEXT_FADE_TIME = 833;
/** Letter grades. */
public enum Grade {
NULL (null, null),
@ -224,8 +237,8 @@ public class GameData {
/** Combo color. */
public Color color;
/** Whether the hit object was a spinner. */
public boolean isSpinner;
/** The type of the hit object. */
public HitResultType hitResultType;
/** Alpha level (for fading out). */
public float alpha = 1f;
@ -237,15 +250,15 @@ public class GameData {
* @param x the center x coordinate
* @param y the center y coordinate
* @param color the color of the hit object
* @param isSpinner whether the hit object was a spinner
* @param hitResultType the type of the hit object
*/
public OsuHitObjectResult(int time, int result, float x, float y, Color color, boolean isSpinner) {
public OsuHitObjectResult(int time, int result, float x, float y, Color color, HitResultType hitResultType) {
this.time = time;
this.result = result;
this.x = x;
this.y = y;
this.color = color;
this.isSpinner = isSpinner;
this.hitResultType = hitResultType;
}
}
@ -827,7 +840,7 @@ public class GameData {
OsuHitObjectResult hitResult = iter.next();
if (hitResult.time + HITRESULT_FADE_TIME > trackPosition) {
// spinner
if (hitResult.isSpinner && hitResult.result != HIT_MISS) {
if (hitResult.hitResultType == HitResultType.SPINNER && hitResult.result != HIT_MISS) {
Image spinnerOsu = GameImage.SPINNER_OSU.getImage();
spinnerOsu.setAlpha(hitResult.alpha);
spinnerOsu.drawCentered(width / 2, height / 4);
@ -843,21 +856,55 @@ public class GameData {
lighting.drawCentered(hitResult.x, hitResult.y, hitResult.color);
}
// hit result
hitResults[hitResult.result].setAlpha(hitResult.alpha);
hitResults[hitResult.result].drawCentered(hitResult.x, hitResult.y);
hitResults[hitResult.result].setAlpha(1f);
// hit animation
Image scaledHitCircle = GameImage.HITCIRCLE.getImage().getScaledCopy(
1f + (((float)(trackPosition - hitResult.time) / HITCIRCLE_FADE_TIME) / 2));
scaledHitCircle.setAlpha(1f - Utils.clamp((float)(trackPosition - hitResult.time) / HITCIRCLE_FADE_TIME, 0, 1));
Image scaledHitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(
1f + (((float)(trackPosition - hitResult.time) / HITCIRCLE_FADE_TIME) / 2));
scaledHitCircleOverlay.setAlpha(1f - Utils.clamp((float) (trackPosition - hitResult.time) / HITCIRCLE_FADE_TIME, 0, 1));
if (hitResult.hitResultType == HitResultType.CIRCLE
|| hitResult.hitResultType == HitResultType.SLIDEREND
|| hitResult.hitResultType == HitResultType.SLIDEREND_FIRSTOBJECT) {
float scale = Utils.easeOut(
Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_FADE_TIME),
1f,
HITCIRCLE_ANIM_SCALE-1f,
HITCIRCLE_FADE_TIME
);
scaledHitCircle.drawCentered(hitResult.x, hitResult.y, hitResult.color);
scaledHitCircleOverlay.drawCentered(hitResult.x, hitResult.y);
float alpha = Utils.easeOut(
Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_FADE_TIME),
1f,
-1f,
HITCIRCLE_FADE_TIME
);
Image scaledHitCircle = GameImage.HITCIRCLE.getImage().getScaledCopy(scale);
scaledHitCircle.setAlpha(alpha);
Image scaledHitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(scale);
scaledHitCircleOverlay.setAlpha(alpha);
scaledHitCircle.drawCentered(hitResult.x, hitResult.y, hitResult.color);
scaledHitCircleOverlay.drawCentered(hitResult.x, hitResult.y);
}
// hit result
if (hitResult.hitResultType == HitResultType.CIRCLE
|| hitResult.hitResultType == HitResultType.SLIDEREND
|| hitResult.hitResultType == HitResultType.SPINNER) {
float scale = Utils.easeBounce(
Utils.clamp(trackPosition - hitResult.time, 0, HITCIRCLE_TEXT_BOUNCE_TIME),
1f,
HITCIRCLE_TEXT_ANIM_SCALE - 1f,
HITCIRCLE_TEXT_BOUNCE_TIME
);
float alpha = Utils.easeOut(
Utils.clamp((trackPosition - hitResult.time) - HITCIRCLE_FADE_TIME, 0, HITCIRCLE_TEXT_FADE_TIME),
1f,
-1f,
HITCIRCLE_TEXT_FADE_TIME
);
Image scaledHitResult = hitResults[hitResult.result].getScaledCopy(scale);
scaledHitResult.setAlpha(alpha);
scaledHitResult.drawCentered(hitResult.x, hitResult.y);
}
hitResult.alpha = 1 - ((float) (trackPosition - hitResult.time) / HITRESULT_FADE_TIME);
} else
@ -1123,7 +1170,7 @@ public class GameData {
if (!Options.isPerfectHitBurstEnabled())
; // hide perfect hit results
else
hitResultList.add(new OsuHitObjectResult(time, result, x, y, null, false));
hitResultList.add(new OsuHitObjectResult(time, result, x, y, null, HitResultType.SLIDERTICK));
}
}
@ -1137,9 +1184,10 @@ public class GameData {
* @param end true if this is the last hit object in the combo
* @param hitObject the hit object
* @param repeat the current repeat number (for sliders, or 0 otherwise)
* @param hitResultType the type of hit object for the result
*/
public void hitResult(int time, int result, float x, float y, Color color,
boolean end, OsuHitObject hitObject, int repeat) {
boolean end, OsuHitObject hitObject, int repeat, HitResultType hitResultType) {
int hitValue = 0;
boolean perfectHit = false;
switch (result) {
@ -1210,7 +1258,7 @@ public class GameData {
else if (result == HIT_MISS && (GameMod.RELAX.isActive() || GameMod.AUTOPILOT.isActive()))
; // "relax" and "autopilot" mods: hide misses
else
hitResultList.add(new OsuHitObjectResult(time, result, x, y, color, hitObject.isSpinner()));
hitResultList.add(new OsuHitObjectResult(time, result, x, y, color, hitResultType));
}
/**

View File

@ -607,4 +607,14 @@ public class Utils {
else
return String.format("%02d:%02d:%02d", seconds / 3600, (seconds / 60) % 60, seconds % 60);
}
public static float easeOut(float t, float a, float b, float d) {
return b * ((t = t / d - 1f) * t * t + 1f) + a;
}
public static float easeBounce(float t, float a, float b, float d) {
if (t < d / 2)
return easeOut(t, a, b, d);
return easeOut(d-t, a, b, d);
}
}

View File

@ -138,7 +138,7 @@ public class Circle implements HitObject {
if (result > -1) {
data.addHitError(hitObject.getTime(), x, y, timeDiff);
data.hitResult(hitObject.getTime(), result, this.x, this.y, color, comboEnd, hitObject, 0);
data.hitResult(hitObject.getTime(), result, this.x, this.y, color, comboEnd, hitObject, 0, HitResultType.CIRCLE);
return true;
}
}
@ -154,17 +154,17 @@ public class Circle implements HitObject {
if (overlap || trackPosition > time + hitResultOffset[GameData.HIT_50]) {
if (isAutoMod) // "auto" mod: catch any missed notes due to lag
data.hitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, 0);
data.hitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, 0, HitResultType.CIRCLE);
else // no more points can be scored, so send a miss
data.hitResult(time, GameData.HIT_MISS, x, y, null, comboEnd, hitObject, 0);
data.hitResult(time, GameData.HIT_MISS, x, y, null, comboEnd, hitObject, 0, HitResultType.CIRCLE);
return true;
}
// "auto" mod: send a perfect hit result
else if (isAutoMod) {
if (Math.abs(trackPosition - time) < hitResultOffset[GameData.HIT_300]) {
data.hitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, 0);
data.hitResult(time, GameData.HIT_300, x, y, color, comboEnd, hitObject, 0, HitResultType.CIRCLE);
return true;
}
}

View File

@ -0,0 +1,10 @@
package itdelatrisu.opsu.objects;
public enum HitResultType {
CIRCLE,
SLIDERSTART,
SLIDERTICK,
SLIDEREND,
SLIDEREND_FIRSTOBJECT,
SPINNER
}

View File

@ -278,14 +278,13 @@ public class Slider implements HitObject {
else
result = GameData.HIT_MISS;
if (currentRepeats % 2 == 0) { // last circle
float[] lastPos = curve.pointAt(1);
data.hitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
lastPos[0], lastPos[1], color, comboEnd, hitObject, currentRepeats + 1);
} else { // first circle
data.hitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
x, y, color, comboEnd, hitObject, currentRepeats + 1);
}
float[] lastPos = curve.pointAt(1);
data.hitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
x, y, color, comboEnd, hitObject, currentRepeats + 1,
currentRepeats % 2 == 0 ? HitResultType.SLIDEREND_FIRSTOBJECT : HitResultType.SLIDEREND);
data.hitResult(hitObject.getTime() + (int) sliderTimeTotal, result,
lastPos[0], lastPos[1], color, comboEnd, hitObject, currentRepeats + 1,
currentRepeats % 2 == 0 ? HitResultType.SLIDEREND : HitResultType.SLIDEREND_FIRSTOBJECT);
return result;
}

View File

@ -191,7 +191,7 @@ public class Spinner implements HitObject {
result = GameData.HIT_MISS;
data.hitResult(hitObject.getEndTime(), result, width / 2, height / 2,
Color.transparent, true, hitObject, 0);
Color.transparent, true, hitObject, 0, HitResultType.SPINNER);
return result;
}