球不会在 PyGame 中弹跳屏幕边缘
Ball doesn't bounce of screen edges in PyGame
我正在 Python 和 PyGame 一起开发 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()
改变方向后移动小球,无论如何:
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
请注意,您根本不需要 top
、bottom
、left
和 right
属性。使用 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
你需要稍微改变一下你的 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
什么很聪明:
- 使用
pygame.Rect
作为你的 Ball 的边界
您可以使用组合(如下面的 self.boundary
组件)或从 Rect
.
继承 Ball
然后您可以重用 边界 中的方法,例如 move_ip(x,y)
。
或通过 screen.contains(boundary)
或一些碰撞测试屏幕内部。
非整数运动
即使球的位置 (x,y) 可能是非整数(由于速度,例如 0.2
),屏幕上的位置也只能表示为并呈现为整数像素坐标。因此,我故意使用 round
和 pygame.Rect
作为屏幕位置。
pygame.Rect
表示像素粒度的对象,因此其属性存储为整数:
The coordinates for Rect objects are all integers.
使用 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
另见这个有趣的 post 解释了 move_ip
(原地移动)和 move
(不移动实例但 returns 移动)之间的区别:
我正在 Python 和 PyGame 一起开发 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()
改变方向后移动小球,无论如何:
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
请注意,您根本不需要 top
、bottom
、left
和 right
属性。使用 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
你需要稍微改变一下你的 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
什么
- 使用
pygame.Rect
作为你的 Ball 的边界
您可以使用组合(如下面的 self.boundary
组件)或从 Rect
.
然后您可以重用 边界 中的方法,例如 move_ip(x,y)
。
或通过 screen.contains(boundary)
或一些碰撞测试屏幕内部。
非整数运动
即使球的位置 (x,y) 可能是非整数(由于速度,例如 0.2
),屏幕上的位置也只能表示为并呈现为整数像素坐标。因此,我故意使用 round
和 pygame.Rect
作为屏幕位置。
pygame.Rect
表示像素粒度的对象,因此其属性存储为整数:
The coordinates for Rect objects are all integers.
使用 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
另见这个有趣的 post 解释了 move_ip
(原地移动)和 move
(不移动实例但 returns 移动)之间的区别: