【问题标题】:Ball doesn't bounce of screen edges in PyGame球在 PyGame 中不会反弹屏幕边缘
【发布时间】:2022-01-21 13:08:39
【问题描述】:

我正在使用 PyGame 在 Python 中构建一个 Pong 游戏。出于某种原因,游戏中的球有时不会从墙壁/屏幕边缘反弹。它会从墙上反弹一两次然后停止。

我不知道为什么会这样。

有人可以检查我的代码以查看是否有任何错误吗?

import os
import pygame

WIDTH = 600
HEIGHT = 500

class Slider:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.height = 120
        self.width = 8

    def draw(self, win):
        pygame.draw.rect(win, (0, 255, 64), (self.x, self.y, self.width, self.height))

    def move(self, dir, dis):
        if dir == 'top' and self.y >= 0:
            self.y -= dis
        elif dir == 'bottom' and self.y <= 379:
            self.y += dis 
        else: 
            self.y = self.y

class Ball:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.radius = 15

        self.speedY = 0.2
        self.speedX = 0.2

        self.top = y
        self.bottom = y + 30
        self.left = x
        self.right = x + 30

    def draw(self, win):
        ball = pygame.draw.circle(win, (255, 255, 0), (self.x, self.y), self.radius)
        self.top = ball.top
        self.bottom = ball.bottom
        self.left = ball.left
        self.right = ball.right

    def move(self):

        if self.top <= 0:
            self.speedY *= -1
        else:
            self.y += self.speedY
            self.x += self.speedX

        if self.bottom >= HEIGHT:
            self.speedY *= -1
        else:
            self.y += self.speedY
            self.x += self.speedX
            
        if self.left <= 0:
            self.speedX *= -1
        else:
            self.y += self.speedY
            self.x += self.speedX

        if self.right >= WIDTH:
            self.speedX *= -1
        else:
            self.y += self.speedY
            self.x += self.speedX


def draw_window(win, *sprites):
    pygame.display.update()
    win.fill((0, 0, 0, 0))
    for sprite in sprites:
        sprite.draw(win)

def main():
    win = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption('Pong Game')

    slider1 = Slider(100, 250)
    slider2 = Slider(500, 250)
    ball = Ball(300, 250)

    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_DOWN:
                slider1.move('bottom', 0.3)
            elif event.key == pygame.K_UP:
                slider1.move('top', 0.3)
            else:
                print(pygame.key.name(event.key))

        ball.move()

        key = pygame.key.get_pressed()

        if key[pygame.K_s]:
            slider2.move('bottom', 0.3)
        elif key[pygame.K_w]:
            slider2.move('top', 0.3)

        draw_window(win, slider1, slider2, ball)


    pygame.quit()
    quit()

main()

【问题讨论】:

    标签: python pygame pong


    【解决方案1】:

    你需要稍微改变你的move 方法,你需要移除else 块,因为它们会扰乱球的运动,只要在调用move 时总是移动球一次。您还可以结合检查球是否在一条轴的边缘(使用or):

    def move(self):
    
        if self.top <= 0 or self.bottom >= HEIGHT:
            self.speedY *= -1
    
        if self.left <= 0 or self.right >= WIDTH:
            self.speedX *= -1
    
        self.y += self.speedY
        self.x += self.speedX
    

    【讨论】:

    • 谢谢。这行得通,顺便说一句,我之前有这个代码,但self.y += self.speedYself.x += self.speedXif 语句之前。所以它没有工作
    【解决方案2】:

    改变方向后移动球,无论如何:

    class Ball:
       # [...]
    
       def move(self):
    
            if self.top <= 0 or self.bottom >= HEIGHT:
                self.speedY *= -1
            if self.left <= 0 or self.right >= WIDTH:
                self.speedX *= -1
    
            self.y += self.speedY
            self.x += self.speedX
    

    注意,您根本不需要topbottomleftright 属性。使用pygame.Rect 对象:

    class Ball:
        def __init__(self, x, y):
            self.x = x
            self.y = y
            self.radius = 15
            self.rect = pygame.Rect(0, 0, self.radius*2, self.radius*2)  
            self.speedY = 0.2
            self.speedX = 0.2
    
        def draw(self, win):
             pygame.draw.circle(win, (255, 255, 0), (self.x, self.y), self.radius)
    
        def move(self):
            self.y += self.speedY
            self.x += self.speedX
            self.rect.center = round(self.x), round(self.y)
    
            if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
                self.speedY *= -1
            if self.rect.left <= 0 or self.rect.right >= WIDTH:
                self.speedX *= -1
    

    【讨论】:

      【解决方案3】:

      Rabbid76 suggests 很聪明:

      您可以使用组合(如下面的 self.boundary 组件)或从 Rect 继承 Ball。

      然后您可以重用该边界中的方法,例如move_ip(x,y)。 或通过screen.contains(boundary) 或一些碰撞测试屏幕内部。

      非整数运动

      即使球的位置 (x,y) 可能是非整数(由于例如0.2 的速度),屏幕上的位置也只能表示为整数像素坐标并渲染为整数像素坐标。因此我特意使用roundpygame.Rect 作为屏幕位置。

      pygame.Rect 以像素粒度表示对象,因此其属性存储为整数:

      Rect对象的坐标都是整数。

      使用pygame.Rect的边界测试碰撞

      class Ball:
          def __init__(self, x, y):
              # use center to get (x,y) equivalent to former (self.x,self.y)
              self.center = (x,y)
              self.radius = 15
              # boundary as Rect (actually square) around the circle of the ball
              self.boundary = pygame.Rect(round(x)-self.radius, round(y)-self.radius, self.radius*2, self.radius*2)  
              self.speedY = 0.2
              self.speedX = 0.2
      
          def draw(self, win):
               pygame.draw.circle(win, (255, 255, 0), self.center, self.radius)
      
          def move(self):
              # move center to new position
              self.center[0] += self.speedX
              self.center[1] += self.speedY 
              # move boundary rectangle (screen position by integers only)
              self.boundary.move_ip(round(self.center[0]), round(self.center[1]))
              
              # test if inside the box using integers
              # if not pygame.Rect(0,0, WIDTH,HEIGHT).contains(self.boundary):
              if self.boundary.top <= 0 or self.boundary.bottom >= HEIGHT:
                  self.speedY *= -1
              if self.boundary.left <= 0 or self.boundary.right >= WIDTH:
                  self.speedX *= -1
      

      另见这篇有趣的帖子,解释了move_ip(原地移动)和move(不移动实例但返回移动的实例)之间的区别: Difference between rect.move() and rect.move_ip in pygame

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多