Merge pull request #37 from fluddokt/SpinnerTest

Spinner Changes
- frame independent spin up/average
- rpm for Auto/SpunOut
- CCW rotation
- fade in
- addition
This commit is contained in:
Jeffrey Han 2015-03-07 15:11:16 -05:00
commit f4d17b9148
3 changed files with 79 additions and 40 deletions

View File

@ -201,7 +201,12 @@ public class OsuHitObject {
if (index != -1) if (index != -1)
tokens[5] = tokens[5].substring(0, index); tokens[5] = tokens[5].substring(0, index);
this.endTime = Integer.parseInt(tokens[5]); this.endTime = Integer.parseInt(tokens[5]);
/* TODO: 'addition' not implemented. */ if (tokens.length > 6) {
String[] additionTokens = tokens[6].split(":");
this.addition = new byte[additionTokens.length];
for (int j = 0; j < additionTokens.length; j++)
this.addition[j] = Byte.parseByte(additionTokens[j]);
}
} }
} }

View File

@ -256,7 +256,7 @@ public class Utils {
* @return the clamped value * @return the clamped value
* @author fluddokt * @author fluddokt
*/ */
public static float clamp(float val, int low, int high) { public static float clamp(float val, float low, float high) {
if (val < low) if (val < low)
return low; return low;
if (val > high) if (val > high)

View File

@ -40,9 +40,20 @@ public class Spinner implements HitObject {
/** Container dimensions. */ /** Container dimensions. */
private static int width, height; private static int width, height;
// Currently it takes about 200ms to spin up (4 * 50)
/** The number of rotation velocities to store. */ /** The number of rotation velocities to store. */
private static final int MAX_ROTATION_VELOCITIES = 50; private static final int MAX_ROTATION_VELOCITIES = 50;
/** The amount of time in ms before another velocity is stored */
private static final int DELTA_UPDATE_TIME = 4;
/** The amount of time in ms to fade in the spinner*/
private static final float FADE_IN_TIME = 500;
/** The remaining amount of time that was not used */
private int deltaOverflow;
/** PI constants. */ /** PI constants. */
private static final float TWO_PI = (float) (Math.PI * 2); private static final float TWO_PI = (float) (Math.PI * 2);
@ -58,6 +69,9 @@ public class Spinner implements HitObject {
/** The current number of rotations. */ /** The current number of rotations. */
private float rotations = 0f; private float rotations = 0f;
/** The current rotation to draw */
private float drawRotation = 0f;
/** The total number of rotations needed to clear the spinner. */ /** The total number of rotations needed to clear the spinner. */
private float rotationsNeeded; private float rotationsNeeded;
@ -99,24 +113,29 @@ public class Spinner implements HitObject {
@Override @Override
public void draw(int trackPosition, boolean currentObject, Graphics g) { public void draw(int trackPosition, boolean currentObject, Graphics g) {
// only draw spinners if current object
if (!currentObject)
return;
int timeDiff = hitObject.getTime() - trackPosition; int timeDiff = hitObject.getTime() - trackPosition;
boolean spinnerComplete = (rotations >= rotationsNeeded); boolean spinnerComplete = (rotations >= rotationsNeeded);
// darken screen // only draw spinners if
g.setColor(Utils.COLOR_BLACK_ALPHA); if (timeDiff - FADE_IN_TIME > 0 )
g.fillRect(0, 0, width, height);
if (timeDiff > 0)
return; return;
// rpm (TODO: make this work for Auto/Spun-Out mods)
// darken screen
float alpha = Utils.clamp(1 - timeDiff / FADE_IN_TIME, 0f, 1f);
if (timeDiff > 0) {
Color c = new Color(Utils.COLOR_BLACK_ALPHA);
c.a *= alpha;
g.setColor(c);
} else
g.setColor(Utils.COLOR_BLACK_ALPHA);
g.fillRect(0, 0, width, height);
int rpm = Math.abs(Math.round(sumVelocity / storedVelocities.length * 60)); int rpm = Math.abs(Math.round(sumVelocity / storedVelocities.length * 60));
Image rpmImg = GameImage.SPINNER_RPM.getImage(); Image rpmImg = GameImage.SPINNER_RPM.getImage();
rpmImg.setAlpha(alpha);
rpmImg.drawCentered(width / 2f, height - rpmImg.getHeight() / 2f); rpmImg.drawCentered(width / 2f, height - rpmImg.getHeight() / 2f);
if(timeDiff < 0)
data.drawSymbolString(Integer.toString(rpm), (int) ((width + rpmImg.getWidth() * 0.95f) / 2f), data.drawSymbolString(Integer.toString(rpm), (int) ((width + rpmImg.getWidth() * 0.95f) / 2f),
(int) (height - data.getScoreSymbolImage('0').getHeight() * 1.025f), 1f, 1f, true); (int) (height - data.getScoreSymbolImage('0').getHeight() * 1.025f), 1f, 1f, true);
@ -127,13 +146,19 @@ public class Spinner implements HitObject {
0, spinnerMetreY, 0, spinnerMetreY,
spinnerMetre.getWidth(), spinnerMetre.getHeight() - spinnerMetreY spinnerMetre.getWidth(), spinnerMetre.getHeight() - spinnerMetreY
); );
spinnerMetreSub.setAlpha(alpha);
spinnerMetreSub.draw(0, height - spinnerMetreSub.getHeight()); spinnerMetreSub.draw(0, height - spinnerMetreSub.getHeight());
// main spinner elements // main spinner elements
float approachScale = 1 - ((float) timeDiff / (hitObject.getTime() - hitObject.getEndTime())); float approachScale = 1 - Utils.clamp( ((float) timeDiff / (hitObject.getTime() - hitObject.getEndTime())), 0f, 1f);
GameImage.SPINNER_CIRCLE.getImage().setRotation(rotations * 360f);
GameImage.SPINNER_CIRCLE.getImage().setAlpha(alpha);
GameImage.SPINNER_CIRCLE.getImage().setRotation(drawRotation * 360f);
GameImage.SPINNER_CIRCLE.getImage().drawCentered(width / 2, height / 2); GameImage.SPINNER_CIRCLE.getImage().drawCentered(width / 2, height / 2);
GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(approachScale).drawCentered(width / 2, height / 2); Image approachCircleScaled = GameImage.SPINNER_APPROACHCIRCLE.getImage().getScaledCopy(approachScale);
approachCircleScaled.setAlpha(alpha);
approachCircleScaled.drawCentered(width / 2, height / 2);
GameImage.SPINNER_SPIN.getImage().setAlpha(alpha);
GameImage.SPINNER_SPIN.getImage().drawCentered(width / 2, height * 3 / 4); GameImage.SPINNER_SPIN.getImage().drawCentered(width / 2, height * 3 / 4);
if (spinnerComplete) { if (spinnerComplete) {
@ -181,32 +206,37 @@ public class Spinner implements HitObject {
return true; return true;
} }
// spin automatically (TODO: correct rotation angles)
if (GameMod.AUTO.isActive()) {
// "auto" mod (fast)
data.changeHealth(delta * GameData.HP_DRAIN_MULTIPLIER);
rotate(delta / 20f);
return false;
} else if (GameMod.SPUN_OUT.isActive()) {
// "spun out" mod (slow)
data.changeHealth(delta * GameData.HP_DRAIN_MULTIPLIER);
rotate(delta / 32f);
return false;
}
// game button is released // game button is released
if (isSpinning && !(Utils.isGameKeyPressed() || GameMod.RELAX.isActive())) if (isSpinning && !(Utils.isGameKeyPressed() || GameMod.RELAX.isActive()))
isSpinning = false; isSpinning = false;
float angle = (float) Math.atan2(mouseY - (height / 2), mouseX - (width / 2));
// set initial angle to current mouse position to skip first click float angle;
if (!isSpinning && (Utils.isGameKeyPressed() || GameMod.RELAX.isActive())) { // http://osu.ppy.sh/wiki/FAQ#Spinners
lastAngle = angle; // spin automatically (TODO: correct rotation angles)
if (GameMod.AUTO.isActive()) {
// "auto" mod (fast)
lastAngle = 0;
//angle = 477/60f * delta/1000f * TWO_PI; ~delta/20
angle = delta / 20f;
isSpinning = true; isSpinning = true;
return false; } else if (GameMod.SPUN_OUT.isActive()) {
// "spun out" mod (slow)
lastAngle = 0;
//angle = 287/60f * delta/1000f * TWO_PI;
angle = delta / 32f;
isSpinning = true;
} else {
angle = (float) Math.atan2(mouseY - (height / 2), mouseX - (width / 2));
// set initial angle to current mouse position to skip first click
if (!isSpinning && (Utils.isGameKeyPressed() || GameMod.RELAX.isActive())) {
lastAngle = angle;
isSpinning = true;
return false;
}
} }
float angleDiff = angle - lastAngle; float angleDiff = angle - lastAngle;
// make angleDiff the smallest angle change possible // make angleDiff the smallest angle change possible
@ -219,16 +249,19 @@ public class Spinner implements HitObject {
// spin caused by the cursor // spin caused by the cursor
float cursorVelocity = 0; float cursorVelocity = 0;
if (isSpinning) if (isSpinning)
cursorVelocity = Math.min(angleDiff / TWO_PI / delta * 1000, 8f); cursorVelocity = Utils.clamp(angleDiff / TWO_PI / delta * 1000, -8f, 8f);
sumVelocity -= storedVelocities[velocityIndex];
sumVelocity += cursorVelocity;
storedVelocities[velocityIndex++] = cursorVelocity;
velocityIndex %= storedVelocities.length;
deltaOverflow += delta;
while(deltaOverflow >= DELTA_UPDATE_TIME){
sumVelocity -= storedVelocities[velocityIndex];
sumVelocity += cursorVelocity;
storedVelocities[velocityIndex++] = cursorVelocity;
velocityIndex %= storedVelocities.length;
deltaOverflow -= DELTA_UPDATE_TIME;
}
float rotationAngle = sumVelocity / storedVelocities.length * TWO_PI * delta / 1000; float rotationAngle = sumVelocity / storedVelocities.length * TWO_PI * delta / 1000;
rotate(rotationAngle); rotate(rotationAngle);
if (rotationAngle > 0.00001f) if (Math.abs(rotationAngle) > 0.00001f)
data.changeHealth(delta * GameData.HP_DRAIN_MULTIPLIER); data.changeHealth(delta * GameData.HP_DRAIN_MULTIPLIER);
lastAngle = angle; lastAngle = angle;
@ -240,6 +273,7 @@ public class Spinner implements HitObject {
* @param angle the angle to rotate (in radians) * @param angle the angle to rotate (in radians)
*/ */
private void rotate(float angle) { private void rotate(float angle) {
drawRotation += (angle / TWO_PI);
angle = Math.abs(angle); angle = Math.abs(angle);
float newRotations = rotations + (angle / TWO_PI); float newRotations = rotations + (angle / TWO_PI);