Reading mouse position¶
In PyGame, there is an easy way to to read the current state of the mouse. The data we are usually most interested in are mouse position and buttons being pressed. In this lesson we will use mouse position reading, and in the next mouse buttons. Aside from the position and the button presses, there is other information about the mouse that we can get, but we won’t do that here. Those interested can find more details for example here .
We can get the mouse position by calling the function pg.mouse.get_pos()
, which returns an ordered pair of coordinates of the point where the mouse cursor is currently located.
As we will see in the examples and tasks that follow, the use of this function is very simple, and we can further use the read position of the mouse in various ways.
Examples and tasks¶
Example - a butterfly follows the mouse:
In this example, we load two butterfly images and display them alternately, as we did in the animations. What is new is that where we show the butterfly is determined by the mouse position we have obtained from the pg.mouse.get_pos() function.


import pygame as pg, petljapg, random
(width, height) = (400, 400)
canvas = petljapg.open_window(width, height, "Butterfly follows the mouse")
butterfly_images = [pg.image.load("butterfly1.png"), pg.image.load("butterfly2.png")]
i_image = 0
def new_frame():
global i_image
i_image = 1 - i_image # the two images are displayed alternately
canvas.fill(pg.Color("white"))
(mouse_x, mouse_y) = pg.mouse.get_pos()
image = butterfly_images[i_image] # image to display
# show the image centered
(x, y) = (mouse_x - image.get_width() / 2, mouse_y - image.get_height() / 2)
canvas.blit(image, (x, y))
petljapg.frame_loop(5, new_frame)
(PyGame__interact_butterfly1_eng)
You have probably noticed that when you move the mouse faster, the butterfly lags a little. This happens because only 5 frames are displayed per second, so the delay can be up to 0.2 seconds.
This delay is easily eliminated by increasing the frame rate (showing more frames per second), but then the images are switched too often and the butterfly flaps its wings too fast. The solution is to increase the frame rate, while displaying each image during multiple frames.
Task - move fast, flap slowly:
Copy the previous program here, then modify it so that the butterfly does not lag behind the mouse while the flapping speed remains the same.
Hint: in order for the butterfly not to lag, we certainly need more frames per second, for example n times more. This means that the new_frame function is called n times more often than before. In order to maintain the same flapping speed, each image needs to be displayed n times longer, that is, during n consecutive frames.
(PyGame__interact_butterfly2_eng)
Task - towards mouse: Write a program in which a green ball is moving towards the mouse, like in the example (“Play task” button).
Hint: In this task the key point is how the coordinates (x,y) of the center of the ball change. In a situation like the one in the picture, we want to increase x by dx and y by dy. Depending on how we want the ball to move, values dx, dy can be calculated in different ways. One easy way is to choose dx=110(xm−x),dy=110(ym−y).

(PyGame__interact_towards_mouse_eng)
Task - towards mouse with trace: Copy the previous program and then modify it so that the ball leaves a gray trace, like in the example (“Play task” button).
Hint: The motion of the ball is same as in the previous example. To make a trace, we need to store a list of several (we used 20) previous positions of the ball.
When calculating a new state, we add the most recent position to the list, and if the list has become too long, we delete the oldest position.
When drawing a trace, for each circle we use color (shade, shade, shade), where shade equals 255 (white) before the loop, and in the loop it decreases by a certain value, so that in the last pass through the loop it becomes zero (black), or as close to zero as possible.
So, for example, if the list is called trace, these or similar statements should appear in your program:
trace = []
...
def new_frame():
...
trace.append((x, y))
...
if ...
trace = trace[1:]
(PyGame__interact_towards_mouse_with_trace_eng)
Finally, you can try out the following two programs and play around with them.
In order to make programs like these, in addition to the programming techniques shown here, one needs some knowledge of physics (elastic force, Newton’s second law) and mathematics (Pythagorean theorem). Take a look at the programs without having to fully understand them. If you like, try modifying some constants a bit, so you can see how this affects the program’s behavior.
Example: Yо-yо
import pygame as pg, petljapg, math
(width, height) = (250, 250)
canvas = petljapg.open_window(width, height, "yoyo")
def distance(A, B):
(xa, ya) = A
(xb, yb) = B
return math.sqrt((xa - xb)**2 + (ya - yb)**2)
(x, y) = (width // 2, height // 2) # ball position
(vx, vy) = (0, 0) # ball speed
rubber_band_length = 80
def new_frame():
global x, y, vx, vy
xm, ym = pg.mouse.get_pos() # mouse position coordinates
r = distance((x, y), (xm, ym)) # ball to mouse distance
ax, ay = 0, 0 # ball acceleration vector
if r > rubber_band_length:
# elastic force produces acceleration toward the mouse
k = 0.0001*(r-rubber_band_length)
ax, ay = k * (xm - x), k * (ym - y)
ay += 0.3 # acceleration from ball weight
vx, vy = vx + ax, vy + ay # new speed
x, y = x + vx, y + vy # new position
# draw a green ball with a black rubber band on a white background
canvas.fill(pg.Color("white"))
ix, iy = int(x), int(y)
pg.draw.line(canvas, pg.Color("black"), (ix, iy), (xm, ym), 2)
pg.draw.circle(canvas, pg.Color("green"), (ix, iy), 10)
petljapg.frame_loop(40, new_frame)
(PyGame__interact_yoyo_eng)
Example: Eyes
This program also requires a little more knowledge of mathematics, so we will not go into detail. If you are interested in how this program works and you аrе good at math, try to understand the details with some help.
import pygame as pg, petljapg, math
(width, height) = (400, 400)
canvas = petljapg.open_window(width, height, "Eyes")
# Function draws an eye whose center is at (cx, cy), radius is r,
# and its pupil is positioned to look at the position of the mouse (mx, my)
def draw_eye(cx, cy, r, mx, my):
pg.draw.circle(canvas, pg.Color("black"), (cx, cy), 2*r, 1) # draw an eye
D = math.sqrt((mx-cx)**2 + (my-cy)**2) # mouse to eye center distance
# k is the part of the vector CM for which we move from the center of the eye
k = r/D if D > r else 1
pupil_center = (cx + round(k*(mx-cx)), cy + round(k*(my-cy)))
pg.draw.circle(canvas, pg.Color("black"), pupil_center, r) # draw pupil
def new_frame():
global mouse_x, mouse_y
(mouse_x, mouse_y) = pg.mouse.get_pos() # mouse position coordinates
canvas.fill(pg.Color("white")) # paint background to white
# draw both eyes using the auxiliary function
draw_eye(left_eye_xc, left_eye_yc, pupil_r, mouse_x, mouse_y)
draw_eye(right_eye_xc, right_eye_yc, pupil_r, mouse_x, mouse_y)
(canvas_xc, canvas_yc) = (width // 2, height // 2) # center of the window
eye_r = width // 8 # eye radius
pupil_r = eye_r // 2 # pupil radius
left_eye_xc = canvas_xc - 3 * pupil_r # x coordinate of center of left eye
right_eye_xc = canvas_xc + 3 * pupil_r # x coordinate of center of right eye
left_eye_yc = right_eye_yc = canvas_yc # y coordinate of centers of both eyes
(mouse_x, mouse_y) = pg.mouse.get_pos()
petljapg.frame_loop(50, new_frame)
(PyGame__interact_eyes_eng)