2

I need to know if 2 ellipses are colliding in pygame/python

I know how to detect collision between two circles using :

if radius1 + radius2 > math.sqrt(((centerx1 - centerx2)**2) + (centery1 - centery2)**2):

But is it possible to do the same between 2 ellipses? Help will be appreciated. Thanks.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
PradMaster
  • 83
  • 10
  • 3
    https://math.stackexchange.com/questions/1114879/detect-if-two-ellipses-intersect – Qiu Dec 16 '20 at 12:24
  • Are the ellipses aligned to the axis? – Rabbid76 Dec 16 '20 at 13:14
  • 1
    yes....they cannot be rotated in any way – PradMaster Dec 16 '20 at 14:53
  • 1
    The problem can be reduced to the intersection of an ellipse and a circle (you can scale 1 dimension). However, there doesn't seem to be a short answer. See [Ellipse–circle and ellipse–ellipse collision detection](http://yehar.com/blog/?p=2926) and [How to detect if an ellipse intersects(collides with) a circle](https://stackoverflow.com/questions/2945337/how-to-detect-if-an-ellipse-intersectscollides-with-a-circle). The major issue is to find the closest point on the ellipse to the circle. – Rabbid76 Dec 16 '20 at 15:50

2 Answers2

0

Yes, it is possible to detect collisions between two ellipses in pygame. One way to do this is by using masks in pygame (of course this is not the only way). pygame.mask.Mask is a class in pygame that can be used to create mask objects. To use masks to detect collisions between two ellipses, you will need to create a pygame.mask.Mask object for each surface of an ellipse, and then use the pygame.mask.Mask.overlap() method to check for overlap between the two masks. Here is a function that takes in two pygame.Rect objects represented as ellipses and checks for overlaps of each surface of those two pygame.Rect objects:

def ellipse_collision(ellipse1, ellipse2):
    # Create a mask for each ellipse
    mask1 = pygame.mask.Mask((ellipse1[0]*2, ellipse1[1]*2))
    mask2 = pygame.mask.Mask((ellipse2[0]*2, ellipse2[1]*2))
    # Draw the ellipses onto the masks
    mask1.draw(pygame.draw.ellipse, (0, 0, ellipse1[0]*2, ellipse1[1]*2))
    mask2.draw(pygame.draw.ellipse, (0, 0, ellipse2[0]*2, ellipse2[1]*2))
    # Check for overlap between the masks
    offset = (ellipse2[2]-ellipse1[2], ellipse2[3]-ellipse1[3])
    return mask1.overlap(mask2, offset)

NOTE: Masks in pygame are useful for fast pixel-perfect collision detection.

Zenthm
  • 104
  • 1
  • 8
0

The problem can be reduced to the intersection of an ellipse and a circle (you can scale 1 dimension). However, there doesn't seem to be a short answer. See Ellipse–circle and ellipse–ellipse collision detection and How to detect if an ellipse intersects(collides with) a circle. The major issue is to find the closest point on the ellipse to the circle.


Even though I don't think the question here was about a collision masks, but about finding a formula that is more powerful than using masks where both masks have to be passed through and the mask bits compared, I will provide a complete alternative solution using masks:

mask1 = pygame.mask.from_surface(ellipse_surf1)
mask2 = pygame.mask.from_surface(ellipse_surf2)
offset_x = ellipse_rect2.x - ellipse_rect1.x
offset_y = ellipse_rect2.y - ellipse_rect2.y
overlap_mask = mask1.overlap_mask(mask2, (offset_x, offset_y))

Complete example:

import pygame

def create_ellipse_angle(color, rect, angle, width=0):
    target_rect = pygame.Rect(rect)
    shape_surf = pygame.Surface(target_rect.size, pygame.SRCALPHA)
    pygame.draw.ellipse(shape_surf, color, (0, 0, *target_rect.size), width)
    rotated_surf = pygame.transform.rotate(shape_surf, angle)
    bounding_rect = rotated_surf.get_rect(center = target_rect.center)
    return rotated_surf, bounding_rect

pygame.init()
window = pygame.display.set_mode((400, 250))
clock = pygame.time.Clock()

background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (160, 160, 160), (192, 192, 192)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
    pygame.draw.rect(background, color, rect)

angle1 = 0
angle2 = 0
run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    ellipse_surf1, ellipse_rect1 = create_ellipse_angle((0, 128, 128), (35, 75, 200, 100), angle1)
    angle1 += 1
    ellipse_surf2, ellipse_rect2 = create_ellipse_angle((128, 0, 128), (165, 75, 200, 100), angle2)
    angle2 -= 0.5

    mask1 = pygame.mask.from_surface(ellipse_surf1)
    mask2 = pygame.mask.from_surface(ellipse_surf2)
    offset_x = ellipse_rect2.x - ellipse_rect1.x
    offset_y = ellipse_rect2.y - ellipse_rect2.y
    overlap_mask = mask1.overlap_mask(mask2, (offset_x, offset_y))
    overlap_surf = overlap_mask.to_surface(setcolor = (255, 0, 0))
    overlap_surf.set_colorkey((0, 0, 0))

    window.blit(background, (0, 0))
    window.blit(ellipse_surf1, ellipse_rect1)
    window.blit(ellipse_surf2, ellipse_rect2)
    window.blit(overlap_surf, ellipse_rect1)
    pygame.display.flip()

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174