<p>有两种策略可以消除这种空虚感</p>
<ol>
<li><p>在检测到碰撞后,将球移动到与球员接触但不与球员相交的位置。e、 g:</p>
<pre class="lang-py prettyprint-override"><code>dx = ballposx - player.rect.centerx
dy = ballposy - player.rect.centery
if abs(dx) > abs(dy):
ballposx = player.rect.left-ballrad if dx < 0 else player.rect.right+ballrad
else:
ballposy = player.rect.top-ballrad if dy < 0 else player.rect.bottom+ballrad
</code></pre>
</li>
<li><p>仅当球的运动矢量指向球的“反”方向时,才反映球的运动。e、 g:</p>
<pre class="lang-py prettyprint-override"><code>if abs(dx) > abs(dy):
if (dx < 0 and v[0] > 0) or (dx > 0 and v[0] < 0):
v.reflect_ip(pygame.math.Vector2(1, 0))
else:
if (dy < 0 and v[1] > 0) or (dy > 0 and v[1] < 0):
v.reflect_ip(pygame.math.Vector2(0, 1))
</code></pre>
</li>
</ol>
<p>另见<a href="https://stackoverflow.com/questions/62864205/sometimes-the-ball-doesnt-bounce-off-the-paddle-in-pong-game">Sometimes the ball doesn't bounce off the paddle in pong game</a></p>
<p>将这两个修正应用到代码中,球将正确地反映在球员身上。e、 g:</p>
<p><a href="https://i.stack.imgur.com/ekmV6.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ekmV6.gif" alt=""/></a></p>
<pre class="lang-py prettyprint-override"><code>ball = pygame.Rect((0,0), (ballrad*2, ballrad*2))
ball.center = int(ballposx),int(ballposy)
if player.rect.colliderect(ball):
dx = ballposx - player.rect.centerx
dy = ballposy - player.rect.centery
if abs(dx) > abs(dy):
ballposx = player.rect.left-ballrad if dx < 0 else player.rect.right+ballrad
if (dx < 0 and v[0] > 0) or (dx > 0 and v[0] < 0):
v.reflect_ip(pygame.math.Vector2(1, 0))
else:
ballposy = player.rect.top-ballrad if dy < 0 else player.rect.bottom+ballrad
if (dy < 0 and v[1] > 0) or (dy > 0 and v[1] < 0):
v.reflect_ip(pygame.math.Vector2(0, 1))
</code></pre>
<hr/>
<p>如果要避免球员将球推出窗外,需要将球限制在窗口区域,并将球像台球一样反射到窗口边缘:</p>
<pre class="lang-py prettyprint-override"><code>min_x, min_y, max_x, max_y = 0, 0, window.get_width(), window.get_height()
ballposx = ballposx + v[0]*dt
ballposy = ballposy + v[1]*dt
if ballposx-ballrad < min_x:
ballposx = ballrad+min_x
v[0]=-v[0]
if ballposy-ballrad < min_y:
ballposy = ballrad+min_y
v[1]=-v[1]
if ballposx + ballrad > max_x:
ballposx = max_x-ballrad
v[0]=-v[0]
if ballposy + ballrad > max_y:
ballposy = max_y-ballrad
v[1]=-v[1]
</code></pre>
<p>另见<a href="https://stackoverflow.com/questions/60213103/use-vector2-in-pygame-collide-with-the-window-frame-and-restrict-the-ball-to-th">Use vector2 in pygame. Collide with the window frame and restrict the ball to the rectangular area</a>分别<a href="https://stackoverflow.com/questions/59802794/how-to-make-ball-bounce-off-wall-with-pygame">How to make ball bounce off wall with Pygame?</a></p>
<p>当检测到碰撞时,必须限制球员的位置,以便球可以在寡妇边界和球员之间进行:</p>
<pre class="lang-py prettyprint-override"><code>if abs(dx) > abs(dy):
if dx < 0:
ballposx = max(player.rect.left-ballrad, ballrad+min_x)
player.rect.left = int(ballposx)+ballrad
else:
ballposx = min(player.rect.right+ballrad, max_x-ballrad)
player.rect.right = int(ballposx)-ballrad
</code></pre>
<p>通过这些更改,球甚至可以“挤压”在窗口边缘和球员之间:</p>
<p><a href="https://i.stack.imgur.com/30mEF.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/30mEF.gif" alt=""/></a></p>
<p>最简单的例子:</p>
<pre class="lang-py prettyprint-override"><code>import pygame
class Player(object):
def __init__(self, x, y, w, h):
self.rect = pygame.rect.Rect(x, y, w, h)
def handle_keys(self):
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.rect.left = max(20, self.rect.left - 1)
if key[pygame.K_RIGHT]:
self.rect.right = min(window.get_height() - 20, self.rect.right + 1)
if key[pygame.K_UP]:
self.rect.top = max(20, self.rect.top - 1)
if key[pygame.K_DOWN]:
self.rect.bottom = min(window.get_width() - 20, self.rect.bottom + 1)
def draw(self, surface):
pygame.draw.rect(surface, (0, 0, 128), self.rect)
pygame.init()
window = pygame.display.set_mode((240, 240))
clock=pygame.time.Clock()
player = Player(20, 20, 50, 50)
v, vel = pygame.math.Vector2(1, 1), 0.5
ballPosX, ballPosY, ballRadius = 120, 120, 10
run = True
while run:
clock.tick(120)
for event in pygame.event.get():
if event.type==pygame.QUIT:
run = False
player.handle_keys()
min_x, min_y, max_x, max_y = 20, 20, window.get_width()-20, window.get_height()-20
ballPosX += v[0] * vel
ballPosY += v[1] * vel
if ballPosX - ballRadius < min_x:
ballPosX = ballRadius + min_x
v[0] = -v[0]
if ballPosY - ballRadius < min_y:
ballPosY = ballRadius + min_y
v[1] = -v[1]
if ballPosX + ballRadius > max_x:
ballPosX = max_x - ballRadius
v[0] = -v[0]
if ballPosY + ballRadius > max_y:
ballPosY = max_y - ballRadius
v[1] = -v[1]
ball = pygame.Rect((0,0), (ballRadius*2, ballRadius*2))
ball.center = int(ballPosX),int(ballPosY)
if player.rect.colliderect(ball):
dx = ballPosX - player.rect.centerx
dy = ballPosY - player.rect.centery
if abs(dx) > abs(dy):
if dx < 0:
ballPosX = max(player.rect.left-ballRadius, ballRadius+min_x)
player.rect.left = int(ballPosX)+ballRadius
else:
ballPosX = min(player.rect.right+ballRadius, max_x-ballRadius)
player.rect.right = int(ballPosX)-ballRadius
if (dx < 0 and v[0] > 0) or (dx > 0 and v[0] < 0):
v.reflect_ip(pygame.math.Vector2(1, 0))
else:
if dy < 0:
ballPosY = max(player.rect.top-ballRadius, ballRadius+min_y)
player.rect.top = int(ballPosY)+ballRadius
else:
ballPosY = min(player.rect.bottom+ballRadius, max_y-ballRadius)
player.rect.bottom = int(ballPosY)-ballRadius
ballPosY = player.rect.top-ballRadius if dy < 0 else player.rect.bottom+ballRadius
if (dy < 0 and v[1] > 0) or (dy > 0 and v[1] < 0):
v.reflect_ip(pygame.math.Vector2(0, 1))
window.fill((255, 255, 255))
pygame.draw.rect(window, (255,0,0), (18, 18, 203, 203), 2)
player.draw(window)
pygame.draw.circle(window, (0, 255, 0), (round(ballPosX), round(ballPosY)), ballRadius)
pygame.display.update()
pygame.quit()
exit()
</code></pre>