Python Arena
Step 3 of 3

The mouse

A mouse click is two events: pygame.MOUSEBUTTONDOWN when the button goes down, and pygame.MOUSEBUTTONUP when it comes back up. Both have two attributes: pos, the point in the window where it happened, as a tuple (x, y), and button, which button it was: 1 is the left button, 2 the middle one and 3 the right one.

import pygame

pygame.init()
window = pygame.display.set_mode((320, 240))

while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            print("button", event.button, "at", event.pos)
        if event.type == pygame.QUIT:
            exit()

    window.fill((0, 0, 0))
    pygame.display.flip()
pygame window320 × 240
The program's window after 20 frames
The window as text
fill (0, 0, 0)
The window after 20 frames (After 4 frames: the mouse button is pressed at (50, 40). After 5 frames: the mouse button is let go at (50, 40). After 10 frames: the mouse button is pressed at (270, 200). After 11 frames: the mouse button is let go at (270, 200).).
Output
button 1 at (50, 40)
button 3 at (270, 200)

event.pos[0] is the x of the click and event.pos[1] its y.

Drawing where the player clicks

This program moves the robot to each click. The click should be at the robot's center, so its top-left corner goes half its size to the left and up:

The image robot.png
robot.png, 40 × 40 pixels
import pygame

pygame.init()
window = pygame.display.set_mode((320, 240))
robot = pygame.image.load("robot.png")

x = 0
y = 0

while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            x = event.pos[0] - robot.get_width() // 2
            y = event.pos[1] - robot.get_height() // 2
        if event.type == pygame.QUIT:
            exit()

    window.fill((0, 0, 0))
    window.blit(robot, (x, y))
    pygame.display.flip()
pygame window320 × 240
The program's window after 30 frames
The window as text
fill (0, 0, 0)
blit robot.png at (80, 130)
The window after 30 frames (After 5 frames: the mouse button is pressed at (200, 60). After 6 frames: the mouse button is let go at (200, 60). After 15 frames: the mouse button is pressed at (100, 150). After 16 frames: the mouse button is let go at (100, 150).).

The click only changes the variables. The drawing still happens in every frame, from the variables, as in every program so far. To keep something for every click, such as a coin at each one, append the points to a list and draw everything in the list in each frame.

Following the mouse

pygame.MOUSEMOTION comes whenever the mouse moves over the window, and its pos is where the mouse pointer is now. Here the robot's center sticks to the pointer:

import pygame

pygame.init()
window = pygame.display.set_mode((320, 240))
robot = pygame.image.load("robot.png")

x = 140
y = 100

while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEMOTION:
            x = event.pos[0] - robot.get_width() // 2
            y = event.pos[1] - robot.get_height() // 2
        if event.type == pygame.QUIT:
            exit()

    window.fill((0, 0, 0))
    window.blit(robot, (x, y))
    pygame.display.flip()
pygame window320 × 240
The program's window after 30 frames
The window as text
fill (0, 0, 0)
blit robot.png at (240, 70)
The window after 30 frames (After 5 frames: the mouse moves to (180, 120). After 10 frames: the mouse moves to (220, 110). After 15 frames: the mouse moves to (260, 90).).

Instead of jumping, the robot can also walk toward the pointer: keep the point it's heading for in variables of its own, and in each frame move the robot a step closer to it, as the second challenge below does.

Did the click hit?

A click hits an image when its point is inside the image's rectangle. The image covers x from its left edge up to (but not including) its left edge plus its width, and the same for y:

mouse_x, mouse_y = event.pos
if x <= mouse_x < x + robot.get_width() and y <= mouse_y < y + robot.get_height():
    print("Got the robot!")

Python lets you chain comparisons like this: x <= mouse_x < x + 40 means x <= mouse_x and mouse_x < x + 40.

Your task

Challenge 1 of 3

Each click should leave a coin centered at the point of the click. Fill in the blanks: the type of event for a press of the mouse button, and the attribute that has the point.

Challenge 2

The robot walks toward the mouse pointer. It starts at (0, 0) and heads for the point that puts its center at the pointer: (pointer's x - 20, pointer's y - 20). In each frame it moves 2 pixels closer across (if it isn't there yet) and 2 pixels closer up or down (if it isn't there yet). Until the mouse moves, it heads for (0, 0), so it stays put. Use a clock at 60 frames per second, and handle the events, move and draw in that order. (The tests move the mouse only to points with even numbers, so the robot always ends exactly there.)

Challenge 3

Whack the monster! The monster appears at the first of these places, its top-left corner given:

places = [(40, 40), (240, 160), (140, 100), (240, 40)]

When the player clicks with the left button (button 1) on the monster, that's a hit, and the monster jumps to the next place in the list; after the last place it goes back to the first. A click that misses the monster, or a click with another button, does nothing. The monster is 40 x 40, and a click hits it when it's inside the monster's rectangle, as in the lesson: a click on the pixel just past its right or bottom edge misses.

When the window is closed, the program prints the number of hits as Hits: 2 and ends: in the pygame.QUIT branch, print the hits and then call exit(). (Do it there, not after the main loop. Here a closed window can't show another frame, so a program that draws again after pygame.QUIT is stopped at its next pygame.display.flip(), and code after its loop never runs.)