Detect collision between textbox and circle in pygame

Viewed 150

I'm trying to create a game in python where one can drag a textbox around the screen, but whenever it touches the borders of a circle around it, I want the loop to start over, but with a different text (by storing all text-strings in a list, but I'm not that far, yet). This is how far I have come:

import pygame

import ptext

pygame.init()

gameDisplay = pygame.display.set_mode((500, 500))

gameDisplay.fill((255,255,255))

x = 190
y = 230
a = 250
b = 250

text = "ExampleText 1."

def textbox(x,y):
    ptext.draw(text, (x,y), color = (0,0,0))


def circle(a,b): 
    pygame.draw.circle(gameDisplay, (0,0,0), (250, 250), 210, 5)


done = False

while not done:
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.MOUSEMOTION:
            if event.buttons[0]:
                x += event.rel[0]
                y += event.rel[1]

    textbox(x,y)
    circle(a,b)
    pygame.display.flip()

pygame.quit()
quit()

Now I understand I will need to detect collision of the borders of my objects, but here I'm pretty lost. I tried to store the variables of my objects in rectangles and then produce another if statement that recognizes whether or not my objects collide (I used a print command because I haven't gotten to the actual command I want, yet), but that won't print anything and I'm sure I'm on the wrong path, but it is my best effort...

For that I have defined:

text_rect = pygame.Rect(x, y, 10, 30)

circle_rect = pygame.Rect(a,b, 300, 300)

and then in my loop:

if circle_rect.colliderect(text_rect): 
     print("COLLIDE")

Does anybody have any tip on a better way to define the objects and to create the function I want?

(Edit: Btw.: I'm not too concerned about the fact that when I drag my textbox, it leaves a print of the text, since that doesn't happen in my original script, but would be thankful if anyone knows why it does that in my current example.)

1 Answers

A rectangle has 4 corner points. If the rectangle is "smaller" then the circle (the diameter of the circle is greater than the diagonal of the rectangle), then the rectangle collides with the contour of a circle, if at least one point is out of the circle and at least one point is in the circle.

Define the rectangle and setup a list of the corner points. Further you've to know the radius of the circle:

w, h = 10, 30
rect = pygame.Rect(x, y, 10, 30)

corners = [rect.bottomleft, rect.bottomright, rect.topleft, rect.topright]
radius = 210

Calculate the Euclidean distance of each corner point to the center of the circle (a, b):

import math
dist = [math.sqrt((p[0]-a)**2 + (p[1]-b)**2) for p in corners]

Create to lists, one with the points in the circle (p_in) and one with the points out of the circle (p_out):

p_out = [i for i, d in enumerate(dist) if d > radius]
p_in  = [i for i, d in enumerate(dist) if d < radius]

If both list contain any element, then the rectangle intersects the circle contour:

if any(p_in) and any(p_out):
    print("COLLIDE")

If len(p_in) is 4, then the rectangle is completely in the circle. If len(p_out) is 4 then the rectangle is completely out of the circle.

if any(p_in) and any(p_out):
    print("COLLIDE")
elif len(p_in) == 4:
    print("IN")
elif len(p_out) == 4:
    print("OUT")

See the simple example, which is based on your code snippet and demonstrates the collision test. The rectangle is attached to the mouse:

import pygame
import math

pygame.init()
gameDisplay = pygame.display.set_mode((500, 500))
done = False
while not done:
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            done = True

    x, y = pygame.mouse.get_pos()
    w, h = 10, 30
    rect = pygame.Rect(x, y, 10, 30)
    a, b = 250, 250
    radius = 210

    corners = [rect.bottomleft, rect.bottomright, rect.topleft, rect.topright]
    dist    = [math.sqrt((p[0]-a)**2 + (p[1]-b)**2) for p in corners] 
    p_out   = [i for i, d in enumerate(dist) if d > radius]
    p_in    = [i for i, d in enumerate(dist) if d < radius]

    if any(p_in) and any(p_out):
        print("COLLIDE")
    elif len(p_in) == 4:
        print("IN")
    elif len(p_out) == 4:
        print("OUT")

    gameDisplay.fill((255,255,255))
    pygame.draw.rect(gameDisplay, (255, 0, 0), rect)
    pygame.draw.circle(gameDisplay, (0,0,0), (a, b), radius, 5)
    pygame.display.flip()

pygame.quit()
quit()
Related