1

I have this game where the goal is to pass a ball through oncoming rectangles with gaps in them. Point gains are registered when the ball's x position falls within a certain margin (plus or minus) from the right of the rectangle being passed by the ball. Since the timer I set up cycles every 10 milliseconds there arises a problem because sometimes it works fine; a point is added per rectangle passed, yet other times it doesn't even register a rectangle pass or it registers the ball's x value falling in the margin twice giving the user two points for one rectangle pass. Here is part of the code for this mechanism:

/*********Keeps track of score and update TextView with score************/
            if (rectWhite1.getTheRight() > Math.round(mBallPos.x) - 5 && rectWhite1.getTheRight() < Math.round(mBallPos.x) + 5) {

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mScoreboard.addPoint();
                        scoreTextView.setText(mScoreboard.getScore() + "");
                    }
                });

            }

            if (rectWhite2.getTheRight() > Math.round(mBallPos.x) - mPointMargin && rectWhite2.getTheRight() < Math.round(mBallPos.x) + mPointMargin) {

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mScoreboard.addPoint();
                        scoreTextView.setText(mScoreboard.getScore() + "");
                    }
                });


            }

            if (rectWhite3.getTheRight() > Math.round(mBallPos.x) - mPointMargin && rectWhite3.getTheRight() < Math.round(mBallPos.x) + mPointMargin) {

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mScoreboard.addPoint();
                        scoreTextView.setText(mScoreboard.getScore() + "");
                    }
                });

            }

mPointMargin is the amount in pixels set as a member variable and it dynamically changes as the game gets faster so the game registers passes accurately. Even with me accounting for this bug it still slips up and has the occasional missed point or double point for one pass. Is there anyway to do this in a more accurate way that will add the score once and kill that if statement until it is needed again or is there another way to solve this problem?

Edit: I took a crack at the code for this mechanism and it went very badly. I expected the points to start adding once the ball was to the right of the rectangle's right x value. But, what happens is that the points continue adding in the hundreds once the rectangle is passed. Here is the boolean method I plugged in:

public boolean polylineIntersects(BallView ball, int right) {
    boolean intersects = false;
    int ballPos1 = 0;
    int ballPos2 = 0;

    if (ball.getX() < right) {
        ballPos1 = Math.round(ball.getX());
    }
    else if (ball.getX() > right) {
        ballPos2 = Math.round(ball.getX());
    }

    if (ballPos2 > ballPos1) {
        intersects = true;
    }
    else {
        intersects = false;
    }


    return intersects;
}

Is there anyway this can work without me employing actually drawing a polyline because I couldn't find a method that detects intersecting Polyline (Android Developers) with a specified area.

Edit 2: Ok so I tried the class and it works... that is it gets crossings but it goes crazy adding score when that check returns true meaning while check stays true those if statements are cycling going crazy until it is false. Here is how I implemented the class you gave me.

     /*Declared Tracker objects in OnCreate*/

//What is below is in OnResume

mTmr3 = new Timer();
    mTsk3 = new TimerTask() {
        @Override
        public void run() {


            mTracker.setGateX(rectWhite1.getTheRight());
            mTracker2.setGateX(rectWhite2.getTheRight());
            mTracker3.setGateX(rectWhite3.getTheRight());

            //*********Keeps track of score and update TextView with score*************
            if (mTracker.check(mBallView)) {
                mediaPlayer1.start();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mScoreboard.addPoint();
                        scoreTextView.setText(mScoreboard.getScore() + "");

                    }
                });

            }

            if (mTracker2.check(mBallView)) {
                mediaPlayer1.start();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mScoreboard.addPoint();
                        scoreTextView.setText(mScoreboard.getScore() + "");

                    }
                });

            }

            if (mTracker3.check(mBallView)) {
                mediaPlayer1.start();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mScoreboard.addPoint();
                        scoreTextView.setText(mScoreboard.getScore() + "");

                    }
                });

            }
        }
    };
    mTmr3.schedule(mTsk3, 10, 10);

I tweaked the class Tracker to this:

