在 Pygame 中检测多边形和矩形之间的碰撞 [英] Detecting collisions between polygons and rectangles in 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屋!