pygame,检测旋转矩形的碰撞 [英] pygame, detecting collision of a rotating rectangle

查看:97
本文介绍了pygame,检测旋转矩形的碰撞的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我绘制了一个旋转的矩形,我需要检查它是否发生碰撞.全班:

类激光:def __init__(self, player_x, player_y):self.x = player_xself.y = player_yself.original_image = pygame.Surface((2, 1000))self.original_image.set_colorkey( (0,0,0) )self.original_image.fill((255,0,0))self.copy_image = self.original_image.copy()self.copy_image.set_colorkey( (0,0,0) )self.rect = self.copy_image.get_rect()self.new_image = pygame.Surface((2, 1000))self.angle = 0def continueDrawLaser(self):如果laser_bool:screen.blit(self.new_image,self.rect)def旋转(自我):# 获取玩家和激光的矩形,就好像角度是 0player_rect = player1.original_player_image.get_rect(topleft=(player1.x, player1.y))Laser_rect = self.original_image.get_rect(midbottom=player_rect.midtop)self.angle = player1.anglepivotPos = [player_rect.centerx - Laser_rect.x, player_rect.centery - Laser_rect.y]# 计算旋转图像的轴对齐边界框w, h = self.original_image.get_size()box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]box_rotate = [p.rotate(self.angle) for p in box]min_box = (min(box_rotate, key=lambda p: p[0])[0], min(box_rotate, key=lambda p: p[1])[1])max_box = (max(box_rotate, key=lambda p: p[0])[0], max(box_rotate, key=lambda p: p[1])[1])# 计算枢轴的平移枢轴 = pygame.math.Vector2(pivotPos[0], -pivotPos[1])pivot_rotate = pivot.rotate(self.angle)枢轴移动 = 枢轴旋转 - 枢轴# 计算旋转图像的左上角原点origin = (laser_rect.x + min_box[0] - pivot_move[0],laser_rect.y - max_box[1] + pivot_move[1]) #x,y# 获取旋转图像self.new_image = pygame.transform.rotate(self.original_image, self.angle)# 获取新的矩形self.rect = self.new_image.get_rect(topleft=origin)

这是碰撞函数:

#检查岩石是否与激光碰撞定义碰撞RockLaser(自我,laser1):激光矩形 = 激光 1.rectRock_rect = pygame.Rect(self.x,self.y,rock_width,rock_height)如果laser_rect.colliderect(rock_rect):岩石.流行(岩石.索引(岩石2))总分得分 += 1

这就是我得到的:

我认为通过 self.rect 就足够了,因为它每次都更新旋转位置,以检测碰撞,但是似乎我必须使用分离轴定理,你能帮我吗?

解决方案

一种选择是创建与直线和矩形相交的算法.

首先创建一个与两条线段相交的算法:

P ... 点在 1. 线上R ... 1. 线的归一化方向Q ... 点在 2. 线上S ... 2. 线的归一化方向alpha ... Q-P 和 R 之间的角度beta ... R 和 S 之间的角度X ...交点t ... P 和 X 之间的距离u ... Q 和 X 之间的距离伽玛 = 180° - 阿尔法 - 贝塔t = |Q-P |* sin(gamma)/sin(beta)你 = |Q-P |* sin(alpha)/sin(beta)t = 点(Q-P,(S.y,-S.x))/点(R,(S.y,-S.x))=行列式(mat2(Q-P,S))/行列式(mat2(R,S))u = 点(Q-P,(R.y,-R.x))/点(R,(S.y,-S.x))=行列式(mat2(Q-P,R))/行列式(mat2(R,S))X = P + R * t = Q + S * u

算法详细解释,在回答

I have a rectangle drawn that rotate and I need to check if it collides. The entire class:

class Laser:
def __init__(self, player_x, player_y):
    self.x = player_x
    self.y = player_y
    self.original_image = pygame.Surface((2, 1000))
    self.original_image.set_colorkey( (0,0,0) )
    self.original_image.fill( (255,0,0) )
    self.copy_image = self.original_image.copy()
    self.copy_image.set_colorkey( (0,0,0) )
    self.rect = self.copy_image.get_rect()
    self.new_image = pygame.Surface((2, 1000))
    self.angle = 0

def continueDrawLaser(self):
    if laser_bool:
        screen.blit(self.new_image, self.rect)

