Pygame:如何将分离轴定理(SAT)应用于旋转平方?

问题描述 投票:0回答:1

我不需要代码(我确实想要代码),但是有人可以向我解释如何创建对角线以查看是否存在间隙吗?我知道我们必须使用向量,但是我不知道如何使用python

python vector pygame collision
1个回答
-1
投票

因此,使用分离轴定理的逻辑是,如果您不能在2个正方形之间画一条线,那么它们就会重叠并发生碰撞。我制作了一个接近的东西,虽然它不是完美的,但它接近,我也没有考虑正方形的旋转,但是如果您找到一种方法来找到正方形的顶点/角,那么这很容易实现。我这样做的方法是将正方形变成直线,并在正方形的中间和正方形之间的法线处直接画了一条线,这有点令人困惑,但是一旦您看到它就有意义。然后,我使用line intersecting maths查找它们是否相交。

pygame image

import pygame
from pygame.locals import *
from pygame import Vector2

pygame.init()

screen = pygame.display.set_mode((500,500))

#check if 2 lines are intersecting
def LineIntersect(line1, line2):
    #the math is from wikipedia
    x1 = line1[0].x
    y1 = line1[0].y
    x2 = line1[1].x
    y2 = line1[1].y

    x3 = line2[0].x
    y3 = line2[0].y
    x4 = line2[1].x
    y4 = line2[1].y

    den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
    if den == 0:
        return 
    t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den

    u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den

    if t > 0 and t < 1 and u > 0 and u < 1:
        pt = Vector2()
        pt.x = x1 + t * (x2 - x1)
        pt.y = y1 + t * (y2 - y1)
        return pt
    return



#class for sqaure
class square:
    def __init__(self,x,y,w):
        self.x = x
        self.y = y
        self.w = w
        self.centerx = self.x + w//2
        self.centery = self.y + w//2
        self.col = (255,0,0)

    def Draw(self, outline = False):
        if outline:
            self.Outline()
        else:
            pygame.draw.rect(screen,self.col,(self.x,self.y,self.w,self.w))

    def Outline(self):
        for point1, point2 in self.Lines():
            pygame.draw.line(screen,sqr2.col,point1,point2,1)         

    #get the lines that make up the square, the outline/perameter
    def Lines(self):
        lines = []
        lines.append((Vector2(self.x,self.y),Vector2(self.x+self.w,self.y)))
        lines.append((Vector2(self.x,self.y),Vector2(self.x,self.y + self.w)))
        lines.append((Vector2(self.x + self.w,self.y + self.w),Vector2(self.x+self.w,self.y)))
        lines.append((Vector2(self.x + self.w,self.y + self.w),Vector2(self.x,self.y + self.w)))
        return lines

