Partial Collision Error

59 views Asked by At

I am currently trying to make a version of Pong using pygame, but I am having some issues with detecting the collision between the ball and the paddle. I got the detection working with both paddles, but I soon came to realize that the left player cannot move their paddle when the ball collides with it without the ball passing through. The player on the right has no such issue, and keeping the left player still allows the ball to reflect normally.

import pygame, sys
from pygame.locals import *

pygame.init()
pygame.mixer.init()
pygame.font.init()

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

screenx = 1080
screeny = 720

clock = pygame.time.Clock()
for pygame.mouse.get_pos in (screenx, screeny):
    pygame.mouse.set_visible(False)


# Blueprint for creating player character
class Player:
    # Defines initial conditions for player
    def __init__(self):
        self.xpos = 0
        self.ypos = 0
        self.width = 20
        self.length = 100
        self.speed = 10
        self.move = 0
        self.upkey = 0
        self.downkey = 0
        self.score = 0
        self.rect = pygame.Rect((self.xpos, self.ypos, self.width, self.length))

    # if player character, defines paddle movement
    def player_move(self, key):
        if key[self.upkey]:
            self.ypos -= self.speed
            self.move = -1
        elif key[self.downkey]:
            self.ypos += self.speed
            self.move = 1
        else:
            self.move = 0

    # Creates screen boundary for players
    def player_boundary(self, key):
        if self.ypos == 0:
            self.speed = 0
            self.move = 0
            if key[self.downkey]:
                self.speed = 10
                self.move = -1
        elif self.ypos + self.length == screeny:
            self.speed = 0
            self.move = 0
            if key[self.upkey]:
                self.speed = 10
                self.move = 1

    # Draws player rectangle to surface
    def draw(self, surface):
        self.rect = pygame.draw.rect(surface, WHITE, (self.xpos, self.ypos, self.width, self.length))


# Blueprint for creating the ball
class Ball:
    def __init__(self):
        self.size = 15
        self.speed = 10
        self.vy = 0
        self.vx = 10
        self.ypos = screeny/2
        self.xpos = 100
        self.rect = pygame.Rect((self.xpos, self.ypos, self.size, self.size))

    # Defines movement of ball (in xy components)
    def ball_move(self):
        self.ypos += self.vy
        self.xpos += self.vx

    # method for ball bouncing off of player paddle **
    def ball_block(self, player):
        if player.move > 0:
            self.vy += player.speed * 0.3
            self.vx = -((self.speed**2 - self.vy**2) ** .5)
        elif player.move < 0:
            self.vy = player.speed * -0.3
            self.vx = -((self.speed**2 - self.vy**2) ** .5)
        elif player.move == 0:
            self.vx = -self.vx

    # for reflecting off of screen boundaries **
    def ball_reflect(self, screeny):
        if self.ypos == 0:
            self.vy = -self.vy
        elif self.ypos == screeny - self.size:
            self.vy = -self.vy

    # Method for detecting when player has scored
    def score(self):
        if self.xpos == 0:
            self.vx = -self.vx
            print ('Score')
        elif self.xpos == screenx - self.size:
            self.vx = -self.vx
            print ('Score')

    # Draws ball to surface
    def draw(self, surface):
        self.rect = pygame.draw.rect(surface, WHITE, (self.xpos, self.ypos,     self.size, self.size))

def setup():
    # Assigns values to player 1 (left side)
    player1 = Player()
    player1.xpos = 50
    player1.ypos = screeny/1.5
    player1.upkey = K_w
    player1.downkey = K_s

    # Assigns values to player 2 (right side)
    player2 = Player()
    player2.xpos = screenx - player2.width - 50
    player2.ypos = screeny/1.5
    player2.upkey = K_UP
    player2.downkey = K_DOWN

    return player1, player2

# Main loop of the game
def main():
    player1, player2 = setup()
    ball = Ball()
    play = True

    while play:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        key = pygame.key.get_pressed()
        if key[K_ESCAPE]:
            play = False

        if ball.rect.colliderect(player1):  # For some reason, ball can go through P1 while moving
            ball.ball_block(player1)
        elif ball.rect.colliderect(player2):
            ball.ball_block(player2)

        # Had issue with ball going through bottom, fixed by adding screeny to dependencies
        if ball.ypos == 0:
            ball.ball_reflect(screeny)
        elif ball.ypos == screeny - ball.size:
            ball.ball_reflect(screeny)

        player1.player_move(key)
        player2.player_move(key)
        ball.ball_move()

        # Calls methods for player boundaries and ball scoring
        player1.player_boundary(key)
        player2.player_boundary(key)
        ball.score()  # Ball will only score if colliding head on and not on P2's side

        # Draws objects to screen
        surface.fill(BLACK)
        player1.draw(surface)
        player2.draw(surface)
        ball.draw(surface)

        pygame.display.update()
        clock.tick(60)

# Sets up screen, calls main loop
surface = pygame.display.set_mode((screenx, screeny))
pygame.display.set_caption('PONG')

main()

As far as I can tell, the code for both players is identical, and yet only one player works in the way I intended it to. Any help would be very much appreciated, and thank you for your time.

EDIT: Fixed by adding a few lines to the ball_block method

    def ball_block(self, player):
    if player.move > 0:
        self.vy += player.speed * 0.3
        if self.vx < 0:
            self.vx = ((self.speed**2 - self.vy**2) ** .5)
        elif self.vx > 0:
            self.vx = -((self.speed**2 - self.vy**2) ** .5)
    elif player.move < 0:
        self.vy = player.speed * -0.3
        if self.vx < 0:
            self.vx = ((self.speed**2 - self.vy**2) ** .5)
        elif self.vx > 0:
            self.vx = -((self.speed**2 - self.vy**2) ** .5)
    elif player.move == 0:
        self.vx = -self.vx
1

There are 1 answers

0
John La Rooy On BEST ANSWER

If the player is moving during the collision, you are always setting

self.vx = -((self.speed**2 - self.vy**2) ** .5)

so it's always negative