Pong Collision Question [XNA]

Started by
6 comments, last by moneal2001 11 years ago

I am working on collision for a pong clone i am working on. I set up a rectangle for the ball and the paddles and when those intersect, I want to invert the X or Y direction the ball is moving based on the area of the ball that collides with the paddle.

Examples. bottom of ball hits the top of the paddle invert the Y direction of motion, or right side of ball hits the left side of paddle invert X direction of motion.

So after the collision call happens I created 4 rectangles representing the ball 1 for each 4th of the ball: top left, top right, bottom left, and bottom right. and set up if statements to check for intersection between those and the paddle rectangle.

here is the collision code i have


        private void checkPaddleCollision(List<Paddle> paddles)
        {
            CollisionType type = CollisionType.Left;
            bool collide = false;
            Paddle paddle = new Paddle();
            
                       

            foreach (Paddle p in paddles)
            {
                

                if(rect.Intersects(p.Bounds))
                {

                    paddle = p;

                    collide = true;

                    Rectangle topLeft, topRight, bottomLeft, bottomRight;
                    int height = rect.Height / 2;
                    int width = rect.Width / 2;

                    topLeft = new Rectangle(rect.X, rect.Y, width, height);
                    topRight = new Rectangle(rect.X + width, rect.Y, width, height);
                    bottomLeft = new Rectangle(rect.X, rect.Y + height, width, height);
                    bottomRight = new Rectangle(rect.X + width, rect.Y + height, width, height);

                    if((p.Bounds.Intersects(topLeft) || p.Bounds.Intersects(topRight)) &&(!p.Bounds.Intersects(bottomLeft) || !p.Bounds.Intersects(bottomRight)))
                    {
                        type = CollisionType.Top;
                    }
                    else if ((p.Bounds.Intersects(bottomLeft) || p.Bounds.Intersects(bottomRight)) && (!p.Bounds.Intersects(topLeft) || !p.Bounds.Intersects(topRight)))
                    {
                        type = CollisionType.Bottom;
                    }
                    else if ((p.Bounds.Intersects(bottomLeft) || p.Bounds.Intersects(topLeft)) && (!p.Bounds.Intersects(topRight) || !p.Bounds.Intersects(bottomRight)))
                    {
                        type = CollisionType.Left;
                    }
                    else if ((p.Bounds.Intersects(bottomRight) || p.Bounds.Intersects(topRight)) && (!p.Bounds.Intersects(topLeft) || !p.Bounds.Intersects(bottomLeft)))
                    {
                        type = CollisionType.Right;
                    }
                                    
                }
            }

            if (collide)
            {
                switch (type)
                {
                    case CollisionType.Left:
                        position.X = paddle.Position.X + paddle.Bounds.Width;
                        break;
                    case CollisionType.Right:
                        position.X = paddle.Position.X - texture.Width;
                        break;
                    case CollisionType.Bottom:
                        position.Y = paddle.Position.Y + texture.Height;
                        break;
                    case CollisionType.Top:
                        position.Y = paddle.Position.Y + paddle.Bounds.Height;
                        break;
                }

                if (type == CollisionType.Left || type == CollisionType.Right)
                {
                    direction.X *= -1;
                    direction.Normalize();
                }
                else
                {
                    direction.Y *= -1;
                    direction.Normalize();
                }
            }
        }

The collision works fine when the ball hits the top or bottom of a paddle but it is not regestering correctly when it hits the left or right of a paddle. It sees it as either top or bottom until the ball is halfway through the paddle then it regesters. can anyone point me in the right direction. Thanks.

Advertisement

I'm still trying to dissect the code, but here are a few initial observations:

  1. You create a Paddle object at the beginning of the method and, initialise it with 'new'. However, after this you don't make reference to that new allocated object. When you reach the for-loop you re-init with each paddle in your list, and never access it in the 'if-collides', unless you detect a collision (even then it will be the paddle from the collision for-loop). Is there a reason for this, as in, is there more of the method not being shown?
  2. At a glance, it looks like you might be having issues with your if-else-if that checks the collision of each quadrant. You shouldn't need to make a check if collisions haven't occured: ((p.Bounds.Intersects(topLeft) || p.Bounds.Intersects(topRight)) &&(!p.Bounds.Intersects(bottomLeft) || !p.Bounds.Intersects(bottomRight))) as this would defeat the point of the if-else only checking certain corners colliding?
  3. Where does rect first get created? Also, you initialise rect width and height to width/2 and height/2. Then you create 4 rectangles using the coordinates and these dimensions. But, because you have defined width/height as half their initial values, what happens when you create say 'topright' using the X coordinate, adding on this width? The rectangle created won't be at the size you expect. Unless the X-value is centered, but then that means topleft is incorrect.

