Unable to fix broken ball movement in pygame pong

2019-09-09 13:15发布

问题:

I'm fairly new to programming, game programming especially. I'm trying to make pong using pygame, but have run into a slight issue. Essentially, the ball hits a paddle, stops, and then keeps going once the paddle is out of the way. Obviously I want the ball to bounce back, but I can't figure out why it won't, when I've coded (what I thought was) the appropriate logic for ball-paddle collisions. Here's my code:

# importing stuff
import sys, pygame
from pygame.locals import *

# starting pygame
pygame.init()

# defining basic colours
white = (255, 255, 255)
black = (0, 0, 0)

# set up the clock
clock = pygame.time.Clock()

# text and such
tFont = pygame.font.SysFont("monospace", 15)

# setting window res and setting display
winX, winY = 600, 300
window = pygame.display.set_mode((winX, winY))

# setting the speed for on screen stuff
playSpeed = 5 # player speed (p1 and p2)

# counts points for each player
#         1   2
points = [0, 0]

# tallies number of times FPS is counted and added to toal amount
fpsCount = 0
fpsTotal = 0

class Ball(object):
    def __init__(self, speed):
        # set default ball position in screen centre
        self.ballX = winX / 2
        self.ballY = winY / 2
        self.ballSpeed = speed
    def move(self):
        if points[0] > points[1]: # if p1 has more points than p2
            self.ballX -= self.ballSpeed # ball goes to p1's side
        elif points[0] < points[1]: # only other condition could be p2 having more points
            self.ballX += self.ballSpeed # ball goes to p2's side
        elif points[0] == points[1]: # if points are equal
            self.ballX -= self.ballSpeed # favour player 1 (change later)

        pygame.draw.circle(window, black, (self.ballX, self.ballY), 3)
    def collide(self, paddle): # unsure if 'paddle' necessary
        # if ball hits top of paddle, bounce to top
        # if hits bottom, bounce to bottom 
        # if ball hits midsection, bounce straight (middle could be about 10px?)
        if paddle == playerOne:
            self.ballX
            self.ballX += self.ballSpeed

class Paddle(object):
    # set the player number (1/2) and if it's an AI or real player
    def __init__(self, player, aiornot):
        if player == 1 and aiornot == False:
            self.coords = [[40, 130], [40, 160]]
        elif player == 2 and aiornot == False:
            self.coords = [[560, 130], [560, 160]]

        self.movement = 'stop' # sets default movement

    def move(self):
        if self.movement == 'down' and self.coords[1][1] < 300:
            self.moveDown()

        elif self.movement == 'up' and self.coords[0][1] > 0:
            self.moveUp()

        elif self.movement == 'stop':
            self.stop()

        # draw the paddle in new position
        pygame.draw.line(window, black, self.coords[0], self.coords[1], 10)

    # movement functions, for direction and such
    def moveDown(self):
        self.coords[0][1] += playSpeed
        self.coords[1][1] += playSpeed

    def moveUp(self):
        self.coords[0][1] -= playSpeed
        self.coords[1][1] -= playSpeed

    def stop(self):
        self.coords[0][1] = self.coords[0][1]
        self.coords[1][1] = self.coords[1][1]

ball = Ball(playSpeed)
playerOne = Paddle(1, False)
playerTwo = Paddle(2, False)

