在 Pygame 中检测多边形和矩形之间的碰撞 [英] Detecting collisions between polygons and rectangles in Pygame

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

问题描述

所以我试图用 pygame 制作一个我们中间的类型游戏.我刚开始,所以我没有太多东西,现在正在制作地图.但是,我正在努力解决的一件事是碰撞逻辑.地图现在有一个细长的八边形形状,但我认为无论形状如何,我都会使用类似 pygame 多边形的东西.当我运行我现在拥有的代码时,它会检查我的玩家(pygame 矩形)和墙壁(pygame 多边形)之间的碰撞,它说:

TypeError: Argument must be rect style object

我发现这是因为 pygame 多边形返回一个矩形,但在碰撞检查器中没有以这种方式分类.我尝试了一个名为


完整示例:

导入pygamepygame.init()宽, 高=500, 500屏幕 = pygame.display.set_mode([500, 500])运行 = 真bcg=(200, 200, 200)红色=(255, 0 ,0)purp=(255, 0, 255)墙=(100, 100, 100)def collideLineLine(l1_p1, l1_p2, l2_p1, l2_p2):# 线的归一化方向和线的起点P = pygame.math.Vector2(*l1_p1)line1_vec = pygame.math.Vector2(*l1_p2) - PR = line1_vec.normalize()Q = pygame.math.Vector2(*l2_p1)line2_vec = pygame.math.Vector2(*l2_p2) - QS = line2_vec.normalize()# 线的法向量RNV = pygame.math.Vector2(R[1], -R[0])SNV = pygame.math.Vector2(S[1], -S[0])RdotSVN = R.dot(SNV)如果 RdotSVN == 0:返回错误# 到交点的距离QP = Q - Pt = QP.dot(SNV)/RdotSVNu = QP.dot(RNV)/RdotSVN返回 t >0 和 u >0 和 t*t 

So I am trying to make an among us type game with pygame. I just started, so I don't have much of anything and am working on the map right now. However, one thing I'm struggling with is the collision logic. The map has an elongated octagon shape for now, but I think no matter the shape I will use something like a pygame polygon. When I ran the code I have now, which checks for a collision between my player (pygame rectangle) and the walls (pygame polygon) it says:

TypeError: Argument must be rect style object

I've figured out this is because of the pygame polygon returning a rectangle, but not being classified that way in the collision checker. I have tried a library called collision, and credit to the collision detection for giving a great effort, but the player was still able to glitch through the walls. Sidenote: I saved the code where I used this library if anyone wants to see it and maybe improve upon my faults.

Anyway, to boil it all down:

I need a way to detect collisions (really, really preferably in pygame) between polygons and rectangles

Thank you for any help you can give and if you have a question/request please leave a comment.

Heres my code:

import pygame
pygame.init()
W, H=500, 500
screen = pygame.display.set_mode([500, 500])
running = True

bcg=(200, 200, 200)
red=(255, 0 ,0)
purp=(255, 0, 255)
wall=(100, 100, 100)

class player:
    def bg(self):        
        screen.fill(bcg)
        x,y=self.x,self.y

        self.outer=(
                (x,y),
                (x+800, y),
                (x+1200, y+200),
                (x+1200, y+600),
                (x+800, y+800),
                (x, y+800),
                (x-400, y+600),
                (x-400, y+200),
                
                (x,y),
                
                (x, y+50),
                (x-350, y+225),
                (x-350, y+575),
                (x, y+750),
                (x+800, y+750),
                (x+1150, y+575),
                (x+1150, y+225),
                (x+800, y+50),
                (x, y+50)
            )
        
        pygame.draw.polygon(screen, wall, self.outer)
        
    
    def __init__(self, color, size=20, speed=0.25):
        self.x=0
        self.y=0
        self.col=color
        self.size=size
        self.speed=speed


    def draw(self):
        s=self.size
        self.rect=pygame.Rect(W/2-s/2, H/2-s/2, self.size, self.size)
        pygame.draw.rect(screen, self.col, self.rect)

    def move(self, x, y):
        x*=self.speed
        y*=self.speed
        if not self.rect.colliderect(self.outer):
            self.x+=x
            self.y+=y
        

        

p=player(red)

while running:
    for event in pygame.event.get():
        
        if event.type == pygame.QUIT:
            running = False

    p.bg()

    keys=pygame.key.get_pressed()
    
    if keys[pygame.K_a]: p.move(1, 0)
    if keys[pygame.K_d]: p.move(-1, 0)
    if keys[pygame.K_w]: p.move(0, 1)
    if keys[pygame.K_s]: p.move(0, -1)

    p.draw()

    pygame.display.update()

pygame.quit()

解决方案

Write a function collideLineLine that test if to line segments are intersecting. The algorithm to this function is explained in detail in the answer to the question pygame, detecting collision of a rotating rectangle:

