Detecting Collisions Between Polygons And Rectangles In Pygame
Solution 1:
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:
defcollideLineLine(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:
returnFalse# distance to the intersection point
QP = Q - P
t = QP.dot(SNV) / RdotSVN
u = QP.dot(RNV) / RdotSVN
return t > 0and u > 0and 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:
defcollideRectPolygon(rect, polygon):
for i inrange(len(polygon)-1):
if colideRectLine(rect, polygon[i], polygon[i+1]):
returnTruereturnFalse
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:
classplayer:
defbg(self):
screen.fill(bcg)
self.outer = self.createPolygon(self.x, self.y)
pygame.draw.polygon(screen, wall, self.outer)
defcreatePolygon(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)]
# [...]defmove(self, x, y):
x *= self.speed
y *= self.speed
polygon = self.createPolygon(self.x + x, self.y + y)
ifnot collideRectPolygon(self.rect, polygon):
self.x += x
self.y += y
See also Collision and Intersection - Rectangle and polygon
Minimal example:
repl.it/@Rabbid76/PyGame-CollisionPolygonRectangle
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)
defcollideLineLine(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:
returnFalse# distance to the intersection point
QP = Q - P
t = QP.dot(SNV) / RdotSVN
u = QP.dot(RNV) / RdotSVN
return t > 0and u > 0and t*t < line1_vec.magnitude_squared() and u*u < line2_vec.magnitude_squared()
defcolideRectLine(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))
defcollideRectPolygon(rect, polygon):
for i inrange(len(polygon)-1):
if colideRectLine(rect, polygon[i], polygon[i+1]):
returnTruereturnFalseclassplayer:
defbg(self):
screen.fill(bcg)
self.outer = self.createPolygon(self.x, self.y)
pygame.draw.polygon(screen, wall, self.outer)
defcreatePolygon(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
defdraw(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)
defmove(self, x, y):
x *= self.speed
y *= self.speed
polygon = self.createPolygon(self.x + x, self.y + y)
ifnot 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()
Solution 2:
Alternatively define the rect with 4 lines and detect if you are above or bellow those lines, and in the 2 frames where the rect is perfectly aligned you just use normal rect collision.
Post a Comment for "Detecting Collisions Between Polygons And Rectangles In Pygame"