Run through your debugger and see what values you get for each rectangle when you create them, and get back to us here. I suspect that the rects you are creating are smaller in size than what you think they are.

I hope this helps, and I look forward to your response,

Stitchs.

I'm still trying to dissect the code, but here are a few initial observations:

  1. You create a Paddle object at the beginning of the method and, initialise it with 'new'. However, after this you don't make reference to that new allocated object. When you reach the for-loop you re-init with each paddle in your list, and never access it in the 'if-collides', unless you detect a collision (even then it will be the paddle from the collision for-loop). Is there a reason for this, as in, is there more of the method not being shown?
  2. At a glance, it looks like you might be having issues with your if-else-if that checks the collision of each quadrant. You shouldn't need to make a check if collisions haven't occured: ((p.Bounds.Intersects(topLeft) || p.Bounds.Intersects(topRight)) &&(!p.Bounds.Intersects(bottomLeft) || !p.Bounds.Intersects(bottomRight))) as this would defeat the point of the if-else only checking certain corners colliding?
  3. Where does rect first get created? Also, you initialise rect width and height to width/2 and height/2. Then you create 4 rectangles using the coordinates and these dimensions. But, because you have defined width/height as half their initial values, what happens when you create say 'topright' using the X coordinate, adding on this width? The rectangle created won't be at the size you expect. Unless the X-value is centered, but then that means topleft is incorrect.

Run through your debugger and see what values you get for each rectangle when you create them, and get back to us here. I suspect that the rects you are creating are smaller in size than what you think they are.

I hope this helps, and I look forward to your response,

Stitchs.