#draw a line inbetween the 2 squares
def DrawLineInBetween():
    #draw a line between the 2 squares, get gradient
    #to avoid divide by zero
    if abs(sqr1.x - sqr2.x) == 0:
        gradient = "infinity"
    else:
        #rise over run
        #left - right = run
        left = sqr1 if sqr1.x < sqr2.x else sqr2
        right = sqr1 if left == sqr2 else sqr2
        gradient = ((left.y - right.y)/abs(sqr1.x - sqr2.x))
    #print("gradient:",gradient)

    #get the middle point between the centers of the squares
    middle = (max(sqr1.x + sqr1.w//2, sqr2.x + sqr2.w//2) - abs(sqr1.x - sqr2.x)//2,
              max(sqr1.y + sqr1.w//2, sqr2.y + sqr2.w//2) - abs(sqr1.y - sqr2.y)//2)
    #to avoid divide by 0
    if gradient == 0:
        point1 = Vector2(middle[0], middle[1] + 100)
        point2 = Vector2(middle[0], middle[1] - 100)
    elif gradient == "infinity":
        point1 = Vector2(middle[0] - 100, middle[1])
        point2 = Vector2(middle[0] + 100, middle[1])        
    else:
        #get normal of line
        gradient = -1/gradient
        #print("normal:",gradient)

        point1 = Vector2(middle[0] + 100, middle[1] + int(-100 * gradient))
        point2 = Vector2(middle[0] - 100, middle[1] + int(100 * gradient))
        #print(point1)
        #print(point2)
        #print(middle)

    pygame.draw.line(screen,(0,255,0),point1,point2,1)

    line = (point1, point2)
    return line


sqr1 = square(100,100,50)
sqr2 = square(200,100,50)

Clock = pygame.time.Clock()

running = True
key = ""

while running:
    screen.fill((0,0,0))

    sqr1.Draw(outline=True)
    sqr2.Draw()
    line = DrawLineInBetween()

    for sqr_line in sqr1.Lines():
        pt = LineIntersect(line,sqr_line)
        if pt:
            pygame.draw.circle(screen,(0,255,255),(int(pt.x),int(pt.y)),5)

    if key == "s":
        sqr1.y += 1
    elif key == "w":
        sqr1.y -= 1
    if key == "d":
        sqr1.x += 1
    if key == "a":
        sqr1.x -= 1

    pygame.display.update()
    Clock.tick(60)

    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            pygame.quit()
            running = False
        if e.type == MOUSEBUTTONDOWN:
            print(e.pos)
        if e.type == KEYDOWN:
            key = e.unicode
        if e.type == KEYUP:
            key = ""

做旋转方块:在正方形类中添加了旋转变量,我用this answer来找到正方形的角,然后一旦我有了角,就使用了线插入。

这里是新班:

#class for sqaure
class square:
    def __init__(self,x,y,w):
        self.x = x
        self.y = y
        self.w = w
        self.centerx = self.x + w//2
        self.centery = self.y + w//2
        self.col = (255,0,0)
        self.rotation_angle = 0

    def Draw(self, outline = False):
        if outline:
            self.Outline()
        else:
            pygame.draw.rect(screen,self.col,(self.x,self.y,self.w,self.w))

    #this used the normal coordinate of an unrotated square to find new coordinates of rotated sqaure
    def GetCorner(self,tempX,tempY):
        angle = math.radians(self.rotation_angle)
        rotatedX = tempX*math.cos(angle) - tempY*math.sin(angle);
        rotatedY = tempX*math.sin(angle) + tempY*math.cos(angle);   

        x = rotatedX + self.centerx;
        y = rotatedY + self.centery;        

        return Vector2(x,y)      

    def Outline(self):
        for point1, point2 in self.Lines():
            pygame.draw.line(screen,sqr2.col,point1,point2,1)         

    #new lines method, only changed to GetCorner()
    def Lines(self):
        lines = []
        top_left = self.GetCorner(self.x - self.centerx, self.y - self.centery)
        top_right = self.GetCorner(self.x + self.w - self.centerx, self.y - self.centery)
        bottom_left = self.GetCorner(self.x - self.centerx, self.y + self.w - self.centery)
        bottom_right = self.GetCorner(self.x + self.w - self.centerx, self.y + self.w - self.centery)

        lines.append((top_left,top_right))
        lines.append((top_left,bottom_left))
        lines.append((bottom_right,top_right))
        lines.append((bottom_right,bottom_left))
        return lines

    #chnaged to this as rotation rotates around center, so need to update both x and centerx
    def Move(self,x =None, y = None):
        if x:
            self.x += x
            self.centerx += x
        if y:
            self.y += y
            self.centery += y

    #get the lines that make up the square, the outline/perameter
    #def Lines(self):
        #lines = []
        #lines.append((Vector2(self.x,self.y),Vector2(self.x+self.w,self.y)))
        #lines.append((Vector2(self.x,self.y),Vector2(self.x,self.y + self.w)))
        #lines.append((Vector2(self.x + self.w,self.y + self.w),Vector2(self.x+self.w,self.y)))
        #lines.append((Vector2(self.x + self.w,self.y + self.w),Vector2(self.x,self.y + self.w)))
        #return lines 
© www.soinside.com 2019 - 2024. All rights reserved.