From e9caad20e606fb513b3e3c1a462693581b5d842b Mon Sep 17 00:00:00 2001 From: Althoumb Date: Fri, 30 Mar 2018 23:08:59 -0700 Subject: [PATCH 1/4] creates hitobjects based on a beatmap linked list created by a parser --- src/game/Beat.java | 20 +++++++++ src/game/BeatmapParser.java | 12 ++++++ src/game/HitObject.java | 24 +++++++++++ src/game/RhythmState.java | 82 +++++++++++++++++++------------------ 4 files changed, 99 insertions(+), 39 deletions(-) create mode 100644 src/game/Beat.java create mode 100644 src/game/BeatmapParser.java create mode 100644 src/game/HitObject.java diff --git a/src/game/Beat.java b/src/game/Beat.java new file mode 100644 index 0000000..c4d0e1f --- /dev/null +++ b/src/game/Beat.java @@ -0,0 +1,20 @@ +package game; + +public class Beat { + int x; //x position of the beat circle + int y; //y position of the beat circle + int time; //time, in ms, of the beat from the beginning of the song + //Clip hitSound; to be used later, when sounds are added + + public Beat() { + this.x = 0; + this.y = 0; + this.time = 0; + } + + public Beat(int x, int y, int time) { + this.x = x; + this.y = y; + this.time = time; + } +} diff --git a/src/game/BeatmapParser.java b/src/game/BeatmapParser.java new file mode 100644 index 0000000..e422557 --- /dev/null +++ b/src/game/BeatmapParser.java @@ -0,0 +1,12 @@ +package game; + +import java.util.LinkedList; + +public class BeatmapParser { + + public LinkedList parseBeatmap() { + LinkedList beatlist = new LinkedList(); + beatlist.add(new Beat(250, 250, 1000)); + return beatlist; + } +} diff --git a/src/game/HitObject.java b/src/game/HitObject.java new file mode 100644 index 0000000..abe2a2d --- /dev/null +++ b/src/game/HitObject.java @@ -0,0 +1,24 @@ +package game; + +public class HitObject { + int x; + int y; + float radius; + float duration; + boolean clicked = false; + + public HitObject() { + this.x = 0; + this.y = 0; + this.radius = 0; + this.duration = 0; + } + + public HitObject(int x, int y, float radius, float duration, boolean clicked) { + this.x = x; + this.y = y; + this.radius = radius; + this.duration = duration; + this.clicked = clicked; + } +} diff --git a/src/game/RhythmState.java b/src/game/RhythmState.java index d4d1f31..11b86f4 100644 --- a/src/game/RhythmState.java +++ b/src/game/RhythmState.java @@ -1,5 +1,9 @@ package game; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.concurrent.CopyOnWriteArrayList; + import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; @@ -13,12 +17,7 @@ public class RhythmState extends DefaultGameState{ - public final float CIRCLE_TIME = 400f; //time it takes for circles to shrink. the time the circles are on screen is double this, as the circles grow, then shrink. - - private Vector2f curpos = new Vector2f(); //position of the current circle (shrinking circle) - private Vector2f nextCircle = new Vector2f(); //position of the next circle (growing circle) - private float prevCircleSize = 0; //current circle size - private float nextCircleSize = 0; //next circle size + public final float CIRCLE_TIME = 500f; //time it takes for circles to shrink. the time the circles are on screen is double this, as the circles grow, then shrink. private float circleTime = -1f; //changing this doesn't do anything. must be negative, however private Image background; //background image @@ -30,6 +29,11 @@ public class RhythmState extends DefaultGameState{ private float maxRadius = CIRCLE_TIME * timescale; //maximum radius of the circles private long starttime = System.currentTimeMillis(); //initial time + private long songtime = 0; //time since beginning of the song, in ms + + private CopyOnWriteArrayList hitobjects = new CopyOnWriteArrayList<>(); + private LinkedList beatmap = new LinkedList<>(); + private int beatmapindex = 0; @Override public void enter(GameContainer arg0, StateBasedGame arg1) throws SlickException { @@ -47,6 +51,9 @@ public int getID() { public void init(GameContainer gc, StateBasedGame arg1) throws SlickException { background = new Image("res/background.jpg"); background = background.getScaledCopy(gc.getWidth(), gc.getHeight()); + + BeatmapParser beatmapparser = new BeatmapParser(); + beatmap = beatmapparser.parseBeatmap(); } @Override @@ -56,21 +63,16 @@ public void leave(GameContainer arg0, StateBasedGame arg1) throws SlickException @Override public void render(GameContainer gc, StateBasedGame arg1, Graphics g) throws SlickException { g.drawImage(background, 0, 0); - g.setColor(Color.cyan); - g.setLineWidth(10f); - g.drawLine(curpos.x, curpos.y, nextCircle.x, nextCircle.y); - if (nextcircleclicked) { - g.setColor(Color.green); - } else { - g.setColor(Color.cyan); - } - g.fill(new Circle(nextCircle.x, nextCircle.y, nextCircleSize)); - if (clicked) { - g.setColor(Color.green); - } else { - g.setColor(Color.cyan); + + for (HitObject hitobject:hitobjects) { + if (hitobject.clicked) { + g.setColor(Color.green); + } else { + g.setColor(Color.white); + } + g.fill(new Circle(hitobject.x, hitobject.y, hitobject.radius)); } - g.fill(new Circle(curpos.x, curpos.y, prevCircleSize)); + g.setColor(Color.white); g.drawString("Points: " + points, gc.getWidth() - 200, 50); g.drawString("Points per second " + (1000f * points / (System.currentTimeMillis() - starttime)), gc.getWidth() - 600, 70); @@ -78,21 +80,24 @@ public void render(GameContainer gc, StateBasedGame arg1, Graphics g) throws Sli @Override public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException { - if (circleTime < 0) { - // interval from radius to width - radius - maxRadius = CIRCLE_TIME * timescale; - curpos.set(nextCircle); - nextCircle = new Vector2f((float) randomRange(maxRadius, gc.getWidth() - maxRadius), - (float) randomRange(maxRadius, gc.getHeight() - maxRadius)); - circleTime = CIRCLE_TIME * timescale; - clicked = nextcircleclicked; - nextcircleclicked = false; + songtime += delta; + + if ((beatmapindex<=beatmap.size()-1)&&(beatmap.get(beatmapindex).time<=songtime)) { + Beat currentbeat = beatmap.get(beatmapindex); + HitObject hitobject = new HitObject(currentbeat.x, currentbeat.y, maxRadius, CIRCLE_TIME, false); + hitobjects.add(hitobject); + beatmapindex++; } - else { - circleTime -= delta * timescale; + + if (!hitobjects.isEmpty()) { + for (HitObject hitobject:hitobjects) { + int index = hitobjects.indexOf(hitobject); + hitobjects.set(index, new HitObject(hitobject.x, hitobject.y, maxRadius * (hitobject.duration / CIRCLE_TIME), hitobject.duration - delta, hitobject.clicked)); + if (hitobject.duration<=0) { + hitobjects.remove(index); + } + } } - prevCircleSize = circleTime; - nextCircleSize = maxRadius - prevCircleSize; } @@ -108,12 +113,11 @@ public boolean isAcceptingInput() { @Override public void mousePressed(int button, int x, int y) { if (button == Input.MOUSE_LEFT_BUTTON) { - if ((!clicked)&&(new Vector2f(x, y).distance(curpos) < prevCircleSize)) { //checks if current circle has already been clicked, then checks if click is within the circle - clicked = true; - points ++; - } else if ((!nextcircleclicked)&&(new Vector2f(x, y).distance(nextCircle) < nextCircleSize)){ //checks if next circle has already been clicked, then checks if click is within the circle - nextcircleclicked = true; - points ++; + for (HitObject hitobject:hitobjects) { + if ((!hitobject.clicked)&&((new Vector2f(x, y).distance(new Vector2f(hitobject.x, hitobject.y))) < hitobject.radius)) { //checks if current circle has already been clicked, then checks if click is within the circle + hitobjects.set(hitobjects.indexOf(hitobject), new HitObject(hitobject.x, hitobject.y, hitobject.radius, hitobject.duration, true)); + points++; + } } } } From 47720a251dfc0b88ee7f6d3345733093cc7f50f0 Mon Sep 17 00:00:00 2001 From: Althoumb Date: Fri, 30 Mar 2018 23:11:14 -0700 Subject: [PATCH 2/4] you can now only click one beat at a time --- src/game/BeatmapParser.java | 1 + src/game/RhythmState.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game/BeatmapParser.java b/src/game/BeatmapParser.java index e422557..2988151 100644 --- a/src/game/BeatmapParser.java +++ b/src/game/BeatmapParser.java @@ -7,6 +7,7 @@ public class BeatmapParser { public LinkedList parseBeatmap() { LinkedList beatlist = new LinkedList(); beatlist.add(new Beat(250, 250, 1000)); + beatlist.add(new Beat(300, 300, 1250)); return beatlist; } } diff --git a/src/game/RhythmState.java b/src/game/RhythmState.java index 11b86f4..787dcf3 100644 --- a/src/game/RhythmState.java +++ b/src/game/RhythmState.java @@ -17,7 +17,7 @@ public class RhythmState extends DefaultGameState{ - public final float CIRCLE_TIME = 500f; //time it takes for circles to shrink. the time the circles are on screen is double this, as the circles grow, then shrink. + public final float CIRCLE_TIME = 600f; //time it takes for circles to shrink. the time the circles are on screen is double this, as the circles grow, then shrink. private float circleTime = -1f; //changing this doesn't do anything. must be negative, however private Image background; //background image @@ -117,6 +117,7 @@ public void mousePressed(int button, int x, int y) { if ((!hitobject.clicked)&&((new Vector2f(x, y).distance(new Vector2f(hitobject.x, hitobject.y))) < hitobject.radius)) { //checks if current circle has already been clicked, then checks if click is within the circle hitobjects.set(hitobjects.indexOf(hitobject), new HitObject(hitobject.x, hitobject.y, hitobject.radius, hitobject.duration, true)); points++; + break; } } } From 91d80b431e700ac2ac6989065340d06878021bd5 Mon Sep 17 00:00:00 2001 From: Althoumb Date: Sat, 31 Mar 2018 09:52:32 -0700 Subject: [PATCH 3/4] literally an osu clone now --- src/game/BeatmapParser.java | 1 + src/game/RhythmState.java | 39 ++++++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/game/BeatmapParser.java b/src/game/BeatmapParser.java index 2988151..a6165ec 100644 --- a/src/game/BeatmapParser.java +++ b/src/game/BeatmapParser.java @@ -8,6 +8,7 @@ public LinkedList parseBeatmap() { LinkedList beatlist = new LinkedList(); beatlist.add(new Beat(250, 250, 1000)); beatlist.add(new Beat(300, 300, 1250)); + beatlist.add(new Beat(150, 350, 1500)); return beatlist; } } diff --git a/src/game/RhythmState.java b/src/game/RhythmState.java index 787dcf3..fedee21 100644 --- a/src/game/RhythmState.java +++ b/src/game/RhythmState.java @@ -17,16 +17,15 @@ public class RhythmState extends DefaultGameState{ - public final float CIRCLE_TIME = 600f; //time it takes for circles to shrink. the time the circles are on screen is double this, as the circles grow, then shrink. + public final float CIRCLE_TIME = 600f; //time it takes for approach circles to shrink. + public final float LENIENCE_TIME = 200f; //lenience, in ms, given to the player to click the hit object - private float circleTime = -1f; //changing this doesn't do anything. must be negative, however private Image background; //background image - private float timescale = .10f; //rate at which circles shrink. doesn't change the time circles are on screen, however + private float timescale = .08f; //rate at which approach circles shrink. doesn't change the time circles are on screen, however private int points = 0; //total number of points - private boolean clicked = false; //whether or not the current circle has been clicked - private boolean nextcircleclicked = false; //whether or not the next circle has been clicked - private float maxRadius = CIRCLE_TIME * timescale; //maximum radius of the circles + private float maxRadius = CIRCLE_TIME * timescale; //maximum radius of the approach circles + private float innerRadius = 20f; //radius of the inner hit circles which you click private long starttime = System.currentTimeMillis(); //initial time private long songtime = 0; //time since beginning of the song, in ms @@ -64,13 +63,21 @@ public void leave(GameContainer arg0, StateBasedGame arg1) throws SlickException public void render(GameContainer gc, StateBasedGame arg1, Graphics g) throws SlickException { g.drawImage(background, 0, 0); - for (HitObject hitobject:hitobjects) { - if (hitobject.clicked) { + for (HitObject hitobject:hitobjects) { //this for loop draws all the hit objects + int index = hitobjects.indexOf(hitobject); + if (index != hitobjects.size() - 1) { //check to make sure hitobject is not the last in the list + g.setLineWidth(5f); + g.setColor(Color.white); + g.draw(new Line(new Vector2f(hitobject.x, hitobject.y), new Vector2f(hitobjects.get(index+1).x, hitobjects.get(index+1).y))); //draws a line in between consecutive hit objects + } + if (hitobject.clicked) { //changes color based on the state of the circle g.setColor(Color.green); } else { g.setColor(Color.white); } - g.fill(new Circle(hitobject.x, hitobject.y, hitobject.radius)); + g.setLineWidth(2f); + g.draw(new Circle(hitobject.x, hitobject.y, hitobject.radius+innerRadius)); //draws approach circle + g.fill(new Circle(hitobject.x, hitobject.y, innerRadius)); //draws hit circle } g.setColor(Color.white); @@ -82,7 +89,7 @@ public void render(GameContainer gc, StateBasedGame arg1, Graphics g) throws Sli public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException { songtime += delta; - if ((beatmapindex<=beatmap.size()-1)&&(beatmap.get(beatmapindex).time<=songtime)) { + if ((beatmapindex<=beatmap.size()-1)&&(beatmap.get(beatmapindex).time<=CIRCLE_TIME+songtime)) { //puts objects in the beatmap into the hitobjects list when needed Beat currentbeat = beatmap.get(beatmapindex); HitObject hitobject = new HitObject(currentbeat.x, currentbeat.y, maxRadius, CIRCLE_TIME, false); hitobjects.add(hitobject); @@ -92,8 +99,8 @@ public void update(GameContainer gc, StateBasedGame sbg, int delta) throws Slick if (!hitobjects.isEmpty()) { for (HitObject hitobject:hitobjects) { int index = hitobjects.indexOf(hitobject); - hitobjects.set(index, new HitObject(hitobject.x, hitobject.y, maxRadius * (hitobject.duration / CIRCLE_TIME), hitobject.duration - delta, hitobject.clicked)); - if (hitobject.duration<=0) { + hitobjects.set(index, new HitObject(hitobject.x, hitobject.y, maxRadius * (hitobject.duration / CIRCLE_TIME), hitobject.duration - delta, hitobject.clicked)); //shrinks approach circle and reduces remaining time on screen + if ((hitobject.duration<=-LENIENCE_TIME)||((hitobject.clicked)&&(hitobject.duration<=0))) { //removes hit circle if time left is less than or equal to the lenience time, or the circle has already been clicked and it's time left is less than zero hitobjects.remove(index); } } @@ -114,10 +121,10 @@ public boolean isAcceptingInput() { public void mousePressed(int button, int x, int y) { if (button == Input.MOUSE_LEFT_BUTTON) { for (HitObject hitobject:hitobjects) { - if ((!hitobject.clicked)&&((new Vector2f(x, y).distance(new Vector2f(hitobject.x, hitobject.y))) < hitobject.radius)) { //checks if current circle has already been clicked, then checks if click is within the circle - hitobjects.set(hitobjects.indexOf(hitobject), new HitObject(hitobject.x, hitobject.y, hitobject.radius, hitobject.duration, true)); - points++; - break; + if ((!hitobject.clicked)&&((new Vector2f(x, y).distance(new Vector2f(hitobject.x, hitobject.y))) < innerRadius)) { //checks if current circle has already been clicked, then checks if click is within the circle + hitobjects.set(hitobjects.indexOf(hitobject), new HitObject(hitobject.x, hitobject.y, hitobject.radius, hitobject.duration, true)); //changes hitobject click state + points++; //increments points + break; //breaks out of for loop so that only one hit object is clicked at once } } } From 43a92858df2c572843e6dc3008875c89c86ca0eb Mon Sep 17 00:00:00 2001 From: Althoumb Date: Sat, 31 Mar 2018 10:11:30 -0700 Subject: [PATCH 4/4] sample beatmap for reference --- src/game/BeatmapParser.java | 19 ++++++++++++++++--- src/game/RhythmState.java | 7 +------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/game/BeatmapParser.java b/src/game/BeatmapParser.java index a6165ec..3ebf5a8 100644 --- a/src/game/BeatmapParser.java +++ b/src/game/BeatmapParser.java @@ -6,9 +6,22 @@ public class BeatmapParser { public LinkedList parseBeatmap() { LinkedList beatlist = new LinkedList(); - beatlist.add(new Beat(250, 250, 1000)); - beatlist.add(new Beat(300, 300, 1250)); - beatlist.add(new Beat(150, 350, 1500)); + beatlist.add(new Beat(100,78,854)); + beatlist.add(new Beat(42,276,3132)); + beatlist.add(new Beat(235,324,3891)); + beatlist.add(new Beat(465,104,5410)); + beatlist.add(new Beat(256,192,5790)); + beatlist.add(new Beat(263,259,9398)); + beatlist.add(new Beat(476,172,10347)); + beatlist.add(new Beat(359,44,11106)); + beatlist.add(new Beat(9,68,12436)); + beatlist.add(new Beat(41,308,14144)); + beatlist.add(new Beat(367,364,15474)); + beatlist.add(new Beat(384,114,16423)); + beatlist.add(new Beat(312,43,16803)); + beatlist.add(new Beat(225,92,17182)); + beatlist.add(new Beat(67,248,18701)); + beatlist.add(new Beat(141,314,19081)); return beatlist; } } diff --git a/src/game/RhythmState.java b/src/game/RhythmState.java index fedee21..13ce604 100644 --- a/src/game/RhythmState.java +++ b/src/game/RhythmState.java @@ -1,6 +1,5 @@ package game; -import java.util.ArrayList; import java.util.LinkedList; import java.util.concurrent.CopyOnWriteArrayList; @@ -17,7 +16,7 @@ public class RhythmState extends DefaultGameState{ - public final float CIRCLE_TIME = 600f; //time it takes for approach circles to shrink. + public final float CIRCLE_TIME = 1000f; //time it takes for approach circles to shrink. public final float LENIENCE_TIME = 200f; //lenience, in ms, given to the player to click the hit object private Image background; //background image @@ -108,10 +107,6 @@ public void update(GameContainer gc, StateBasedGame sbg, int delta) throws Slick } - private double randomRange(float lower, float upper) { - return Math.random() * (upper - lower) + lower; - } - @Override public boolean isAcceptingInput() { return true;