1. the new paddle created at the top is a holder for the paddle that gets collided with. so when collision happens the paddle(p) that gets collided with gets assigned to paddle.


 foreach (Paddle p in paddles)
            {
                

                if(rect.Intersects(p.Bounds))
                {

                    paddle = p; 

then paddle is used to move the ball outside the bounds of the paddle here:


 switch (type)
                {
                    case CollisionType.Left:
                        position.X = paddle.Position.X + paddle.Bounds.Width;
                        break;
                    case CollisionType.Right:
                        position.X = paddle.Position.X - texture.Width;
                        break;
                    case CollisionType.Bottom:
                        position.Y = paddle.Position.Y + texture.Height;
                        break;
                    case CollisionType.Top:
                        position.Y = paddle.Position.Y + paddle.Bounds.Height;
                        break;

2. This is where I am having my issue. I am trying to reduce some of the work for myself i guess. I want it to check for non-collision to reduce the if statements i am writing. take that specific statement; it checks top collision first, then if top collision happens it checks bottom collision and if bottom collision happens that statement should come back false. it should then move to the next check.

3. rect is created and updated as the size and location of my ball. Since my ball rectangle is square, I then use half the width and height to create the 4 corner squares.

so topLeft is at the location of rect with half the height and width, while topRight is located at X + width( the center of rect) and Y(the top of rect), bottomLeft is located at X(the Left side of rect) and Y + height(the center of rect), and finally bottomLeft is X+ width(the center of rect and Y+ height(the center of rect). if that is making since.

any one able help with this or should i just rewrite the collision checks

I am, I'm just still trying to wrap my head around what's going on. I'll start by immediately following up on my first lot of points:

  1. I'm not too familiar with XNA/C# but, do you need to initialise the temp Paddle p object with the 'new' keyword. I only ask because, like you say, you replace it with the object at the current position in the List, as soon as you go into collision checks. If you are doing this every loop, then you are creating more overhead by calling 'new'. (It might not be noticable with 2 paddles and a ball, but if you use the code later, with more objects, it could cause significant slow-down. I'm also unfamiliar with the garbage collection in XNA, and if you specifically need to call a delete on every new.)
  2. Regarding the collision. I believe that the way 'Rectangle' intersect works is that it check for collsions between 2 rectangles by check if x1 < x2+w, y1, < y2+h, x1+w > x2, y1+h > y2, so you are checking 4 corners of these mini-rectangles, which you are using as corners for the square. This links with the way you use your rectangles in combination with each other, for example; you check topLeft OR topRight, and if either of these occur, it is a Top collision however, you also use topRight to check it a Right side collision happens. What happens if the ball comes from the right and hits the topRight square from the Right side first. You would want it to register as a collision with the paddle from the Right edge. But, with the way you have set-up your if-else-if, what is actually going to happen? If you can sort this out, it might have a knock-on effect in fixing the Left and Right problems you are having.
  3. I understand now how you have made these rectangles and the where they are located, with reference to the object they are being created for. I thought you had your coordinates way off to what you thought, but I worked it out. What I will say is that (this links with what I mentioned in point 1) you also create 4 new rectangles each time the collision is checked and, whilst this does not happen until 'rect' intersects a paddle, it's still adds additional overhead with 4 additional calls to 'new'. What you should do here is create the bounding rectangles in each objects constructor, so that they only get created once on start-up, and then you loop through them when you go through each paddle.

I hope this is of use and please ask if you don't understand any of it, I sometimes have a habit of 'waffling'.

Stitchs.

thanks stitches i decided to change how i did the if statements just using all possible combinations instead of combining all the intersection checks like i had it. I do understand the overhead of using new. and i will add the paddle and small rectangles to the constructor and just assign them when they are needed. Thanks again.

here is my new collision code that works in case anyone might need it:


private void checkPaddleCollision(List<Paddle> paddles)
        {
            CollisionType type = CollisionType.Left;
            bool collide = false;
            
                       

            foreach (Paddle p in paddles)
            {
                

                if(rect.Intersects(p.Bounds))
                {

                    paddle = p;

                    collide = true;
                    int height = rect.Height / 2;
                    int width = rect.Width / 2;

                    topLeft.X = rect.X;
                    topLeft.Y = rect.Y;
                    topLeft.Width = width;
                    topLeft.Height = height;
                    
                    topRight.X = rect.X + width;
                    topRight.Y = rect.Y;
                    topRight.Width = width;
                    topRight.Height = height;

                    bottomLeft.X = rect.X;
                    bottomLeft.Y = rect.Y + height;
                    bottomLeft.Width = width;
                    bottomLeft.Height = height;

                    bottomRight.X = rect.X + width;
                    bottomRight.Y = rect.Y + height;
                    bottomRight.Width = width;
                    bottomRight.Height = height;
                    

                    if(p.Bounds.Intersects(topLeft) && p.Bounds.Intersects(bottomLeft))
                    {
                        type = CollisionType.Left;
                    }
                    else if (p.Bounds.Intersects(topRight) && p.Bounds.Intersects(bottomRight))
                    {
                        type = CollisionType.Right;
                    }
                    else if (p.Bounds.Intersects(topRight) && p.Bounds.Intersects(topLeft))
                    {
                        type = CollisionType.Top;
                    }
                    else if (p.Bounds.Intersects(bottomRight) && p.Bounds.Intersects(bottomLeft))
                    {
                        type = CollisionType.Bottom;
                    }
                    else if (p.Bounds.Intersects(topLeft))
                    {
                        type = CollisionType.Top;
                    }
                    else if (p.Bounds.Intersects(topRight))
                    {
                        type = CollisionType.Top;
                    }
                    else if (p.Bounds.Intersects(bottomLeft))
                    {
                        type = CollisionType.Bottom;
                    }
                    else if (p.Bounds.Intersects(bottomRight))
                    {
                        type = CollisionType.Bottom;
                    }                                    
                }
            }

            if (collide)
            {
                switch (type)
                {
                    case CollisionType.Left:
                        position.X = paddle.Position.X + paddle.Bounds.Width;
                        break;
                    case CollisionType.Right:
                        position.X = paddle.Position.X - texture.Width;
                        break;
                    case CollisionType.Bottom:
                        position.Y = paddle.Position.Y - texture.Height;
                        break;
                    case CollisionType.Top:
                        position.Y = paddle.Position.Y + paddle.Bounds.Height;
                        break;
                }

                if (type == CollisionType.Left || type == CollisionType.Right)
                {
                    direction.X *= -1;
                    direction.Normalize();
                }
                else
                {
                    direction.Y *= -1;
                    direction.Normalize();
                }
            }
        }

Glad to hear that it works now. Just out of curiosity, does it work with this section of code omitted:


                    else if (p.Bounds.Intersects(topLeft))
                    {
                        type = CollisionType.Top;
                    }
                    else if (p.Bounds.Intersects(topRight))
                    {
                        type = CollisionType.Top;
                    }
                    else if (p.Bounds.Intersects(bottomLeft))
                    {
                        type = CollisionType.Bottom;
                    }
                    else if (p.Bounds.Intersects(bottomRight))
                    {
                        type = CollisionType.Bottom;
                    }   

It seems like it is adding more bloat to the method than needed. Also, they don't account for left and right, and are treated solely for Top/Bottom. Try omitting it and let me know.

Stitchs.

actually yes it does work fine that way and better than the way i posted. the ball still bounces left or right if hitting at the corner of the paddle where it was going behind the paddle if it hit the corner. Thanks I was really over thinking the problem.

This topic is closed to new replies.

Advertisement