如何避免 PyGame 中圆形和矩形之间的小故障碰撞? [英] How do I avoid an glitchy collision between circle and rectangle in PyGame?

查看:70
本文介绍了如何避免 PyGame 中圆形和矩形之间的小故障碰撞?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

乒乓球拍移动得如此之快,以至于在检测到碰撞之前,球就在球拍内卷起.问题是用户输入将桨移动了一个像素,所以我不知道如何减慢它的速度.什么是修复?代码如下:

导入pygame、sys、os从 pygame 导入*从 pygame.locals 导入*白色 = (255, 255, 255)绿色 = (0, 255, 0)蓝色 = (0, 0, 128)红色 = (255,0,0)os.environ["SDL_VIDEO_CENTERED"]="1"显示大小=600DISPLAYSURF = pygame.display.set_mode((displaysize,displaysize))矩形宽度 = 50矩形高度= 50rectposx =0rectposy =0类播放器(对象):def __init__(self):self.rect = pygame.rect.Rect((rectposx, rectposy, rectwidth, rectheight))def handle_keys(self):键 = pygame.key.get_pressed()距离 = 1如果 key[pygame.K_LEFT] 和 (player.rect.x>0):self.rect.move_ip(-1, 0)如果 key[pygame.K_RIGHT] 和 (player.rect.x<600-rectwidth):self.rect.move_ip(1, 0)如果 key[pygame.K_UP] 和 (player.rect.y>0):self.rect.move_ip(0, -1)如果 key[pygame.K_DOWN] 和 (player.rect.y<600-rectheight):self.rect.move_ip(0, 1)def draw(self, DISPLAYSURF):pygame.draw.rect(DISPLAYSURF, BLUE, self.rect)定义后文本(自我):pygame.image.load(self.rect).convert_alpha()pygame.init()玩家 = 玩家()pygame.display.set_caption('Hello World!')时钟=pygame.time.Clock()fontObj = pygame.font.Font(None,32)textSurfaceObj = fontObj.render('Hello World!', True, GREEN, BLUE)#textPosition =dt=0.1v = pygame.math.Vector2(5,5)ballposx=200球花=200球拉德=10#DISPLAYSURF.fill(白色)#x=10#y=10#dx=5#rectpos = pygame.Rect(x,y,50,50)#rect = pygame.draw.rect(DISPLAYSURF, BLUE, rectpos)pygame.display.update()运行 = 真n=0在跑步的时候:对于 pygame.event.get() 中的事件:如果 event.type == KEYDOWN:如果 event.key == K_ESCAPE:运行 = 错误如果 event.type==QUIT:pygame.quit()系统退出player.handle_keys()ballposx=ballposx+v[0]*dtballposy=ballposy+v[1]*dtDISPLAYSURF.fill(白色)DISPLAYSURF.blit(textSurfaceObj,(0,0))player.draw(DISPLAYSURF)ball=pygame.draw.circle(DISPLAYSURF, GREEN, (int(ballposx),int(ballposy)), ballrad)rectposx1=player.rect.xrectposy1=player.rect.yrectvelx=-(rectposx-rectposx1)/dtrectvely=-(rectposy-rectposy1)/dt如果ballposx-ballrad<0:v[0]=-v[0]如果ballposy-ballrad<0:v[1]=-v[1]如果ballposx+ballrad>600:v[0]=-v[0]如果 ballposy+ballrad>600:v[1]=-v[1]如果 player.rect.colliderect(ball):pygame.math.Vector2.reflect_ip(v,-v+5*pygame.math.Vector2(rectvelx,rectvely))#print (player.rect.x, rectposy, ball.x, ball.y)球质量=1矩形质量=5rectposx=rectposx1rectposy=rectposy1打印(五)#raise SystemExit("你赢了!")pygame.display.update()时钟滴答(120)

解决方案

有两种策略可以解决这个问题.

  1. 以某种方式移动球,使其在检测到碰撞后接触玩家但不与玩家相交.例如:

    dx = ballposx - player.rect.centerxdy = ballposy - player.rect.centery如果 abs(dx) >绝对(dy):ballposx = player.rect.left-ballrad 如果 dx <0 else player.rect.right+ballrad别的:ballposy = player.rect.top-ballrad 如果 dy <0 其他 player.rect.bottom+ballrad

  2. 仅当球的运动矢量指向反对"方向时才反映球的运动.球.例如:

    if abs(dx) >绝对(dy):如果 (dx < 0 and v[0] > 0) or (dx > 0 and v[0] < 0):v.reflect_ip(pygame.math.Vector2(1, 0))别的:如果 (dy < 0 and v[1] > 0) or (dy > 0 and v[1] < 0):v.reflect_ip(pygame.math.Vector2(0, 1))

