球在 PyGame 中不会反弹屏幕边缘



我正在用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()

你需要稍微改变你的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

Rabbid76的建议非常聪明:

  • 使用pygame.Rect作为球的边界

您可以使用composition(如下面的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(不移动实例,但返回移动的实例(之间的区别:pygame 中rect.move((和rect.move_ip之间的差异

改变方向后移动球,在任何情况下:

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

最新更新