def collideLineLine(l1_p1, l1_p2, l2_p1, l2_p2):

    # normalized direction of the lines and start of the lines
    P  = pygame.math.Vector2(*l1_p1)
    line1_vec = pygame.math.Vector2(*l1_p2) - P
    R = line1_vec.normalize()
    Q  = pygame.math.Vector2(*l2_p1)
    line2_vec = pygame.math.Vector2(*l2_p2) - Q
    S = line2_vec.normalize()

    # normal vectors to the lines
    RNV = pygame.math.Vector2(R[1], -R[0])
    SNV = pygame.math.Vector2(S[1], -S[0])
    RdotSVN = R.dot(SNV)
    if RdotSVN == 0:
        return False

    # distance to the intersection point
    QP  = Q - P
    t = QP.dot(SNV) / RdotSVN
    u = QP.dot(RNV) / RdotSVN

    return t > 0 and u > 0 and t*t < line1_vec.magnitude_squared() and u*u < line2_vec.magnitude_squared()

Write the function colideRectLine that test if a rectangle and a line segment is intersecting. To test if a line segment intersects a 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))

The next function collideRectPolygon tests if a polygon and a rectangle are intersecting. This can be achieved by testing each line segment on the polygon against the rectangle in a loop:

def collideRectPolygon(rect, polygon):
    for i in range(len(polygon)-1):
        if colideRectLine(rect, polygon[i], polygon[i+1]):
            return True
    return False

Finally you can use collideRectPolygon for the collision test. Note, however, that for the test you need to use the polygon as if the player were moving:

class player:
    def bg(self):        
        screen.fill(bcg)
        self.outer = self.createPolygon(self.x, self.y)
        pygame.draw.polygon(screen, wall, self.outer)
  
    def createPolygon(self, x, y):
        return [
            (x,y), (x+800, y), (x+1200, y+200), (x+1200, y+600), 
            (x+800, y+800), (x, y+800), (x-400, y+600), (x-400, y+200),           
            (x,y), (x, y+50), (x-350, y+225), (x-350, y+575), 
            (x, y+750), (x+800, y+750), (x+1150, y+575), (x+1150, y+225),
            (x+800, y+50),(x, y+50)]
    
    # [...]

    def move(self, x, y):
        
        x *= self.speed
        y *= self.speed
        polygon = self.createPolygon(self.x + x, self.y + y)
        if not collideRectPolygon(self.rect, polygon):
            self.x += x
            self.y += y

See also Collision and Intersection - Rectangle and polygon


Minimal example:


Complete example:

import pygame
pygame.init()
W, H=500, 500
screen = pygame.display.set_mode([500, 500])
running = True

bcg=(200, 200, 200)
red=(255, 0 ,0)
purp=(255, 0, 255)
wall=(100, 100, 100)

def collideLineLine(l1_p1, l1_p2, l2_p1, l2_p2):

    # normalized direction of the lines and start of the lines
    P  = pygame.math.Vector2(*l1_p1)
    line1_vec = pygame.math.Vector2(*l1_p2) - P
    R = line1_vec.normalize()
    Q  = pygame.math.Vector2(*l2_p1)
    line2_vec = pygame.math.Vector2(*l2_p2) - Q
    S = line2_vec.normalize()

    # normal vectors to the lines
    RNV = pygame.math.Vector2(R[1], -R[0])
    SNV = pygame.math.Vector2(S[1], -S[0])
    RdotSVN = R.dot(SNV)
    if RdotSVN == 0:
        return False

    # distance to the intersection point
    QP  = Q - P
    t = QP.dot(SNV) / RdotSVN
    u = QP.dot(RNV) / RdotSVN

    return t > 0 and u > 0 and t*t < line1_vec.magnitude_squared() and u*u < line2_vec.magnitude_squared()

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

def collideRectPolygon(rect, polygon):
    for i in range(len(polygon)-1):
        if colideRectLine(rect, polygon[i], polygon[i+1]):
            return True
    return False

class player:
    def bg(self):        
        screen.fill(bcg)
        self.outer = self.createPolygon(self.x, self.y)
        pygame.draw.polygon(screen, wall, self.outer)
  
    def createPolygon(self, x, y):
        return [
            (x,y), (x+800, y), (x+1200, y+200), (x+1200, y+600), 
            (x+800, y+800), (x, y+800), (x-400, y+600), (x-400, y+200),           
            (x,y), (x, y+50), (x-350, y+225), (x-350, y+575), 
            (x, y+750), (x+800, y+750), (x+1150, y+575), (x+1150, y+225),
            (x+800, y+50),(x, y+50)]
    
    def __init__(self, color, size=20, speed=0.25):
        self.x=0
        self.y=0
        self.col=color
        self.size=size
        self.speed=speed

    def draw(self):
        s=self.size
        self.rect=pygame.Rect(W/2-s/2, H/2-s/2, self.size, self.size)
        pygame.draw.rect(screen, self.col, self.rect)

    def move(self, x, y):
        
        x *= self.speed
        y *= self.speed
        polygon = self.createPolygon(self.x + x, self.y + y)
        if not collideRectPolygon(self.rect, polygon):
            self.x += x
            self.y += y
            
p=player(red)
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    p.bg()

    keys=pygame.key.get_pressed()
    
    if keys[pygame.K_a]: p.move(1, 0)
    if keys[pygame.K_d]: p.move(-1, 0)
    if keys[pygame.K_w]: p.move(0, 1)
    if keys[pygame.K_s]: p.move(0, -1)

    p.draw()
    pygame.display.update()

pygame.quit()

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

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