# main loop
while True:
    # event handling for exit
    for event in pygame.event.get():
        if event.type == QUIT:
            # print the average FPS
            print round(fpsTotal / fpsCount, 2), "fps (avg)"
            pygame.quit()
            sys.exit()
        # setting direction upon arrow key press
        elif event.type == KEYDOWN:
            if event.key == K_DOWN:
                playerOne.movement = 'down'
            elif event.key == K_UP:
                playerOne.movement = 'up'
            elif event.key == K_s:
                playerTwo.movement = 'down'
            elif event.key == K_w:
                playerTwo.movement = 'up'
        # when the key is released, stop moving
        elif event.type == KEYUP:
            if event.key == K_DOWN or event.key == K_UP:
                playerOne.movement = 'stop'
            elif event.key == K_s or event.key == K_w:
                playerTwo.movement = 'stop'
            print "player1:", playerOne.coords
            print "player2:", playerTwo.coords

    # this is a mess... if the balls x coords = the paddles x coords, and the balls y
    # coord is somewhere between the start and end point of the paddle, then do the balls
    # collision function on the paddle
    if ball.ballX >= playerOne.coords[0][0] and ball.ballX <= playerOne.coords[1][0]: # or ball.ballX == 40
        if ball.ballY >= playerOne.coords[0][1] and ball.ballY <= playerOne.coords[1][1]:
            ball.collide(playerOne)

    print ball.ballX, ball.ballY

    # fill the window
    window.fill(white)
    # redraw the bat with new position
    playerOne.move()
    playerTwo.move()
    # redraw the ball with new position
    ball.move()
    # set FPS to 60
    clock.tick(60)
    # for working out average FPS
    fpsCount += 1
    fpsTotal += clock.get_fps()
    # set window title
    pygame.display.set_caption("Long Pong")
    # render FPS to text, display text
    text = tFont.render(str(round(clock.get_fps(), 2)), 8, black)
    window.blit(text, (545, 5))
    # update display
    pygame.display.update()

And also a pastebin here if it's easier to look at/copy.

I appreciate any help with this, I've been able to work out any other problem on my own, but I can't tell what I'm missing here.

回答1:

I hope the below code helps. Although my program was a bit different because every time the ball hit the paddle we had to generate a new ball.

import random
from livewires import games, color

games.init(screen_width = 640, screen_height = 480, fps = 50)

class Ball(games.Sprite):

    quit_label = games.Text(value = "Press Q to Quit", size = 25, color = color.white, top = 5, right = 130,
                              is_collideable = False)
    games.screen.add(quit_label)

    def update(self):
        if self.right > games.screen.width:
            self.dx = -self.dx

        if self.left < 0:
            self.game_over()

        if self.bottom > games.screen.height or self.top < 0:
            self.dy = -self.dy

        #pressing 'q' quits the game
        if games.keyboard.is_pressed(games.K_q):
            self.game_over()

    def bounce(self):
        self.dx = -self.dx


    def game_over(self):
        """ End the game. """
        end_message = games.Message(value = "Game Over",
                                    size = 90,
                                    color = color.red,
                                    x = games.screen.width/2,
                                    y = games.screen.height/2,
                                    lifetime = 3 * games.screen.fps,
                                    after_death = games.screen.quit,
                                    is_collideable = False)
        games.screen.add(end_message)
        self.destroy()

class Paddle(games.Sprite):
    image = games.load_image("paddle.bmp")

    score = games.Text(value = 0, size = 25, color = color.white, top = 15, 
        right = games.screen.width - 10, is_collideable = False)
    games.screen.add(score)

    def __init__(self):
        super(Paddle, self).__init__(image = Paddle.image, x = games.mouse.x, bottom = games.screen.height)

    def update(self):
        """ Move to mouse y position. """
        self.y = games.mouse.y

        if self.left > 0:
            self.left = 10

        if self.right > games.screen.height:
            self.right = games.screen.height

        self.check_catch()

    def check_catch(self):
        for ball in self.overlapping_sprites:
            Paddle.score.value += 1
            ball_image2 = games.load_image("ball.bmp")
            ball2 = Ball(image = ball_image2,
                      x = games.screen.width/2,
                      y = games.screen.height/2,
                      dx = 1,
                      dy = 1)
            games.screen.add(ball2)
            ball.bounce()



def main():
    wall_image = games.load_image("background.bmp", transparent = False)
    games.screen.background = wall_image

    ball_image = games.load_image("ball.bmp")
    the_ball = Ball(image = ball_image,
                      x = games.screen.width/2,
                      y = games.screen.height/2,
                      dx = 1,
                      dy = 1)
    games.screen.add(the_ball)

    the_paddle = Paddle()
    games.screen.add(the_paddle)

    games.mouse.is_visible = False

    games.screen.mainloop()

# kick it off!
main()