3
\$\begingroup\$

I've been adding some extra stuff like Gravity and a table to a simple pong game made with Pygame and Python, the problem is that I don't know how to add collision detection, so when the ball hits the table it changes its direction like when the ball hits the paddle (right now my table is just moving upward and downward).

This is the code:

import pygame
from pygame.locals import *
from sys import exit
import random
import time

pygame.init()

screen=pygame.display.set_mode((640,480),0,32)
pygame.display.set_caption("Pong Pong!")

#Creating 2 bars, a ball and background.
back = pygame.Surface((640,480))
background = back.convert()
background.fill((0,0,0))
bar = pygame.Surface((10,50))
bar1 = bar.convert()
bar1.fill((0,0,255))
bar2 = bar.convert()
bar2.fill((255,0,0))
table = pygame.Surface((10,144))
table1 = table.convert()
table1.fill((255,255,255))
circ_sur = pygame.Surface((15,15))
circ = pygame.draw.circle(circ_sur,(0,255,0),(15//2,15//2),15//2)
circle = circ_sur.convert()
circle.set_colorkey((0,0,0))

# some definitions
bar1_x, bar2_x = 10. , 620.
bar1_y, bar2_y = 215. , 215.
table1_x = 325.
table1_y = 205.
circle_x, circle_y = 307.5, 232.5
bar1_move, bar2_move = 0. , 0.
speed_x, speed_y, speed_circ = 250., 250., 250.
bar1_score, bar2_score = 0,0
gm = 3 #Table Movement
g = 7.5 #Gravity
isDown = True #Table Flag

#clock and font objects
clock = pygame.time.Clock()
font = pygame.font.SysFont("calibri",40)

while True:

    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
        if event.type == KEYDOWN:
            if event.key == K_UP:
                bar1_move = -ai_speed
            elif event.key == K_DOWN:
                bar1_move = ai_speed
        elif event.type == KEYUP:
            if event.key == K_UP:
                bar1_move = 0.
            elif event.key == K_DOWN:
                bar1_move = 0.

    score1 = font.render(str(bar1_score), True,(255,255,255))
    score2 = font.render(str(bar2_score), True,(255,255,255))

    screen.blit(background,(0,0))
    frame = pygame.draw.rect(screen,(255,255,255),Rect((5,5),(630,470)),2)
    middle_line = pygame.draw.aaline(screen,(255,255,255),(330,5),(330,475))
    screen.blit(bar1,(bar1_x,bar1_y))
    screen.blit(bar2,(bar2_x,bar2_y))
    screen.blit(table1,(table1_x, table1_y))
    screen.blit(circle,(circle_x,circle_y))
    screen.blit(score1,(250.,210.))
    screen.blit(score2,(380.,210.))

    bar1_y += bar1_move

# movement of circle
    time_passed = clock.tick(30)
    time_sec = time_passed / 1000.0

    speed_y += g
    circle_x += speed_x * time_sec
    circle_y += speed_y * time_sec 
    ai_speed = speed_circ * time_sec +5
#AI of table.
    if isDown:
        table1_y += gm
        if table1_y >= 331:
            isDown = False
    else:
        table1_y += -gm
        if table1_y <= 10:
            isDown = True
#AI of computer.
    if circle_x >= 305.:
        if not bar2_y == circle_y + 7.5:
            if bar2_y < circle_y + 7.5:
                bar2_y += ai_speed
            if  bar2_y > circle_y - 42.5:
                bar2_y -= ai_speed
        else:
            bar2_y == circle_y + 7.5

    if bar1_y >= 420.: bar1_y = 420.
    elif bar1_y <= 10. : bar1_y = 10.
    if bar2_y >= 420.: bar2_y = 420.
    elif bar2_y <= 10.: bar2_y = 10.

#since I don't know anything about collision, ball hitting bars goes like    this.
    if circle_x <= bar1_x + 10.:
        circle_y += 5
        if circle_y >= bar1_y - 7.5 and circle_y <= bar1_y + 42.5:
            circle_x = 20.
            speed_x = -speed_x
    if circle_x >= bar2_x - 15.:
        if circle_y >= bar2_y - 7.5 and circle_y <= bar2_y + 62.5:
            circle_x = 605.
            speed_x = -speed_x
            #       
            #
    if circle_x < 5.:
        bar2_score += 1
        g = -g
        circle_x, circle_y = 320., 462.5
        bar1_y,bar_2_y = 215., 215.
    elif circle_x > 620.:
        bar1_score += 1
        g = -g
        circle_x, circle_y = 307.5, 462.5
        bar1_y, bar2_y = 215., 215.
    if circle_y <= 10.:
        speed_y = -speed_y
        circle_y = 10.
    elif circle_y >= 457.5:
        speed_y = -speed_y
        circle_y = 457.5

    pygame.display.update()

Suggestions?

\$\endgroup\$
1
\$\begingroup\$

I wrote this in C#, but It has the same idea, and can be taken as pseudo code.

Because in pong you're only dealing with rectangles and not polygons, it makes collision detection and response fairly easy. In C#, the .NET 4.5 framework already has a method at allows you to check if a rectangle is intersecting (touching) another rectangle. (Keep in mind that your ball doesn't have to be a circle, only the sprite does). Check this source out for information on how to detect collision, it's actually really simple to do.

This probably isn't the most efficient method, but it works fast and is easy to understand. Here's how it works:

Counters are made for all sides when trying to know what side of the rectangle (paddle, in your case), hit. Through a series of if statements, the velocity of the ball is analyzed, and gives a point to whichever the possible sides hit are.

For example, left say I have a ball moving to the right, and is about to hit the paddle. We can be sure that the ball can only hit the top, bottom, or left side of the paddle. The ball could never hit the right side of the paddle, because it never travels to the left in a decreasing X velocity. Here's a graphic I made that might be a little more clear:

enter image description here

The red sides indicate all the possible sides that ball is able to hit when the X velocity is increasing (going to the right). After testing the X and Y velocity, we now test the position of the ball relative to the paddle. Using the same format, if the ball is to the left of the paddle, it is most likely to hit the left, top and bottom sides. We then add points appropriately. Through a series of these tests, if we test both the X and Y velocity and position, we get results that might look like:

top: 3

bottom: 3

left: 4

right: 2

Because "left" has the highest number, it means that it contains the highest number of possibilities that the ball is able to hit. Therefore, this is the side that the ball hit.

        //counters for the sides
        byte top = 0;
        byte left = 0;
        byte right = 0;
        byte bottom = 0;

        //testing for X velocity
        if (_velocity.X > 0)
        {
            top++;
            bottom++;
            left++;

        }
        else
        {
            top++;
            bottom++;
            right++;
        }

        //testing for Y velocity
        if (_velocity.Y > 0)
        {
            left++;
            right++;
            top++;

        }
        else
        {
            left++;
            right++;
            bottom++;
        }

        //testing for X position
        if (_position.X > rect.X)
        {
            top++;
            bottom++;
            right++;
        }
        else
        {
            top++;
            bottom++;
            left++;
        }

        //testing for Y position
        if (_position.Y > rect.Y)
        {
            left++;
            right++;
            bottom++;
        }
        else
        {
            left++;
            right++;
            top++;
        }

        //using results
        byte[] sides = { top, left, right, bottom };

        //sides.Max() just means that of all the results in the array, the
        //it returns the side that had the highest (max) number. 
        if (sides.Max() == top)
        {
            _velocity.Y = ball.velocity.Y * -1;
        }
        else if (sides.Max() == bottom)
        {
            _velocity.Y = ball.velocity.Y * -1;
        }
        else if (sides.Max() == left)
        {
            _velocity.X = ball.velocity.X * -1;
        }
        else if (sides.Max() == right)
        {
            _velocity.X = ball.velocity.X * -1;
        }

I hope this helps, and good luck on your coding!

\$\endgroup\$
1
\$\begingroup\$

I would try making sprites, but if you're new to pygame and python that can be a complicated idea. What I would do then, is draw a rect around your paddles that have the same position as your paddles so the rects will follow the paddle. Afterwards, I'd put a rect around the ball. Then make a collision detection that says:

if ball_rect.collidepoint(x,y):
    change ball position incrementally.

x,y would be the paddle positions. And if the ball hits the paddle positions you can change the balls position incrementally.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.