Search code examples
javaandroidgame-physics

Point adding mechanism troubles?


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  ;
}
}

Solution

  • 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).