另见

ball = pygame.Rect((0,0), (ballrad*2, ballrad*2))ball.center = int(ballposx),int(ballposy)如果 player.rect.colliderect(ball):dx = ballposx - player.rect.centerxdy = ballposy - player.rect.centery如果 abs(dx) >绝对(dy):ballposx = player.rect.left-ballrad 如果 dx <0 else player.rect.right+ballrad如果 (dx < 0 and v[0] > 0) or (dx > 0 and v[0] < 0):v.reflect_ip(pygame.math.Vector2(1, 0))别的:ballposy = player.rect.top-ballrad 如果 dy <0 其他 player.rect.bottom+ballrad如果 (dy < 0 and v[1] > 0) or (dy > 0 and v[1] < 0):v.reflect_ip(pygame.math.Vector2(0, 1))


如果您想避免玩家将球推出窗外,您需要将球限制在窗外区域,并像台球一样将球反射到窗外:

min_x, min_y, max_x, max_y = 0, 0, window.get_width(), window.get_height()ballposx = ballposx + v[0]*dtballposy = ballposy + v[1]*dt如果 ballposx-ballrad 最大_x:ballposx = max_x-ballradv[0]=-v[0]如果 ballposy + ballrad >max_y:ballposy = max_y-ballradv[1]=-v[1]

另见

最小示例:

