Search code examples
pythonpygamecollision-detectioncollision

Collision Detection Issues - PyGame


I'm pretty new to Python and PyGame. I have made a ball and two paddles ( The two paddle's are controlled by 2 players ). My issue is when the ball hits player 2's paddle, it doesn't bounce off. The problem is at the bottom of the code where there's a comment saying: #WITH PLAYER 2.

# IMPORTS
import pygame, time;

# INIT PYGAME
pygame.init();

# GLOBALS
global screen, display_w, display_h;
global clock, FPS;
global gameOver;
global ball, player_1, player_2;

# ASSIGN GLOBALS
def assign_globals():
    global screen, display_w, display_h;
    global clock, FPS;
    global gameOver;
    global ball, player_1, player_2;

    display_w = 800;
    display_h = 600;
    screen = pygame.display.set_mode((display_w, display_h));

    clock = pygame.time.Clock();
    FPS = 60;

    gameOver = False;

    ball = Ball();
    player_1 = Player();
    player_1.x = 0;
    player_1.color = (255, 0, 0);
    player_2 = Player();
    player_2.x = display_w - 15;
    player_2.color = (0, 255, 0);

# MAIN
def main():
    assign_globals();
    setup();
    game_loop();
    set_fps();

# GAME LOOP
def game_loop():
    global gameOver;

    while(not gameOver):
        for event in pygame.event.get():
            if(event.type == pygame.QUIT):
                gameOver = True;

            if(event.type == pygame.KEYDOWN):
                # PLAYER 1
                if(event.key == pygame.K_w):
                    player_1.velY -= 1;
                if(event.key == pygame.K_s):
                    player_1.velY += 1;
                # PLAYER 2
                if(event.key == pygame.K_UP):
                    player_2.velY -= 1;
                if(event.key == pygame.K_DOWN):
                    player_2.velY += 1;

            if(event.type == pygame.KEYUP):
                # PLAYER 1
                if(event.key == pygame.K_w or event.key == pygame.K_s):
                    player_1.velY = 0;
                # PLAYER 2
                if(event.key == pygame.K_UP or event.key == pygame.K_DOWN):
                    player_2.velY = 0;

        draw();
        animate();
        collision();

# DRAW
def draw():
    global screen;

    screen.fill((0, 0, 0));
    ball.draw();
    player_1.draw();
    player_2.draw();

    pygame.display.update();

# ANIMATE
def animate():
    ball.animate();
    player_1.animate();
    player_2.animate();

# COLLISION
def collision():
    ball.collision();
    player_1.collision();
    player_2.collision();

# SETUP
def setup():
    pygame.display.set_caption("Pong");

# CLASSES
class Ball():
    def __init__(self, x=0, y=0, w=0, h=0, velX=0, velY=0, color=()):
        global display_w, display_h;

        self.x = display_w / 2;
        self.y = display_w / 2;
        self.w = 20;
        self.h = 20;
        self.color = (0, 0, 255);
        self.velX = 0.4;
        self.velY = 0.4;

    def reset(self):
        self.x = display_w / 2;
        self.y = display_w / 2;
        self.velX = 0.4;
        self.velY = 0.4;

    def draw(self):
        global screen;

        pygame.draw.ellipse(screen, self.color, (self.x, self.y, self.w, self.h));

    def animate(self):
        self.x += self.velX;
        self.y += self.velY;

    def collision(self):
        global display_w, display_h;

        # WITH WALL
        if(self.x >= display_w - self.w):
            self.reset();
        if(self.x <= 0):
            self.reset();
        if(self.y >= display_h - self.h):
            self.velY *= -1;
        if(self.y <= 0):
            self.velY *= -1;

        # WITH PLAYER 1
        if(self.x <= player_1.x + player_1.w and
            self.x >= player_1.x and
            self.y >= player_1.y and
            self.y <= player_1.y + player_1.h):
                ball.velX *= -1;

        # WITH PLAYER 2
        if(self.x <= player_2.x + player_2.w and
            self.x >= player_2.x and
            self.y >= player_2.y and
            self.y <= player_2.y + player_2.h):
                ball.velX *= -1;

class Player():
    def __init__(self, x=0, y=0, w=0, h=0, velY=0, color=()):
        global display_w, display_h;

        self.w = 15;
        self.h = 100;
        self.x = color;
        self.y = display_h / 2 - self.h / 2;
        self.velY = 0;
        self.color = color;

    def draw(self):
        global screen;

        pygame.draw.rect(screen, self.color, (self.x, self.y, self.w, self.h));

    def animate(self):
        self.y += self.velY;

    def collision(self):
        global display_h;

        # WITH WALL
        if(self.y + self.h > display_h):
            self.velY = 0;
        elif(self.y < 0):
            self.velY = 0;


# SET FPS
def set_fps():
    global clock, FPS;

    clock.tick(FPS);

# CALL MAIN
if(__name__ == "__main__"):
    main();

# QUIT
pygame.quit();
quit();

Solution

  • The problem seems to be that you are basing collision detection on the upper-left corner of the ball's rectangle. The left paddle is against the left side, so that's (not accurate but usually) not so bad, but the right paddle is against the right side, so the left corner of the ball rectangle will only be inside the paddle of player 2 when the ball has already penetrated the paddle, and at that point, the wall detection routine (which comes before the paddle detection) will trigger a reset, because it scored.

    So player 2's paddle is made irrelevant, since it detects the far corner, and any time it would detect a collision, it detects a score instead and resets the ball position before it tests for a collision.

    So, you need to take into account the whole shape of the ball, or at least make the detection point symmetrical. If you use the center point, it'll be fair, but it'll also miss on the edges of the ball. Ideally you could do a test whether the paddle intersects the ball's ellipse.