views:

706

answers:

2

I have made the game, "Breakout". A small fun side-project.

Now, I usually do not make games, so collision-handling is not something I normally think about.

I have a paddle, a ball and some bricks.

For now, when there is a collision (I draw rectangles around each of the objects mentioned), I simply change the Y value of the ball to -Y.

This works fine, EXCEPT if the ball hits a brick from the side (either East or West). The side-effect is not pretty and ruins the gameplay.

I think I can safely assume that instead of the above technique, I need to change the X value to -X when this happens.

So far I have: if (ballRect.IntersectsWith(brickRect))

ballRect and brickRect being rectangles around each object.

Now, what if I created a rectangle around the eastern border of the brick, the western border, etc? I guess the width would be about a pixel.

If collision happens with western or eastern rectangle, then the balls X value should be -X. And vice versa.

What about the corners though? Should I just randomly choose which rectangle to control of x corner?

Or perhaps should I make a rectangle around each corner? the rectangle being 1*1 in side. If there is a collision => -x AND -y values of the ball?

Please share your thoughts.

Here is the process so far:

    foreach (var brick in Bricks)
    {
        if (brick.IsAlive)
        {
            var brickRect = new Rectangle(brick.X, brick.Y, BrickWidth, BrickHeight);
            if (ballRect.IntersectsWith(brickRect)) //Ball has hit brick. lets find out which side of the brick
            {
                var brickRectNorth = new Rectangle(brick.X, brick.Y + BrickHeight, BrickWidth, 1);
                var brickRectSouth = new Rectangle(brick.X, brick.Y, BrickWidth, 1);

                var brickRectEast = new Rectangle(brick.X, brick.Y, 1, BrickHeight);
                var brickRectWest = new Rectangle(brick.X + BrickWidth, brick.Y, 1, BrickHeight);

                if (ballRect.IntersectsWith(brickRectNorth) || ballRect.IntersectsWith(brickRectSouth))
                {
                    //STUFF that makes ball.y = -ball.y
                }
                if (ballRect.IntersectsWith(brickRectWest) || ballRect.IntersectsWith(brickRectEast))
                {
                    //STUFF that makes ball.x = -ball.x
                }
            }
        }
    }
A: 

Sounds OK to me. I would try it out, see what happens, and go from there.

fbrereto
+2  A: 

Rather than looking for rectangle intersections, I'd intersect the actual edges. At the corner, your ball is touching two edges simultaneously, so its motion vector should be affected by both.

I would keep the single rectangle for collision detection, since that reduces the number of rectangles you need to test in your outer loop, but then once a collision with a brick has been detected, go into an inner loop to detect which edge it was that was hit. If you just test each edge and adjust the vector accordingly for each one, the corner will come for free (as long as you don't break out of the loop when you find the first intersecting edge).

Edit: In response to your updated question:

Actually, this is how I would do it (given your code, this appears to be C# 3.0, so that's what I've assumed below):

foreach(var brick in Bricks) {
    if(brick.IsAlive) {
        var brickRect = new Rectangle(brick.X, brick.Y, BrickWidth, BrickHeight);
        if(ballRect.IntersectsWith(brickRect)) {
            // Ball has hit brick.  Now let's adjust the ball's vector accordingly

            // Convenience variables.  Compiler will probably inline.
            var brickLeft = brick.X;
            var brickRight = brick.X + BrickWidth;
            var brickTop = brick.Y;
            var brickBottom = brick.Y + BrickHeight;

            var ballLeft = ball.X - ball.Radius;
            var ballRight = ball.X + ball.Radius;
            var ballTop = ball.Y - ball.Radius;
            var ballBottom = ball.Y + ball.Radius;

            // Test which vector(s) we need to flip
            bool flipX = (ballRight >= brickLeft || ballLeft <= brickRight);
            bool flipY = (ballTop >= brickBottom || ballBottom <= brickTop);

            // Flip the vectors (there are probably ways to optimize this,
            // too, but without seeing your code I can't tell).
            if(flipY) {
                // Stuff that makes ball.y = -ball.y
            }

            if(flipX) {
                // Stuff that makes ball.x = -ball.x
            }
        }
    }
}

Basically, the point is that since you already know the ball actually intersects the brick, you can simplify to a simple box test, which is much faster. Also, there's no need to create extra rectangles for the edges -- just use the edges of the rectangle you already have.

Daniel Pryden
Thanks for the feedback. I hadn't thought of that
CasperT
I have updated my post. Is this what you meant?
CasperT
Hi. The Ball is a rectangle (although drawn as an eclipse). So radius is:int ballRadius = BallSize/2;About the //stuff... I basicly just do a lot of things, such as giving the player som points, killing the brick and so on. In the end, it literally writes:BallDirection = new Point(-BallDirection.X, BallDirection.Y); //x = -xYour code is giving me some very odd results, but I will look into it some more
CasperT