public class Tracker {
private int lastX = Integer.MAX_VALUE;
private int gateX;


public void setGateX(int gateX) {
    this.gateX = gateX;
}

public boolean check(BallView ball) {
    if (lastX == Integer.MAX_VALUE) {
        lastX = Math.round(ball.getX());
    }
    int currX = Math.round(ball.getX());
    return currX == gateX || lastX - gateX < 0 && currX - gateX > 0
            || lastX - gateX > 0 && currX - gateX < 0  ;
}
}
Benyam Ephrem
  • 448
  • 6
  • 20
  • I don't know how rectangles and ball move. But an accurate, cycle-independ method would be to compute the intersection of the point's path with the rectangles. This way, the algo uses the entire "history", not just a "snapshot". - The required algorithm might even be in your java.awt (not sure what android has). – laune Mar 01 '15 at 06:07
  • @laune So you are suggesting me to move that code out of the timer and use a different algorithm? How would I do that specifically because I don't see how you can't use a timer since my game moves very fast and to get an instance of the ball passing I'd need to use a fast sampling timer. I have to update a TextView with the score constantly so I have to take "snapshots" to keep that score fresh. – Benyam Ephrem Mar 01 '15 at 15:09
  • No, the code should remain in the timer, but I do suggest a different algorithm. Conceptually, the ball describes a curve (polyline) and a "hit" happens whenever this polyline intersects some Area (rectangle?). So, at t0 the ball is outside, below the rectangle, at tn it is above the rectangle. Then the polyline is ball at t0-t1-...tn and if it intersects the rectangle you have one "hit". You'll still need sampling at high speed for the ball path, but you may miss the ball actually being in the rectangle. – laune Mar 01 '15 at 15:17
  • @laune But using that method you can still miss ball hits as you said and I don't want that since my users will likely believe my app isn't very quality since it can't even keep score accurately. I've tried what you said at high sampling rates and it still misses ball hits sometimes. Should I just accept this bug as something unfixable or keep searching for an answer? I mean the best I can do and have tried is to change the mPointMargin varible to expand the point zone as rectangles and the ball move faster to not miss a point. I rather a player get two points than none... – Benyam Ephrem Mar 01 '15 at 15:43
  • No, it will not miss ball hits - I never said that. You will *compute* ball hits for sure. – laune Mar 01 '15 at 15:44
  • If you tried my algorithm and the result isn't correct: check the algorithm. How did you do it? – laune Mar 01 '15 at 15:45
  • @laune The way it works is not rectangle intersection it adds a point if the ball's x value falls within the right side of the rectangle plus and minus the point margin. I can't do it where it detects rectangle collision. I'm kinda confused by what you mean with the poly line concept. Could you explain it further? Thank you for the help!!! – Benyam Ephrem Mar 01 '15 at 15:49
  • But the "within the right side of the rectangle plus and minus the point margin" must be some geometric shape. What is it? - I'll gladly explain in more detail, but I need to know how the rectangle moves and how the ball moves. – laune Mar 01 '15 at 15:52
  • I have abused an answer to show what I mean. – laune Mar 01 '15 at 15:59
  • @laune I added my falied attempt at this "history" tracking mechanism, except I didn't draw a poly line. I am trying to sample a point when the ball is left of the rectangle and sample a point when it is to the right of the rectangle. Then compare those values to determine if an intersect happened. I'm only using X values since Y values are irrelevant here. What can I do to make this code work so that it returns true once and stops once the ball passes? – Benyam Ephrem Mar 06 '15 at 21:57
  • @BenyaemEphrem Will have to look at it tomorrow. Too late. – laune Mar 06 '15 at 22:46
  • This method ('polylineIntersects`) is useless: it is called with a certain object BallView containing just one (1) value for the abscissa (x). So what do you expect from that? - Also, I don't understand why ordinate (y) values shouldn't matter. As far as I have understood, the ball is supposed to pass between to points (x1,y1) and (x2,y2) with x1==x2. - Earlier you wrote about "oncoming rectangles", and that I don't see at all in method `polylineIntersects`. – laune Mar 07 '15 at 13:37
  • If the android support library doesn't have Shape and its implementations according to JDK: it is not difficult to write this for your special case. But I don't know the necessary details... – laune Mar 07 '15 at 14:18
  • @laune I can give you any details you need, I just have trouble with this. Sorry I replied late, I was working on my app's design the past week. Literally all I have left is to fix this scoring mechanism. So anything you want to know I can tell you. I don't have enough reputation to open a private chat. – Benyam Ephrem Mar 08 '15 at 02:42
  • @laune The thing is I have other code mechanisms that handle the y values (like when player hits the sides of the screen or misses a gap, hitting a rectangle) and kill the player when they are not within proper bounds (you don't need to worry about how those are implemented). So that is not a problem. We only need to track X values to keep track of score. If a player dies they wont be there to track X values which is what matters when it come to score. – Benyam Ephrem Mar 08 '15 at 02:56
  • I have added a class with a method that checks whether two successive positions are east and west of the gap. It appears to me now, that the problem is much simpler than I originally thought: you just have to check whether the ball passes the line across the gap (like a soccer goal line). All this "rectangle"/"area" and "polyline" stuff isn't really applicable. Is there anything I'm missing? – laune Mar 08 '15 at 06:56
  • @laune I edited my question with my implementation of the class. If I increase the timer speed it goes even more crazy with score. If I decrease the timer speed (to 1000 milliseconds) it will widen the window of recording last and current x values and have a weird laggy effect that adds score when a player is well past the pipe that was passed (it records score accurately). I have no idea what to do from here...I have no clue why it returns true for so long...I might have no choice but to use the previous method for calculating passes. – Benyam Ephrem Mar 09 '15 at 03:34
  • I kind of don't understand how you are getting a "history" here...like how is the `lastX` being correctly updated? Why is `lastX` set to `Integer.MAX_VALUE`...like after the first true statement it will never be true again, ceasing the lastX from being recorded I think...... – Benyam Ephrem Mar 09 '15 at 03:54

1 Answers1

3

I need an answer so I can format using monospaced. Does the rectangle have an exit that must be met by the ball - so it passes through?

--------------
  +--+
  |  | o

  |  |
  +--+    t0
--------------
  +--+
  |  |  

o |  |
  +--+       t1
--------------

The ball path is the polyline connecting the o's. (Here it is just a straight line.)

  +--+
  |  | o

o |  |
  +--+

which intersects the "good" shape. This computation can be done at t2, when the ball has passed the shape. At t0 and t1, only the ball position must be saved, and a quick check whether the ball is still below the shape's upper bound.

So the "good" shape is the one that connect both gap's "corner poles", marked by GGGG.

  +--+
  |  | o
  GGGG
o |  |
  +--+

Later According to polylineIntersects, a BallView lets you do getX.

public class Tracker {
    private int lastX = Integer.MAX_VALUE;
    private int gateX;
    public Tracker( int gateX ){
        this.gateX = gateX;
    }
    public boolean check( BallView ball ){
        if( lastX == Integer.MAX_VALUE ){
            lastX = ball.getX();
            return;
        }
        int currX = ball.getX();
        return currX == gateX ||
               lastX - gateX > 0 && currX - gateX < 0 ||
               lastX - gateX < 0 && currX - gateX > 0;
    }

An object of class Tracker needs to be created for each rectangle/gap ahead. Then method check must be called for each new BallView. Once it returns true, score +1 and discard the Tracker object (or add a reset that reestablishes the original state).

laune
  • 31,114
  • 3
  • 29
  • 42
  • The way the game works is the ball passes through gaps in rectangles approaching from the right (shifting left). So would t0 be from the left, t1 in the rectangles gap, and t2 on the other side (the right past the rectangle)? – Benyam Ephrem Mar 01 '15 at 16:37
  • Are the dotted lines the gap's entry and the solid line the rectangles border? In that case that t1 diagram is a player death since it touches the rectangle. – Benyam Ephrem Mar 01 '15 at 16:40
  • Ah, we are getting closer. – laune Mar 01 '15 at 16:59
  • So you want me to find whether a ploy line of the ball's path overlaps the good shape (which is the gap)? If my timer samples every 10 milliseconds then wouldn't I get multiple true statements for that or do you mean calculate the amount of poly lines overlapping the right of the rectangle? – Benyam Ephrem Mar 01 '15 at 17:28
  • I propose to calculate as soon as the ball is to the left of the rectangle. You must have collected some ball positions t0, t1,...tn - they define the polyline. Now: if this intersects GGGGG, one point is scored. If it intersects either of the two areas in the rectangle that are not GGGGG, the player's ball is dead. – laune Mar 01 '15 at 17:32
  • In other words: using discrete (i.e. every 10ms) views that aren't reliable, compute a continuous scenario. (If the granularity of ball movements is smaller than 10ms, it might still make some "kinks" you don't see when interpolating every 10ms. But that can't be helped.) – laune Mar 01 '15 at 17:35
  • What if 2 poly lines are detected intersecting in the gap? Wouldn't that add a lot of points. How can you be sure to get the sampling rate to the point that only one poly line is ever detected per pass/intersecting? – Benyam Ephrem Mar 01 '15 at 17:42
  • There is only one ball, I think? A polyline is a sequence of point-to-point lines, P1 to P2 to ... to Pn. There are no "2 poly lines" - the ball has one path. – laune Mar 01 '15 at 17:48
  • Ok, I will try my best to apply this concept to my app. Thank you so much for taking your time to help me! – Benyam Ephrem Mar 01 '15 at 17:52
  • Let me know how you are doing! – laune Mar 01 '15 at 17:52
  • Thanks for all the help!!! I ended up using a different mechanism that involves incrementing a number whenever the ball passes the gap and only adding a point when the number equals 1 (it is inatialized at 0) and this fixes the crazy point adding. Then when the ball hits open space (touches no rectangle) the number resets to 0 and waits for the next pass. This works great and no passes are ever missed. I am really grateful for you taking so much of your time to help me before. – Benyam Ephrem Mar 14 '15 at 16:24
  • Sounds very much like the procedure I've come to recommend at the end, after all this "area" and "polyline" could be discarded. – laune Mar 14 '15 at 17:51