def rotate(self):
    # get rectangle of player and laser, as if the angle would be 0
    player_rect = player1.original_player_image.get_rect(topleft=(player1.x, player1.y))
    laser_rect = self.original_image.get_rect(midbottom=player_rect.midtop)
    self.angle = player1.angle
    pivotPos = [player_rect.centerx - laser_rect.x, player_rect.centery - laser_rect.y]

    # calcaulate the axis aligned bounding box of the rotated image
    w, h = self.original_image.get_size()
    box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
    box_rotate = [p.rotate(self.angle) for p in box]
    min_box = (min(box_rotate, key=lambda p: p[0])[0], min(box_rotate, key=lambda p: p[1])[1])
    max_box = (max(box_rotate, key=lambda p: p[0])[0], max(box_rotate, key=lambda p: p[1])[1])

    # calculate the translation of the pivot
    pivot = pygame.math.Vector2(pivotPos[0], -pivotPos[1])
    pivot_rotate = pivot.rotate(self.angle)
    pivot_move = pivot_rotate - pivot

    # calculate the upper left origin of the rotated image
    origin = (laser_rect.x + min_box[0] - pivot_move[0], laser_rect.y - max_box[1] + pivot_move[1])  #x,y

    # get a rotated image
    self.new_image = pygame.transform.rotate(self.original_image, self.angle)

    # get new rectangle
    self.rect = self.new_image.get_rect(topleft=origin)

This is the collision func:

#check if rock collides with laser
def collisionRockLaser(self, laser1):
    laser_rect = laser1.rect
    rock_rect = pygame.Rect(self.x, self.y, rock_width, rock_height)
    if laser_rect.colliderect(rock_rect):
        rocks.pop(rocks.index(rock2))
        global score
        score += 1

And this is what I get:

I thought it was enough pass self.rect,since it updates with the rotated position each time,to detect collisions,however it seems that I have to use the Separating Axis Theorem,could you help me?

解决方案

One option is to create and algorithm which intersects a line and a rectangle.

First create an algorithm which intersects 2 line segments:

P     ... point on the 1. line
R     ... normalized direction of the 1. line

Q     ... point on the 2. line
S     ... normalized direction of the 2. line

alpha ... angle between Q-P and R
beta  ... angle between R and S

X     ... intersection point
t     ... distance between P and X
u     ... distance between Q and X

gamma  =  180° - alpha - beta

t  = | Q - P | * sin(gamma) / sin(beta)
u  = | Q - P | * sin(alpha) / sin(beta)

t  =  dot(Q-P, (S.y, -S.x)) / dot(R, (S.y, -S.x))  =  determinant(mat2(Q-P, S)) / determinant(mat2(R, S))
u  =  dot(Q-P, (R.y, -R.x)) / dot(R, (S.y, -S.x))  =  determinant(mat2(Q-P, R)) / determinant(mat2(R, S))

X  =  P + R * t  =  Q + S * u

The algorithm is explained in detail, in the answer to Problem with calculating line intersections

def collideLineLine(P0, P1, Q0, Q1):  
    d = (P1[0]-P0[0]) * (Q1[1]-Q0[1]) + (P1[1]-P0[1]) * (Q0[0]-Q1[0]) 
    if d == 0:
        return False
    t = ((Q0[0]-P0[0]) * (Q1[1]-Q0[1]) + (Q0[1]-P0[1]) * (Q0[0]-Q1[0])) / d
    u = ((Q0[0]-P0[0]) * (P1[1]-P0[1]) + (Q0[1]-P0[1]) * (P0[0]-P1[0])) / d
    return 0 <= t <= 1 and 0 <= u <= 1

To test if the line segment intersects the rectangle, you have to test if it intersect any of the 4 sides of the rectangle:

def colideRectLine(rect, p1, p2):

    return (collideLineLine(p1, p2, rect.topleft, rect.bottomleft) or
            collideLineLine(p1, p2, rect.bottomleft, rect.bottomright) or
            collideLineLine(p1, p2, rect.bottomright, rect.topright) or
            collideLineLine(p1, p2, rect.topright, rect.topleft))

Setup a line along the laser, dependent on the angle:

angle = player1.angle
if angle < 0:
    angle += 360

if angle < 90 or (angle > 180 and angle < 270):
    laserline = [laser1.rect.topleft, laser1.rect.bottomright]
else:
    laserline = [laser1.rect.bottomleft, laser1.rect.topright]

Evaluate in the line intersects a pygame.Rect enemy_rect:

collide = colideRectLine(enemy_rect, *laserline)

这篇关于pygame,检测旋转矩形的碰撞的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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