导入pygame类播放器(对象):def __init__(self, x, y, w, h):self.rect = pygame.rect.Rect(x, y, w, h)def handle_keys(self):键 = pygame.key.get_pressed()如果键[pygame.K_LEFT]:self.rect.left = max(20, self.rect.left - 1)如果键[pygame.K_RIGHT]:self.rect.right = min(window.get_height() - 20, self.rect.right + 1)如果键[pygame.K_UP]:self.rect.top = max(20, self.rect.top - 1)如果键[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()窗口 = pygame.display.set_mode((240, 240))时钟=pygame.time.Clock()玩家 = 玩家(20, 20, 50, 50)v, vel = pygame.math.Vector2(1, 1), 0.5ballPosX, ballPosY, ballRadius = 120, 120, 10运行 = 真运行时:时钟滴答(120)对于 pygame.event.get() 中的事件:如果 event.type==pygame.QUIT:运行 = 错误player.handle_keys()min_x, min_y, max_x, max_y = 20, 20, window.get_width()-20, window.get_height()-20ballPosX += v[0] * velballPosY += v[1] * vel如果 ballPosX - ballRadius 最大_x:ballPosX = max_x - ballRadiusv[0] = -v[0]如果 ballPosY + ballRadius >max_y:ballPosY = max_y - ballRadiusv[1] = -v[1]ball = pygame.Rect((0,0), (ballRadius*2, ballRadius*2))ball.center = int(ballPosX),int(ballPosY)如果 player.rect.colliderect(ball):dx = ballPosX - player.rect.centerxdy = ballPosY - player.rect.centery如果 abs(dx) >绝对(dy):如果 dx <0:ballPosX = max(player.rect.left-ballRadius, ballRadius+min_x)player.rect.left = int(ballPosX)+ballRadius别的:ballPosX = min(player.rect.right+ballRadius, max_x-ballRadius)player.rect.right = int(ballPosX)-ballRadius如果(dx<0并且v[0]>0)或(dx>0并且v[0]<0):v.reflect_ip(pygame.math.Vector2(1, 0))别的:如果 dy <0:ballPosY = max(player.rect.top-ballRadius, ballRadius+min_y)player.rect.top = int(ballPosY)+ballRadius别的:ballPosY = min(player.rect.bottom+ballRadius, max_y-ballRadius)player.rect.bottom = int(ballPosY)-ballRadiusballPosY = player.rect.top-ballRadius 如果 dy <0 其他 player.rect.bottom+ballRadius如果 (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(窗口)pygame.draw.circle(window, (0, 255, 0), (round(ballPosX), round(ballPosY)), ballRadius)pygame.display.update()pygame.quit()出口()

The pong paddle moves so fast that the ball winds up inside the paddle before the collision is detected. The problem is the user input moves the paddle by a single pixel so I don't know how to slow it down. What is the fix? Here is the code:

import pygame, sys, os
from pygame import*
from pygame.locals import*

WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)
RED = (255,0,0)
os.environ["SDL_VIDEO_CENTERED"]="1"
displaysize=600
DISPLAYSURF = pygame.display.set_mode((displaysize,displaysize))
rectwidth = 50
rectheight= 50
rectposx =0
rectposy =0

class Player(object):
    def __init__(self):
        self.rect = pygame.rect.Rect((rectposx, rectposy, rectwidth, rectheight))
    def handle_keys(self):
        key = pygame.key.get_pressed()
        dist = 1
        if key[pygame.K_LEFT] and (player.rect.x>0):
            self.rect.move_ip(-1, 0)
        if key[pygame.K_RIGHT] and (player.rect.x<600-rectwidth):
            self.rect.move_ip(1, 0)
        if key[pygame.K_UP] and (player.rect.y>0):
            self.rect.move_ip(0, -1)
        if key[pygame.K_DOWN] and (player.rect.y<600-rectheight):
            self.rect.move_ip(0, 1)
    def draw(self, DISPLAYSURF):
        pygame.draw.rect(DISPLAYSURF, BLUE, self.rect)
    def postext(self):
        pygame.image.load(self.rect).convert_alpha()

pygame.init()
player =Player()
pygame.display.set_caption('Hello World!')

clock=pygame.time.Clock()
fontObj = pygame.font.Font(None,32)
textSurfaceObj = fontObj.render('Hello World!', True, GREEN, BLUE)
#textPosition =
dt=0.1
v = pygame.math.Vector2(5,5)
ballposx=200
ballposy=200
ballrad=10
#DISPLAYSURF.fill(WHITE)
#x=10
#y=10
#dx=5
#rectpos = pygame.Rect(x,y,50,50)
#rect = pygame.draw.rect(DISPLAYSURF, BLUE, rectpos)
pygame.display.update()
running = True
n=0
while running:
    for event in pygame.event.get():
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
        if event.type==QUIT:
            pygame.quit()
            sys.exit
    player.handle_keys()
    ballposx=ballposx+v[0]*dt
    ballposy=ballposy+v[1]*dt
    DISPLAYSURF.fill(WHITE)
    DISPLAYSURF.blit(textSurfaceObj,(0,0))
    player.draw(DISPLAYSURF)
    ball=pygame.draw.circle(DISPLAYSURF, GREEN, (int(ballposx),int(ballposy)), ballrad)
    rectposx1=player.rect.x
    rectposy1=player.rect.y
    rectvelx=-(rectposx-rectposx1)/dt
    rectvely=-(rectposy-rectposy1)/dt
    if ballposx-ballrad<0:
        v[0]=-v[0]
    if ballposy-ballrad<0:
        v[1]=-v[1]
    if ballposx+ballrad>600:
        v[0]=-v[0]
    if ballposy+ballrad>600:
        v[1]=-v[1]
    if player.rect.colliderect(ball):
        pygame.math.Vector2.reflect_ip(v,-v+5*pygame.math.Vector2(rectvelx,rectvely)) 

    #print (player.rect.x, rectposy, ball.x, ball.y)
    ballmass=1
    rectmass=5
    rectposx=rectposx1
    rectposy=rectposy1
    print (v)
            #raise SystemExit("You win!")
    pygame.display.update()
    clock.tick(120)

解决方案

There are 2 strategies to a void that.

  1. Move the ball in the way, that it is touching the player but not intersecting the player once a collision is detected. e.g.:

    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
    

  2. Reflect the movement of the ball only if its movement vector points in a direction "against" the ball. e.g.:

    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))
    

See also Sometimes the ball doesn't bounce off the paddle in pong game

Applying these 2 fixes to your code the ball will reflect properly on the player. e.g.:

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))


If you want to avoid the player pushing the ball out of the window, you need to restrict the ball to the window area and reflect the ball off the edges of the window like a pool ball:

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]

See also Use vector2 in pygame. Collide with the window frame and restrict the ball to the rectangular area respectively How to make ball bounce off wall with Pygame?.

When a collision is detected, the player's position must be restricted so that the ball can take place between the widow boundary and the player:

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

With these changes, the ball can even be "squeezed" between the edge of the window and the player:

Minimal example:

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()

这篇关于如何避免 PyGame 中圆形和矩形之间的小故障碰撞?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