Skip to content Skip to sidebar Skip to footer

Issue Finding Side Of Collision For Circle-rectangle Collision

I have found a function that works quite well for detecting collisions between a circle and a rectangle, and also finding the point of contact. I then use that point to determine w

Solution 1:

You can find the side of the circle, by finding the on the rectangle, which is on the straight line, which is given by the center of the circle and the center of the rectangle.

The point on the rectangle and the circle can be computed by the minimum relation of the offset between the center points and the size of the rectangle. In the following algorithm, the rectangle is defined by the center point (r_cpt) and the size (r_size) and the circle is defined by the center point (c_cpt) and the radius (c_rad):

defintersectRectangleCircle(r_cpt, r_size, c_cpt, c_rad):

    v2_c_cpt = pygame.math.Vector2(c_cpt)
    v2_r_cpt = pygame.math.Vector2(r_cpt)

    offset = v2_c_cpt - v2_r_cpt
    if offset.x == 0and offset.y == 0:
        return [v2_c_cpt, v2_r_cpt]

    if offset.x == 0:   
        ratio = r_size[1] / abs(offset.y)
    elif offset.y == 0: 
        ratio = r_size[0] / abs(offset.x)
    else:
        ratio  = min(r_size[0] / abs(offset.x), r_size[1] / abs(offset.y))
    ratio *= 0.5

    p1 = v2_r_cpt + (offset * ratio)
    offset.scale_to_length(c_rad)
    p2 = v2_c_cpt - offset

    return [p1, p2]

The direction to the circle is the given by the vector from the center point of the rectangle to the point on the rectangle contour:

isect_pts = intersectRectangleCircle(rect_center, rect_size, circle_center, circle_diameter/2)
dx, dy = isect_pts[0].x - rect_center[0], isect_pts[1].y - rect_center[1] 

See the example, (dx, dy) is represented by the magenta colored line:

repl.it/@Rabbid76/PyGame-NearestPointOnRectangle

import pygame
import math

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

defintersectRectangleCircle(r_cpt, r_size, c_cpt, c_rad):

    v2_c_cpt = pygame.math.Vector2(c_cpt)
    v2_r_cpt = pygame.math.Vector2(r_cpt)

    offset = v2_c_cpt - v2_r_cpt
    if offset.x == 0and offset.y == 0:
        return [v2_c_cpt, v2_r_cpt]

    if offset.x == 0:   
        ratio = r_size[1] / abs(offset.y)
    elif offset.y == 0: 
        ratio = r_size[0] / abs(offset.x)
    else:
        ratio  = min(r_size[0] / abs(offset.x), r_size[1] / abs(offset.y))
    ratio *= 0.5

    p1 = v2_r_cpt + (offset * ratio)
    offset.scale_to_length(c_rad)
    p2 = v2_c_cpt - offset

    return [p1, p2]


definBetween(p1, p2, px):

    v = pygame.math.Vector2(p2) - pygame.math.Vector2(p1)
    d = v.length()
    if d == 0:
        returnFalse
    v.normalize_ip()

    vx = pygame.math.Vector2(px) - pygame.math.Vector2(p1)
    dx = v.dot(vx)
    
    return dx >= 0and dx <= d

done = Falsewhilenot done:

    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            done = True


    rect_center     = screen.get_rect().center
    rect_size       = screen.get_width() // 5, screen.get_height() // 10
    rect            = pygame.Rect(rect_center[0] - rect_size[0] // 2, rect_center[1] - rect_size[1] // 2, *rect_size)
    circle_center   = pygame.mouse.get_pos()
    circle_diameter = min(*screen.get_size()) // 5

    isect_pts = intersectRectangleCircle(rect_center, rect_size, circle_center, circle_diameter/2)
    dx, dy = isect_pts[0].x - rect_center[0], isect_pts[1].y - rect_center[1] 

    screen.fill((255,255,255))
    pygame.draw.rect(screen, (0, 0, 0), rect, 3)
    pygame.draw.circle(screen, (0, 0, 0), circle_center, circle_diameter // 2, 3)

    pygame.draw.line(screen, (0, 0, 255), rect_center, circle_center, 1)
    pygame.draw.line(screen, (255, 0, 255), rect_center, (round(isect_pts[0].x), round(isect_pts[0].y)), 3)
    for i inrange(2):
        px, py = round(isect_pts[i].x), round(isect_pts[i].y)
        col = (255, 0, 0) if inBetween(rect_center, circle_center, (px, py)) else (0, 255, 0)
        pygame.draw.line(screen, col, (px-5, py), (px+5, py), 3)
        pygame.draw.line(screen, col, (px, py-5), (px, py+5), 3)

    pygame.display.flip()

pygame.quit()
quit()

Post a Comment for "Issue Finding Side Of Collision For Circle-rectangle Collision"