I'm programming a game that's a cross between Arkanoid and Space Invaders. You have a bat which you use to hit a ball. The ball destroys bricks, except that in this case the bricks are germs. The germs move all the way left (or right) and then switch direction. Whenever they're in the middle, they drop down a little bit.
The problem is when the ball hits the germs. Sometimes, the ball shifts farther away than it should when it bounces off. This is better than the bug I used to have (the ball going right through the germs - which only occurs very rarely now) but still annoying.
In Developing Games in Java, it explains how to slide a sprite off of a tile. I basically did the same thing except sliding a sprite (the ball) of another sprite (a germ). I've tried several other methods, but all caused the ball to go right through the germs at times.
Although my code is excessively complicated, I see no way to obtain help but to post it here. Here's my code for moving the balls:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| if(horizontalDirection != Direction.NONE) { double xVelocity = ((horizontalDirection == Direction.NEGATIVE) ? -1 : 1) * horizontalSpeed; double oldX = ball.getX(); double newX = oldX + xVelocity * elapsedSeconds; ball.setPosition(newX, ball.getY());
SelfIterableBag<Sprite> bGermHitByBall = GamePlayGermUtil.getGermsCollidingWithSprite(ball); if(bGermHitByBall.size() > 0) { hitGermsWithBall(ball, bGermHitByBall); Direction newDirection = ballMover.getHorizontalMovementDirection().getReverse(); Sprite germ = bGermHitByBall.getAny(); horizontalCorrection = correctBallGermCollisionHelper(ball, germ, true, newDirection); changeBallDirection(ball, true, newDirection); } } if(verticalDirection != Direction.NONE) { double yVelocity = ((verticalDirection == Direction.NEGATIVE) ? -1 : 1) * verticalSpeed; double oldY = ball.getY(); double newY = oldY + yVelocity * elapsedSeconds; ball.setPosition(ball.getX(), newY);
SelfIterableBag<Sprite> bGermHitByBall = GamePlayGermUtil.getGermsCollidingWithSprite(ball); if(bGermHitByBall.size() > 0) { hitGermsWithBall(ball, bGermHitByBall); Direction newDirection = ballMover.getVerticalMovementDirection().getReverse(); Sprite germ = bGermHitByBall.getAny(); verticalCorrection = correctBallGermCollisionHelper(ball, germ, false, newDirection); changeBallDirection(ball, false, newDirection); } } |
The SelfIterableBag class is my version of the Bag class discussed elsewhere on this forum. It's basically an array. The Direction class is the direction for one axis only - it's either NEGATIVE, NONE, or POSITIVE to specify which direction the Sprite is moving in that axis.
The "getGermsCollidingWithSprite" method is not a problem. Here is the code of the 2 main methods used here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| private static void changeBallDirection(final Sprite ball, final boolean shouldChangeHorizontally, final Direction correctionDirection) { SpriteMover ballMover = ball.getMover(); if(shouldChangeHorizontally) ballMover.setHorizontalMovementDirection(correctionDirection); else ballMover.setVerticalMovementDirection(correctionDirection); }
private static void correctGermToBallCollision(final Sprite ball, final Sprite germ, final Direction horizontalGermMovementDirection, final boolean didGermMoveDown) { boolean shouldFixHorizontally = true; if(didGermMoveDown) { Rectangle ballGermIntersection = ball.getIntersection(germ); shouldFixHorizontally = ballGermIntersection.width < ballGermIntersection.height; } Direction newDirection = (shouldFixHorizontally ? horizontalGermMovementDirection : Direction.POSITIVE); correctBallGermCollisionHelper(ball, germ, shouldFixHorizontally, newDirection); }
|
And here's the helper method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| private static double correctBallGermCollisionHelper(final Sprite ball, final Sprite germ, final boolean shouldFixHorizontally, final Direction correctionDirection) { double correction = 0.0; if(shouldFixHorizontally) { double xNew = ball.getX();
switch(correctionDirection) { case NEGATIVE: if(ball.getX2() >= germ.getX()) xNew = germ.getX() - ball.getWidth(); break; case POSITIVE: if(ball.getX() <= germ.getX2()) xNew = germ.getX2() + 1; break; } correction = xNew - ball.getX(); ball.setPosition(xNew, ball.getY()); } else { double yNew = ball.getY();
switch(correctionDirection) { case NEGATIVE: if(ball.getY2() >= germ.getY()) yNew = germ.getY() - ball.getHeight(); break; case POSITIVE: if(ball.getY() <= germ.getY2()) yNew = germ.getY2() + 1; break; } correction = yNew - ball.getY(); ball.setPosition(ball.getX(), yNew); } return Math.abs(correction); }
|
Minor changes, such as using the horizontal/vertical correction values to only change the ball's direction along one axis, cause the ball to go through the germs again, which is unacceptable. I considered sliding the ball once after the germs move and then again after the ball moves, but that would just make the correction even greater.