# How can I make the player “look” to the mouse direction? (Pygame / 2D)

Here's my code:

import sys
import pygame
pygame.init()

screen_width = 640
screen_height = 480
screen = pygame.display.set_mode((screen_width, screen_height))
running = True

class Actor:

def __init__(self, x, y, w, h):
self.x = x
self.y = y
self.w = w
self.h = h
self.rotated_surface = self.surface.copy()
self.rect = self.surface.get_rect()

class Player(Actor):

def __init__(self):
Actor.__init__(self, 0, 0, 32, 32)
self.directions = [False, False, False, False]
self.speed = 0.1

def rotate(self, angle):
rot_image = pygame.transform.rotozoom(self.surface, angle, 1)
rot_rect = self.rect.copy()
rot_rect.center = rot_image.get_rect().center
self.rotated_surface = rot_image

def move(self):
if self.directions[0]:
self.y -= self.speed
if self.directions[1]:
self.y += self.speed
if self.directions[2]:
self.x -= self.speed
if self.directions[3]:
self.x += self.speed

def draw(self):
screen.blit(self.rotated_surface, (self.x, self.y))

player = Player()

def redraw():
screen.fill((75, 0, 0))
player.draw()
player.move()
pygame.display.flip()

while (running):
for e in pygame.event.get():
if e.type == pygame.QUIT:
sys.exit()
elif e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
sys.exit()
if e.key == pygame.K_w:
player.directions[0] = True
if e.key == pygame.K_s:
player.directions[1] = True
if e.key == pygame.K_a:
player.directions[2] = True
if e.key == pygame.K_d:
player.directions[3] = True
elif e.type == pygame.KEYUP:
if e.key == pygame.K_w:
player.directions[0] = False
if e.key == pygame.K_s:
player.directions[1] = False
if e.key == pygame.K_a:
player.directions[2] = False
if e.key == pygame.K_d:
player.directions[3] = False
elif e.type == pygame.MOUSEMOTION:
mouse_x = pygame.mouse.get_pos()[0]
mouse_y = pygame.mouse.get_pos()[1]
angle = math.atan2(mouse_y - player.y, mouse_x - player.x)
angle = angle * (180 / math.pi)
player.rotate(angle)

redraw()


Here's the image:

The blue arrow represents the "weapon" / "eyes" of the shooter, and the green arrow represents the mouse cursor. How can I use the transform module to rotate the image "in the direction" of the mouse cursor?

First you need to calculate the vector pointing from your player to the current mouse position. This can be done by subtracting the player's position with the mouse's position:

mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - self.x, mouse_y - self.y


Then calculate the angle:

angle = math.atan2(rel_y, rel_x)


This will calculate the angle in radians while the pygame.transform.rotate(surface, angle)takes the angle in degree. It will also calculate the angle in the other direction (counter-clockwise) so you need to negate the result. If the image starts at the wrong direction just add/subtract 90 or 180 until it's right:

angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)


After rotating, you need to reposition your rect so that the image remains in the same spot. Rotating images changes it size which makes it look slightly out of position each rotation. I usually position it so its center is consistent.

self.image = pygame.transform.rotate(self.original_image, int(angle))
self.rect = self.image.get_rect(center=self.position)


Notice that I rotate an attribute named self.original_image. When applying rotation to an image it becomes slightly distorted. Doing this several times will eventually make your image unrecognizable. So always have a reference to the original image that's unaltered.

Also, naming your attribute self.image instead of self.surface and inheriting from pygame.sprite.Sprite allows you to use many convenient functions described in the pygame.sprite module.

The full code:

import math

def rotate(self):
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - self.x, mouse_y - self.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = pygame.transform.rotate(self.original_image, int(angle))
self.rect = self.image.get_rect(center=self.position)

• 360 / (2 * PI) is the same as 180 / PI – Bálint Dec 8 '16 at 12:10
• @Bálint Yes, you're correct, stupid mistake by me. Editing it in! – Ted Klein Bergman Dec 8 '16 at 12:15
• That's not a mistake, neither is it stupid – Bálint Dec 8 '16 at 14:02

I don't have experience with python, but try: atan2(mouseY, mouseX) -and yes, thats (y, x) not (x,y).

You can also use vectors to get the angle. The pygame.math.Vector2 class has a .as_polar method which returns the polar coordinates, the radius (distance) and the angle to the target.

import pygame as pg
from pygame.math import Vector2

class Player(pg.sprite.Sprite):

def __init__(self, pos):
super().__init__()
self.image = pg.Surface((50, 30), pg.SRCALPHA)
pg.draw.polygon(self.image, pg.Color('steelblue2'),
[(0, 0), (50, 15), (0, 30)])
self.orig_image = self.image  # Store a reference to the original.
self.rect = self.image.get_rect(center=pos)
self.pos = Vector2(pos)

def update(self):
self.rotate()

def rotate(self):
# The vector to the target (the mouse position).
direction = pg.mouse.get_pos() - self.pos
# .as_polar gives you the polar coordinates of the vector,
# i.e. the radius (distance to the target) and the angle.
# Rotate the image by the negative angle (y-axis in pygame is flipped).
self.image = pg.transform.rotate(self.orig_image, -angle)
# Create a new rect with the center of the old rect.
self.rect = self.image.get_rect(center=self.rect.center)

pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group(Player((300, 220)))
done = False

while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True

all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)

pg.display.flip()
clock.tick(30)