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:
//if the ball is moving horizontally, move it
if(horizontalDirection != Direction.NONE) {
//determine its horizontal velocity
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);
} //end if there were germ to ball collisions
} //end if the ball is moving horizontally
//if the ball is moving vertically, move it
if(verticalDirection != Direction.NONE) {
//determine its vertical velocity
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);
} //end if there were germ to ball collisions
} //end if the ball is moving vertically
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:
/**Corrects the specified ball's direction.
* @param ball the ball Sprite
* @param shouldChangeHorizontally whether to change the ball's direction horizontally
* @param correctionDirection the direction to move the ball in for the correction*/
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);
} //end changeBallDirection
/**Corrects a collision where the germ collided with the ball.
* @param ball the ball colliding
* @param germ the germ that was collided with
* @param horizontalGermMovementDirection the Direction the germ moved in horizontally
* @param didGermMoveDown whether the germ moved down*/
private static void correctGermToBallCollision(final Sprite ball,
final Sprite germ, final Direction horizontalGermMovementDirection,
final boolean didGermMoveDown)
{
//determine how to fix ball Sprite's coordinates based upon how the germ is moving
boolean shouldFixHorizontally = true;
if(didGermMoveDown) {
//get the intersection between the 2 Sprites
Rectangle ballGermIntersection = ball.getIntersection(germ);
//fix the smallest distance possible
shouldFixHorizontally = ballGermIntersection.width < ballGermIntersection.height;
}
//correct the ball's position
Direction newDirection = (shouldFixHorizontally ? horizontalGermMovementDirection :
Direction.POSITIVE);
correctBallGermCollisionHelper(ball, germ, shouldFixHorizontally, newDirection);
} //end correctGermToBallCollision
And here’s the helper method:
/**Corrects the position of the ball after a collision with a germ.
* @param ball the ball Sprite
* @param germ the germ Sprite
* @param shouldFixHorizontally whether to fix the collision horizontally
* @param correctionDirection the direction to move the ball in for the correction
* @return the amount of the correction. This amount is always positive, giving no
* information about direction.*/
private static double correctBallGermCollisionHelper(final Sprite ball,
final Sprite germ, final boolean shouldFixHorizontally,
final Direction correctionDirection)
{
double correction = 0.0;
//determine the position to move to horizontally
if(shouldFixHorizontally) {
double xNew = ball.getX();
switch(correctionDirection) {
case NEGATIVE:
//shove the germ left
if(ball.getX2() >= germ.getX())
xNew = germ.getX() - ball.getWidth();
break;
case POSITIVE:
//shove the germ right
if(ball.getX() <= germ.getX2())
xNew = germ.getX2() + 1;
break;
//ignore the approaching vertically case
} //end switch movement direction
correction = xNew - ball.getX();
ball.setPosition(xNew, ball.getY());
} else { //else determine the position to move to vertically
double yNew = ball.getY();
switch(correctionDirection) {
case NEGATIVE:
//shove the germ up
if(ball.getY2() >= germ.getY())
yNew = germ.getY() - ball.getHeight();
break;
case POSITIVE:
//shove the germ down
if(ball.getY() <= germ.getY2())
yNew = germ.getY2() + 1;
break;
//ignore the approaching horizontally case
} //end switch movement direction
correction = yNew - ball.getY();
ball.setPosition(ball.getX(), yNew);
} //end else determine the position to move to vertically
return Math.abs(correction);
} //end correctBallGermCollisionHelper
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.