如何使用 pygame 对乒乓球中的弹跳运动进行编码
How to code bounce movement in pong using pygame
我是 python 的菜鸟,我正在尝试重新创建 Pong 游戏,并且我正在尝试尽可能自己做。
这些是我目前的代码存在的问题:
- 我基本上对球在边缘反弹时的每一个可能运动进行了编码。我花了几个小时研究它,我让它开始工作,我只是想知道是否有更有效的方法来用我的代码生成类似的输出(因为我知道这应该是一个初学者项目)?
- 每次球弹过边缘时,分数都会瞬间增加,每次球重生时都会回到 0。
- 如何让小球随机生成?在每一轮的开始(和重新开始)?
这是我的 class 代码和程序的主要功能:
import pygame
import random
from rect_button import Button
pygame.init()
WIDTH = 800
HEIGHT = 500
window = pygame.display.set_mode((WIDTH, HEIGHT))
TITLE = "Pong"
pygame.display.set_caption(TITLE)
# COLORS
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
# FONTS
small_font = pygame.font.SysFont("courier", 20)
large_font = pygame.font.SysFont("courier", 60, True)
class Paddle:
def __init__(self, x, y, width, height, vel, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = vel
self.color = color
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height), 0)
class Ball:
def __init__(self, x, y, side, vel, color):
self.x = x
self.y = y
self.side = side
self.vel = vel
self.color = color
self.lower_right = False
self.lower_left = True
self.upper_right = False
self.upper_left = False
self.ball_bag = []
self.last_movement = 'ball.lower_right'
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.side, self.side), 0)
def move_lower_right(self):
self.x += self.vel
self.y += self.vel
def move_upper_right(self):
self.x += self.vel
self.y -= self.vel
def move_upper_left(self):
self.x -= self.vel
self.y -= self.vel
def move_lower_left(self):
self.x -= self.vel
self.y += self.vel
def start(self):
self.lower_right = True
self.lower_left = False
self.upper_right = False
self.upper_left = False
self.last_movement = 'ball.lower_left'
# return random.choice([self.lower_right, self.lower_left, self.upper_left, self.upper_right]) is True
def main():
run = True
fps = 60
clock = pygame.time.Clock()
# Initializing Paddles
left_paddle = Paddle(20, 100, 10, 50, 5, white)
right_paddle = Paddle(770, 350, 10, 50, 5, white)
balls = []
def redraw_window():
window.fill(black)
left_paddle.draw(window)
right_paddle.draw(window)
for ball in balls:
ball.draw(window)
player_A_text = small_font.render("Player A: " + str(score_A), 1, white)
player_B_text = small_font.render("Player B: " + str(score_B), 1, white)
window.blit(player_A_text, (320 - int(player_A_text.get_width() / 2), 10))
window.blit(player_B_text, (480 - int(player_B_text.get_width() / 2), 10))
pygame.draw.rect(window, white, (20, 450, 760, 1), 0)
pygame.draw.rect(window, white, (20, 49, 760, 1), 0)
pygame.draw.rect(window, white, (19, 50, 1, 400), 0)
pygame.draw.rect(window, white, (780, 50, 1, 400), 0)
pygame.display.update()
while run:
score_A = 0
score_B = 0
clock.tick(fps)
if len(balls) == 0:
ball = Ball(random.randrange(320, 465), random.randrange(200, 285), 15, 3, white)
balls.append(ball)
if ball.lower_left:
ball.move_lower_left()
if ball.last_movement == 'ball.lower_right':
if ball.y + ball.side > HEIGHT - 50:
ball.lower_left = False
ball.last_movement = 'ball.lower_left'
ball.upper_left = True
if ball.last_movement == 'ball.upper_left':
if ball.x < 30:
if left_paddle.x < ball.x < left_paddle.x + left_paddle.width:
if left_paddle.y < ball.y + ball.side < left_paddle.y + left_paddle.height:
ball.lower_left = False
ball.last_movement = 'ball.lower_left'
ball.lower_right = True
else:
score_B += 1
balls.remove(ball)
#ball.start()
if ball.y + ball.side > HEIGHT - 50:
ball.lower_left = False
ball.last_movement = 'ball.lower_left'
ball.upper_left = True
if ball.upper_left:
ball.move_upper_left()
if ball.last_movement == 'ball.lower_left':
if ball.x < 30:
if left_paddle.x < ball.x < left_paddle.x + left_paddle.width:
if left_paddle.y < ball.y + ball.side < left_paddle.y + left_paddle.height:
ball.upper_left = False
ball.last_movement = 'ball.upper_left'
ball.upper_right = True
else:
score_B += 1
balls.remove(ball)
#ball.start()
if ball.y < 50:
ball.upper_left = False
ball.last_movement = 'ball.upper_left'
ball.lower_left = True
if ball.last_movement == 'ball.upper_right':
if ball.y < 50:
ball.upper_left = False
ball.last_movement = 'ball.upper_left'
ball.lower_left = True
if ball.upper_right:
ball.move_upper_right()
if ball.last_movement == 'ball.upper_left':
if ball.y < 50:
ball.upper_right = False
ball.last_movement = 'ball.upper_right'
ball.lower_right = True
if ball.last_movement == 'ball.lower_right':
if ball.x + ball.side > WIDTH - 30:
if right_paddle.x + right_paddle.width > ball.x + ball.side > right_paddle.x:
if right_paddle.y < ball.y + ball.side < right_paddle.y + right_paddle.height:
ball.upper_right = False
ball.last_movement = 'ball.upper_right'
ball.upper_left = True
else:
score_A += 1
balls.remove(ball)
#ball.start()
if ball.y < 50:
ball.upper_right = False
ball.last_movement = 'ball.upper_right'
ball.lower_right = True
if ball.lower_right:
ball.move_lower_right()
if ball.last_movement == 'ball.upper_right':
if ball.y + ball.side > HEIGHT - 50:
ball.lower_right = False
ball.last_movement = 'ball.lower_right'
ball.upper_right = True
if ball.x + ball.side > WIDTH - 30:
if right_paddle.x + right_paddle.width > ball.x + ball.side > right_paddle.x:
if right_paddle.y < ball.y + ball.side < right_paddle.y + right_paddle.height:
ball.lower_right = False
ball.last_movement = 'ball.lower_right'
ball.lower_left = True
else:
score_A += 1
balls.remove(ball)
#ball.start()
if ball.last_movement == 'ball.lower_left':
if ball.y + ball.side > HEIGHT - 50:
ball.lower_right = False
ball.last_movement = 'ball.lower_right'
ball.upper_right = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_UP] and right_paddle.y > 50:
right_paddle.y -= right_paddle.vel
if keys[pygame.K_w] and left_paddle.y > 50:
left_paddle.y -= left_paddle.vel
if keys[pygame.K_DOWN] and right_paddle.y + right_paddle.height < HEIGHT - 50:
right_paddle.y += right_paddle.vel
if keys[pygame.K_s] and left_paddle.y + left_paddle.height < HEIGHT - 50:
left_paddle.y += left_paddle.vel
if keys[pygame.K_SPACE]:
pass
redraw_window()
quit()
def main_menu():
run = True
play_button = Button(green, 100, 350, 150, 75, "Play Pong")
quit_button = Button(red, 550, 350, 150, 75, "Quit")
pong_text = large_font.render("Let's Play Pong!!!", 1, black)
while run:
window.fill(white)
play_button.draw(window, black)
quit_button.draw(window, black)
window.blit(pong_text, (int(WIDTH / 2 - pong_text.get_width() / 2), 100))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
quit()
if event.type == pygame.MOUSEMOTION:
if play_button.hover(pygame.mouse.get_pos()):
play_button.color = (0, 200, 0)
else:
play_button.color = green
if quit_button.hover(pygame.mouse.get_pos()):
quit_button.color = (200, 0, 0)
else:
quit_button.color = red
if event.type == pygame.MOUSEBUTTONDOWN:
if play_button.hover(pygame.mouse.get_pos()):
main()
if quit_button.hover(pygame.mouse.get_pos()):
run = False
quit()
main_menu()
谢谢!!!
回答
- Every time the ball bounces past the edge, the score increments for a split second, and it goes back to 0 every time the ball respawns.
分数在主循环中不断初始化。你必须在循环之前初始化分数:
def main():
# [...]
score_A = 0 # <--- INSERT
score_B = 0
while run:
# score_A = 0 <--- DELETE
# score_B = 0
I basically coded every possible movement of the ball when it bounces on the edge. I spent hours working on it, and I got it to work, I was just wondering if there was a more efficient way to produce a similar output with my code (coz I know this is supposed to be a beginner project)?
和
How can I make the ball spawn at random directions? at the start (and restart) of every round?
您可以使用浮动坐标,而不是实现球的“每个”方向。这些变量通常称为 dx 和 dy。
通过这种方式为您的球获得随机或反向方向很简单,只需为 dx 和 dy 使用随机或反向值即可。
您的球的更新应该如下所示:
def update(self. dt):
self.x += self.dx * self.speed * time_elapsed
self.y += self.dy * self.speed * time_elapsed # Time elasped is often called dt.
Every time the ball bounces past the edge, the score increments for a split second, and it goes back to 0 every time the ball respawns.
查看 Rabid76 的回答。理想情况下,您应该有一个 GameState 对象,其中包含分数、生命和其他内容作为属性。
问题 1 的答案:
是的。肯定有很多更有效的方法来做事。没有必要为方向做那么多变数。简单地说direction = [True, False]
,其中direction[0]
表示左,not direction[0]
表示x-axis中的右。同样,direction[1]
代表y轴。这也解决了您在开始时随机化方向的问题。你可以简单地在你的 init 方法中做 direction = [random.randint(0, 1), random.randint(0, 1)]
来随机化方向。以同样的方式,也列出速度。 self.speed = [0.5, random.uniform(0.1, 1)]
。这样,左右方向的速度将始终相同,但 y 将根据所选的随机数而变化,因此具有适当的随机性,您也不必硬编码随机方向。这样,移动变得非常简单。
class Ball:
def __init__(self, x, y, color, size):
self.x = x
self.y = y
self.color = color
self.size = size
self.direction = [random.randint(0, 1), random.randint(0, 1)]
self.speed = [0.5, random.uniform(0.1, 1)]
def draw(self, display):
pygame.draw.rect(display, self.color, (self.x, self.y, self.size, self.size))
def move(self):
if self.direction[0]:
self.x += self.speed[0]
else:
self.x -= self.speed[0]
if self.direction[1]:
self.y += self.speed[0]
else:
self.y -= self.speed[0]
因为我们定义的方向是布尔值,改变状态也变得非常容易。如果球击中球拍,您只需切换 x 中的布尔值 direction[0] = not direction[0]
并为 y 选择一个新的随机数,而不是手动分配布尔值。
def switchDirection(self):
self.direction = not self.direction
self.speed[1] = random.uniform(0.1, 1)
Paddle 也可以稍微改进,通过给 Paddle class 一个 move
函数而不是在主循环中移动。这只是意味着您必须编写更少的代码。
def move(self, vel, up=pygame.K_UP, down=pygame.K_DOWN):
keys = pygame.key.get_perssed()
if keys[up]:
self.y -= vel
if keys[down]:
self.y += vel
对于碰撞,我建议使用 pygame.Rect()
和 colliderect
,因为它更健壮,也可能更高效。
示例:
import random
import pygame
WIN = pygame.display
D = WIN.set_mode((800, 500))
class Paddle:
def __init__(self, x, y, width, height, vel, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = vel
self.color = color
def move(self, vel, up=pygame.K_UP, down=pygame.K_DOWN):
keys = pygame.key.get_pressed()
if keys[up]:
self.y -= vel
if keys[down]:
self.y += vel
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height), 0)
def getRect(self):
return pygame.Rect(self.x, self.y, self.width, self.height)
left_paddle = Paddle(20, 100, 10, 50, 5, (0, 0, 0))
right_paddle = Paddle(770, 350, 10, 50, 5, (0, 0, 0))
class Ball:
def __init__(self, x, y, color, size):
self.x = x
self.y = y
self.color = color
self.size = size
self.direction = [random.randint(0, 1), random.randint(0, 1)]
self.speed = [0.3, random.uniform(0.2, 0.2)]
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.size, self.size))
def switchDirection(self):
self.direction[0] = not self.direction[0]
self.direction[1] = not self.direction[1]
self.speed = [0.2, random.uniform(0.1, 0.5)]
def bounce(self):
self.direction[1] = not self.direction[1]
self.speed = [0.2, random.uniform(0.01, 0.2)]
def move(self):
if self.direction[0]:
self.x += self.speed[0]
else:
self.x -= self.speed[0]
if self.direction[1]:
self.y += self.speed[1]
else:
self.y -= self.speed[1]
def getRect(self):
return pygame.Rect(self.x, self.y, self.size, self.size)
def boundaries(self):
if ball.x <= 10:
self.switchDirection()
if ball.x + self.size >= 800:
self.switchDirection()
if ball.y + self.size >= 490:
self.bounce()
if ball.y <= 0:
self.bounce()
ball = Ball(400, 250, (255, 0, 0), 20)
while True:
pygame.event.get()
D.fill((255, 255, 255))
ball.draw(D)
ball.boundaries()
ball.move()
#print(ball.x, ball.y)
left_paddle.draw(D)
right_paddle.draw(D)
right_paddle.move(0.4)
left_paddle.move(0.4, down=pygame.K_s, up=pygame.K_w)
if left_paddle.getRect().colliderect(ball.getRect()):
ball.switchDirection()
if right_paddle.getRect().colliderect(ball.getRect()):
ball.switchDirection()
WIN.flip()
一些关于如何减少所有球 movement/collision 代码并使事情更可重用的思考:
import pygame
class Ball:
def __init__(self, bounds, color):
from random import randint, choice
self.bounds = bounds
self.position = pygame.math.Vector2(
randint(self.bounds.left, self.bounds.left+self.bounds.width),
randint(self.bounds.top, self.bounds.top+self.bounds.height)
)
self.velocity = pygame.math.Vector2(choice((-1, 1)), choice((-1, 1)))
self.color = color
self.size = 8
def draw(self, window):
pygame.draw.rect(
window,
self.color,
(
self.position.x-self.size,
self.position.y-self.size,
self.size*2,
self.size*2
),
0
)
def update(self):
self.position.x += self.velocity.x
self.position.y += self.velocity.y
if not self.bounds.left+self.size < self.position.x < self.bounds.left+self.bounds.width-self.size:
self.velocity.x *= -1
if not self.bounds.top+self.size < self.position.y < self.bounds.top+self.bounds.height-self.size:
self.velocity.y *= -1
def main():
from random import randint
window_width, window_height = 800, 500
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Pong")
clock = pygame.time.Clock()
black = (0, 0, 0)
white = (255, 255, 255)
padding = 20
bounds = pygame.Rect(padding, padding, window_width-(padding*2), window_height-(padding*2))
ball = Ball(bounds, white)
def redraw_window():
window.fill(black)
pygame.draw.rect(
window,
white,
(
padding,
padding,
window_width-(padding*2),
window_height-(padding*2)
),
1
)
ball.draw(window)
pygame.display.update()
while True:
clock.tick(60)
ball.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
break
else:
redraw_window()
continue
break
pygame.quit()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
这不是一个完整的 drop-in 替换,我刚刚重新实现了 Ball
class。没有桨。本质上,当实例化一个球时,您传入一个 pygame.Rect
,它描述了允许球弹跳的范围。您还传入了一个颜色元组。然后球在边界内选择一个随机位置(该位置是 pygame.math.Vector2
,而不是将 x
和 y
存储为单独的实例变量)。一个球也有一个速度,它也是一个 pygame.math.Vector2
,所以你可能有独立的速度分量 - 一个用于 x
(水平速度),一个用于 y
(垂直速度)。球的 size
简单地描述了球的尺寸。例如,如果 size
设置为 8
,则球将为 16x16 像素。
Ball
class也有一个update
方法,每game-loop次迭代调用一次。它将球移动到速度决定的下一个位置,并检查球是否与边界发生碰撞。如果是,则取反相应的速度分量。
我是 python 的菜鸟,我正在尝试重新创建 Pong 游戏,并且我正在尝试尽可能自己做。
这些是我目前的代码存在的问题:
- 我基本上对球在边缘反弹时的每一个可能运动进行了编码。我花了几个小时研究它,我让它开始工作,我只是想知道是否有更有效的方法来用我的代码生成类似的输出(因为我知道这应该是一个初学者项目)?
- 每次球弹过边缘时,分数都会瞬间增加,每次球重生时都会回到 0。
- 如何让小球随机生成?在每一轮的开始(和重新开始)?
这是我的 class 代码和程序的主要功能:
import pygame
import random
from rect_button import Button
pygame.init()
WIDTH = 800
HEIGHT = 500
window = pygame.display.set_mode((WIDTH, HEIGHT))
TITLE = "Pong"
pygame.display.set_caption(TITLE)
# COLORS
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
# FONTS
small_font = pygame.font.SysFont("courier", 20)
large_font = pygame.font.SysFont("courier", 60, True)
class Paddle:
def __init__(self, x, y, width, height, vel, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = vel
self.color = color
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height), 0)
class Ball:
def __init__(self, x, y, side, vel, color):
self.x = x
self.y = y
self.side = side
self.vel = vel
self.color = color
self.lower_right = False
self.lower_left = True
self.upper_right = False
self.upper_left = False
self.ball_bag = []
self.last_movement = 'ball.lower_right'
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.side, self.side), 0)
def move_lower_right(self):
self.x += self.vel
self.y += self.vel
def move_upper_right(self):
self.x += self.vel
self.y -= self.vel
def move_upper_left(self):
self.x -= self.vel
self.y -= self.vel
def move_lower_left(self):
self.x -= self.vel
self.y += self.vel
def start(self):
self.lower_right = True
self.lower_left = False
self.upper_right = False
self.upper_left = False
self.last_movement = 'ball.lower_left'
# return random.choice([self.lower_right, self.lower_left, self.upper_left, self.upper_right]) is True
def main():
run = True
fps = 60
clock = pygame.time.Clock()
# Initializing Paddles
left_paddle = Paddle(20, 100, 10, 50, 5, white)
right_paddle = Paddle(770, 350, 10, 50, 5, white)
balls = []
def redraw_window():
window.fill(black)
left_paddle.draw(window)
right_paddle.draw(window)
for ball in balls:
ball.draw(window)
player_A_text = small_font.render("Player A: " + str(score_A), 1, white)
player_B_text = small_font.render("Player B: " + str(score_B), 1, white)
window.blit(player_A_text, (320 - int(player_A_text.get_width() / 2), 10))
window.blit(player_B_text, (480 - int(player_B_text.get_width() / 2), 10))
pygame.draw.rect(window, white, (20, 450, 760, 1), 0)
pygame.draw.rect(window, white, (20, 49, 760, 1), 0)
pygame.draw.rect(window, white, (19, 50, 1, 400), 0)
pygame.draw.rect(window, white, (780, 50, 1, 400), 0)
pygame.display.update()
while run:
score_A = 0
score_B = 0
clock.tick(fps)
if len(balls) == 0:
ball = Ball(random.randrange(320, 465), random.randrange(200, 285), 15, 3, white)
balls.append(ball)
if ball.lower_left:
ball.move_lower_left()
if ball.last_movement == 'ball.lower_right':
if ball.y + ball.side > HEIGHT - 50:
ball.lower_left = False
ball.last_movement = 'ball.lower_left'
ball.upper_left = True
if ball.last_movement == 'ball.upper_left':
if ball.x < 30:
if left_paddle.x < ball.x < left_paddle.x + left_paddle.width:
if left_paddle.y < ball.y + ball.side < left_paddle.y + left_paddle.height:
ball.lower_left = False
ball.last_movement = 'ball.lower_left'
ball.lower_right = True
else:
score_B += 1
balls.remove(ball)
#ball.start()
if ball.y + ball.side > HEIGHT - 50:
ball.lower_left = False
ball.last_movement = 'ball.lower_left'
ball.upper_left = True
if ball.upper_left:
ball.move_upper_left()
if ball.last_movement == 'ball.lower_left':
if ball.x < 30:
if left_paddle.x < ball.x < left_paddle.x + left_paddle.width:
if left_paddle.y < ball.y + ball.side < left_paddle.y + left_paddle.height:
ball.upper_left = False
ball.last_movement = 'ball.upper_left'
ball.upper_right = True
else:
score_B += 1
balls.remove(ball)
#ball.start()
if ball.y < 50:
ball.upper_left = False
ball.last_movement = 'ball.upper_left'
ball.lower_left = True
if ball.last_movement == 'ball.upper_right':
if ball.y < 50:
ball.upper_left = False
ball.last_movement = 'ball.upper_left'
ball.lower_left = True
if ball.upper_right:
ball.move_upper_right()
if ball.last_movement == 'ball.upper_left':
if ball.y < 50:
ball.upper_right = False
ball.last_movement = 'ball.upper_right'
ball.lower_right = True
if ball.last_movement == 'ball.lower_right':
if ball.x + ball.side > WIDTH - 30:
if right_paddle.x + right_paddle.width > ball.x + ball.side > right_paddle.x:
if right_paddle.y < ball.y + ball.side < right_paddle.y + right_paddle.height:
ball.upper_right = False
ball.last_movement = 'ball.upper_right'
ball.upper_left = True
else:
score_A += 1
balls.remove(ball)
#ball.start()
if ball.y < 50:
ball.upper_right = False
ball.last_movement = 'ball.upper_right'
ball.lower_right = True
if ball.lower_right:
ball.move_lower_right()
if ball.last_movement == 'ball.upper_right':
if ball.y + ball.side > HEIGHT - 50:
ball.lower_right = False
ball.last_movement = 'ball.lower_right'
ball.upper_right = True
if ball.x + ball.side > WIDTH - 30:
if right_paddle.x + right_paddle.width > ball.x + ball.side > right_paddle.x:
if right_paddle.y < ball.y + ball.side < right_paddle.y + right_paddle.height:
ball.lower_right = False
ball.last_movement = 'ball.lower_right'
ball.lower_left = True
else:
score_A += 1
balls.remove(ball)
#ball.start()
if ball.last_movement == 'ball.lower_left':
if ball.y + ball.side > HEIGHT - 50:
ball.lower_right = False
ball.last_movement = 'ball.lower_right'
ball.upper_right = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_UP] and right_paddle.y > 50:
right_paddle.y -= right_paddle.vel
if keys[pygame.K_w] and left_paddle.y > 50:
left_paddle.y -= left_paddle.vel
if keys[pygame.K_DOWN] and right_paddle.y + right_paddle.height < HEIGHT - 50:
right_paddle.y += right_paddle.vel
if keys[pygame.K_s] and left_paddle.y + left_paddle.height < HEIGHT - 50:
left_paddle.y += left_paddle.vel
if keys[pygame.K_SPACE]:
pass
redraw_window()
quit()
def main_menu():
run = True
play_button = Button(green, 100, 350, 150, 75, "Play Pong")
quit_button = Button(red, 550, 350, 150, 75, "Quit")
pong_text = large_font.render("Let's Play Pong!!!", 1, black)
while run:
window.fill(white)
play_button.draw(window, black)
quit_button.draw(window, black)
window.blit(pong_text, (int(WIDTH / 2 - pong_text.get_width() / 2), 100))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
quit()
if event.type == pygame.MOUSEMOTION:
if play_button.hover(pygame.mouse.get_pos()):
play_button.color = (0, 200, 0)
else:
play_button.color = green
if quit_button.hover(pygame.mouse.get_pos()):
quit_button.color = (200, 0, 0)
else:
quit_button.color = red
if event.type == pygame.MOUSEBUTTONDOWN:
if play_button.hover(pygame.mouse.get_pos()):
main()
if quit_button.hover(pygame.mouse.get_pos()):
run = False
quit()
main_menu()
谢谢!!!
回答
- Every time the ball bounces past the edge, the score increments for a split second, and it goes back to 0 every time the ball respawns.
分数在主循环中不断初始化。你必须在循环之前初始化分数:
def main():
# [...]
score_A = 0 # <--- INSERT
score_B = 0
while run:
# score_A = 0 <--- DELETE
# score_B = 0
I basically coded every possible movement of the ball when it bounces on the edge. I spent hours working on it, and I got it to work, I was just wondering if there was a more efficient way to produce a similar output with my code (coz I know this is supposed to be a beginner project)?
和
How can I make the ball spawn at random directions? at the start (and restart) of every round?
您可以使用浮动坐标,而不是实现球的“每个”方向。这些变量通常称为 dx 和 dy。 通过这种方式为您的球获得随机或反向方向很简单,只需为 dx 和 dy 使用随机或反向值即可。
您的球的更新应该如下所示:
def update(self. dt):
self.x += self.dx * self.speed * time_elapsed
self.y += self.dy * self.speed * time_elapsed # Time elasped is often called dt.
Every time the ball bounces past the edge, the score increments for a split second, and it goes back to 0 every time the ball respawns.
查看 Rabid76 的回答。理想情况下,您应该有一个 GameState 对象,其中包含分数、生命和其他内容作为属性。
问题 1 的答案:
是的。肯定有很多更有效的方法来做事。没有必要为方向做那么多变数。简单地说direction = [True, False]
,其中direction[0]
表示左,not direction[0]
表示x-axis中的右。同样,direction[1]
代表y轴。这也解决了您在开始时随机化方向的问题。你可以简单地在你的 init 方法中做 direction = [random.randint(0, 1), random.randint(0, 1)]
来随机化方向。以同样的方式,也列出速度。 self.speed = [0.5, random.uniform(0.1, 1)]
。这样,左右方向的速度将始终相同,但 y 将根据所选的随机数而变化,因此具有适当的随机性,您也不必硬编码随机方向。这样,移动变得非常简单。
class Ball:
def __init__(self, x, y, color, size):
self.x = x
self.y = y
self.color = color
self.size = size
self.direction = [random.randint(0, 1), random.randint(0, 1)]
self.speed = [0.5, random.uniform(0.1, 1)]
def draw(self, display):
pygame.draw.rect(display, self.color, (self.x, self.y, self.size, self.size))
def move(self):
if self.direction[0]:
self.x += self.speed[0]
else:
self.x -= self.speed[0]
if self.direction[1]:
self.y += self.speed[0]
else:
self.y -= self.speed[0]
因为我们定义的方向是布尔值,改变状态也变得非常容易。如果球击中球拍,您只需切换 x 中的布尔值 direction[0] = not direction[0]
并为 y 选择一个新的随机数,而不是手动分配布尔值。
def switchDirection(self):
self.direction = not self.direction
self.speed[1] = random.uniform(0.1, 1)
Paddle 也可以稍微改进,通过给 Paddle class 一个 move
函数而不是在主循环中移动。这只是意味着您必须编写更少的代码。
def move(self, vel, up=pygame.K_UP, down=pygame.K_DOWN):
keys = pygame.key.get_perssed()
if keys[up]:
self.y -= vel
if keys[down]:
self.y += vel
对于碰撞,我建议使用 pygame.Rect()
和 colliderect
,因为它更健壮,也可能更高效。
示例:
import random
import pygame
WIN = pygame.display
D = WIN.set_mode((800, 500))
class Paddle:
def __init__(self, x, y, width, height, vel, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = vel
self.color = color
def move(self, vel, up=pygame.K_UP, down=pygame.K_DOWN):
keys = pygame.key.get_pressed()
if keys[up]:
self.y -= vel
if keys[down]:
self.y += vel
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height), 0)
def getRect(self):
return pygame.Rect(self.x, self.y, self.width, self.height)
left_paddle = Paddle(20, 100, 10, 50, 5, (0, 0, 0))
right_paddle = Paddle(770, 350, 10, 50, 5, (0, 0, 0))
class Ball:
def __init__(self, x, y, color, size):
self.x = x
self.y = y
self.color = color
self.size = size
self.direction = [random.randint(0, 1), random.randint(0, 1)]
self.speed = [0.3, random.uniform(0.2, 0.2)]
def draw(self, window):
pygame.draw.rect(window, self.color, (self.x, self.y, self.size, self.size))
def switchDirection(self):
self.direction[0] = not self.direction[0]
self.direction[1] = not self.direction[1]
self.speed = [0.2, random.uniform(0.1, 0.5)]
def bounce(self):
self.direction[1] = not self.direction[1]
self.speed = [0.2, random.uniform(0.01, 0.2)]
def move(self):
if self.direction[0]:
self.x += self.speed[0]
else:
self.x -= self.speed[0]
if self.direction[1]:
self.y += self.speed[1]
else:
self.y -= self.speed[1]
def getRect(self):
return pygame.Rect(self.x, self.y, self.size, self.size)
def boundaries(self):
if ball.x <= 10:
self.switchDirection()
if ball.x + self.size >= 800:
self.switchDirection()
if ball.y + self.size >= 490:
self.bounce()
if ball.y <= 0:
self.bounce()
ball = Ball(400, 250, (255, 0, 0), 20)
while True:
pygame.event.get()
D.fill((255, 255, 255))
ball.draw(D)
ball.boundaries()
ball.move()
#print(ball.x, ball.y)
left_paddle.draw(D)
right_paddle.draw(D)
right_paddle.move(0.4)
left_paddle.move(0.4, down=pygame.K_s, up=pygame.K_w)
if left_paddle.getRect().colliderect(ball.getRect()):
ball.switchDirection()
if right_paddle.getRect().colliderect(ball.getRect()):
ball.switchDirection()
WIN.flip()
一些关于如何减少所有球 movement/collision 代码并使事情更可重用的思考:
import pygame
class Ball:
def __init__(self, bounds, color):
from random import randint, choice
self.bounds = bounds
self.position = pygame.math.Vector2(
randint(self.bounds.left, self.bounds.left+self.bounds.width),
randint(self.bounds.top, self.bounds.top+self.bounds.height)
)
self.velocity = pygame.math.Vector2(choice((-1, 1)), choice((-1, 1)))
self.color = color
self.size = 8
def draw(self, window):
pygame.draw.rect(
window,
self.color,
(
self.position.x-self.size,
self.position.y-self.size,
self.size*2,
self.size*2
),
0
)
def update(self):
self.position.x += self.velocity.x
self.position.y += self.velocity.y
if not self.bounds.left+self.size < self.position.x < self.bounds.left+self.bounds.width-self.size:
self.velocity.x *= -1
if not self.bounds.top+self.size < self.position.y < self.bounds.top+self.bounds.height-self.size:
self.velocity.y *= -1
def main():
from random import randint
window_width, window_height = 800, 500
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Pong")
clock = pygame.time.Clock()
black = (0, 0, 0)
white = (255, 255, 255)
padding = 20
bounds = pygame.Rect(padding, padding, window_width-(padding*2), window_height-(padding*2))
ball = Ball(bounds, white)
def redraw_window():
window.fill(black)
pygame.draw.rect(
window,
white,
(
padding,
padding,
window_width-(padding*2),
window_height-(padding*2)
),
1
)
ball.draw(window)
pygame.display.update()
while True:
clock.tick(60)
ball.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
break
else:
redraw_window()
continue
break
pygame.quit()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
这不是一个完整的 drop-in 替换,我刚刚重新实现了 Ball
class。没有桨。本质上,当实例化一个球时,您传入一个 pygame.Rect
,它描述了允许球弹跳的范围。您还传入了一个颜色元组。然后球在边界内选择一个随机位置(该位置是 pygame.math.Vector2
,而不是将 x
和 y
存储为单独的实例变量)。一个球也有一个速度,它也是一个 pygame.math.Vector2
,所以你可能有独立的速度分量 - 一个用于 x
(水平速度),一个用于 y
(垂直速度)。球的 size
简单地描述了球的尺寸。例如,如果 size
设置为 8
,则球将为 16x16 像素。
Ball
class也有一个update
方法,每game-loop次迭代调用一次。它将球移动到速度决定的下一个位置,并检查球是否与边界发生碰撞。如果是,则取反相应的速度分量。