Click for Points

Goals

  • Use events to detect mouse clicks

  • Use Pythagorean theorem to calculate the distance between the mouse and the ball

  • Update the score if they click the ball.

  • Display the score as text in the Pygame window

Starter code

# Bouncing Ball
import pygame
from pygame.draw import circle
from pygame.locals import K_ESCAPE, KEYDOWN, QUIT

pygame.init()

WIDTH = 640
HEIGHT = 480
SIZE = (WIDTH, HEIGHT)

screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()

# ---------------------------
# Initialize global variables

circle_x = 100
circle_y = 50
circle_x_speed = 5
circle_y_speed = 5
circle_radius = 30
# ---------------------------

running = True
while running:
    # EVENT HANDLING
    for event in pygame.event.get():
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
        elif event.type == QUIT:
            running = False
    
    # GAME STATE UPDATES
    # All game math and comparisons happen here

    if circle_x + circle_radius > WIDTH or circle_x - circle_radius < 0:
        circle_x_speed *= -1

    if circle_y + circle_radius > HEIGHT or circle_y - circle_radius < 0:
        circle_y_speed *= -1

    circle_x += circle_x_speed
    circle_y += circle_y_speed

    # DRAWING
    screen.fill((255, 255, 255))  # always the first drawing command
    pygame.draw.circle(screen, (0, 0, 255), (circle_x, circle_y), circle_radius)  # radius!

    # Must be the last two lines
    # of the game loop
    pygame.display.flip()
    clock.tick(30)
    #---------------------------


pygame.quit()

Video Tutorial

What you need to do

  • Add a second ball. Do not copy and paste!

Final code

 1# Click for Points Solution
 2import math
 3
 4import pygame
 5from pygame.draw import circle
 6from pygame.locals import K_ESCAPE, KEYDOWN, QUIT, MOUSEBUTTONDOWN
 7
 8pygame.init()
 9pygame.font.init()
10
11WIDTH = 640
12HEIGHT = 480
13SIZE = (WIDTH, HEIGHT)
14
15screen = pygame.display.set_mode(SIZE)
16clock = pygame.time.Clock()
17
18# ---------------------------
19# Initialize global variables
20
21circle_x = 100
22circle_y = 50
23circle_x_speed = 5
24circle_y_speed = 5
25circle_radius = 50
26score = 0
27
28score_font = pygame.font.SysFont('Comic Sans MS', 50)
29
30# ---------------------------
31
32running = True
33while running:
34    # EVENT HANDLING
35    for event in pygame.event.get():
36        if event.type == KEYDOWN:
37            if event.key == K_ESCAPE:
38                running = False
39        elif event.type == QUIT:
40            running = False
41        elif event.type == MOUSEBUTTONDOWN:
42
43            x, y = event.pos
44            a = circle_x - x
45            b = circle_y - y
46            distance = math.sqrt(a**2 + b**2)
47            if distance < circle_radius:
48                score += 1
49
50    
51    # pressed = pygame.mouse.get_pressed()
52    # print(pressed)
53    
54    # GAME STATE UPDATES
55    # All game math and comparisons happen here
56
57    if circle_x + circle_radius > WIDTH or circle_x - circle_radius < 0:
58        circle_x_speed *= -1
59
60    if circle_y + circle_radius > HEIGHT or circle_y - circle_radius < 0:
61        circle_y_speed *= -1
62
63    circle_x += circle_x_speed
64    circle_y += circle_y_speed
65
66    # DRAWING
67
68    screen.fill((255, 255, 255))  # always the first drawing command
69    pygame.draw.circle(screen, (0, 0, 255), (circle_x, circle_y), circle_radius)  # radius!
70
71    score_text = score_font.render(f'Score: {score}', False, (0, 0, 0))
72    screen.blit(score_text, (0, 0))
73    # print(f"Score: {score}")
74
75    # Must be the last two lines
76    # of the game loop
77    pygame.display.flip()
78    clock.tick(30)
79    #---------------------------
80
81
82pygame.quit()

Breakdown

6from pygame.locals import K_ESCAPE, KEYDOWN, QUIT, MOUSEBUTTONDOWN

If we want to handle mouse click events, we most likely want to use the MOUSEBUTTONDOWN event. This should imported from pygame.locals. This will allow us to use the MOUSEBUTTONDOWN event type on line 41.

41        elif event.type == MOUSEBUTTONDOWN:
42
43            x, y = event.pos
44            a = circle_x - x
45            b = circle_y - y
46            distance = math.sqrt(a**2 + b**2)
47            if distance < circle_radius:
48                score += 1

Note

Sometimes the imports from pygame.locals can get a little long. You can also reference the MOUSEBUTTONDOWN event directly from pygame.locals right in the if statement.

elif event.type == pygame.locals.MOUSEBUTTONDOWN:
    # handle the click

You can also reference anything else from pygame.locals like this as well.

Let’s breakdown how points are scored:

43            x, y = event.pos
44            a = circle_x - x
45            b = circle_y - y
46            distance = math.sqrt(a**2 + b**2)
47            if distance < circle_radius:
48                score += 1

Line 43 does a convenient Tuple-unpacking for us to separate out the x and y component of the mouse coordinate. Then, we set up the Pathagorean Theorem to get the distance between the mouse and the center of the circle. We then check to see if the distance is less than the circle’s radius. If it is, then that means it’s a click on the circle.

Next we can draw some text to the screen to show the score. The first step is to initialize pygame’s font module.

9pygame.font.init()

The next step is to actually load a font from the system. If the font you choose is not on your system, pygame will give you some default alternative.

28score_font = pygame.font.SysFont('Comic Sans MS', 50)

Once the font is loaded, we can use it to render some text. Because this text may change from frame to frame, it must be rendered in the game-loop.

71    score_text = score_font.render(f'Score: {score}', False, (0, 0, 0))
72    screen.blit(score_text, (0, 0))

Finally, the renderd text gets “blitted” to the screen (whatever that means) at the location provided.

Note

If you have text that won’t change from frame to frame, you could demonstrate your keen insight by rendering the text only once, outside the game-loop. Of course, you must still “blit” the rendered text in the game-loop or else it won’t show.