5
\$\begingroup\$

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.surface = pygame.image.load("GFX/player.png") 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:

enter image description here

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?

\$\endgroup\$

4 Answers 4

8
\$\begingroup\$

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) 
\$\endgroup\$
3
  • \$\begingroup\$ 360 / (2 * PI) is the same as 180 / PI \$\endgroup\$ Commented Dec 8, 2016 at 12:10
  • \$\begingroup\$ @Bálint Yes, you're correct, stupid mistake by me. Editing it in! \$\endgroup\$ Commented Dec 8, 2016 at 12:15
  • 1
    \$\begingroup\$ That's not a mistake, neither is it stupid \$\endgroup\$ Commented Dec 8, 2016 at 14:02
1
\$\begingroup\$

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

\$\endgroup\$
0
\$\begingroup\$

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. radius, angle = direction.as_polar() # 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) 
\$\endgroup\$
0
\$\begingroup\$

You have to compute the angle of the vector from the player to the mouse. get the mouse position by pygame.mouse.get_pos() and the rectangle (pygame.Rect) around the player (self.rect ):

mx, my = pygame.mouse.get_pos() player_rect = self.rect 

Calculate the vector from the player to the mouse and compute the angle of vector by math.atan2. The y-axis needs to be reversed (-dy) as the y-axis is generally pointing up, but in the PyGame coordinate system the y-axis is pointing down.

dx, dy = mx - player_rect.centerx, player_rect.centery - my angle = math.degrees(math.atan2(-dy, dx)) - correction_angle 

In addition, a correction angle must be deducted (- correction_angle). The correction angle depends on the Sprite. If the Sprite

is looking to the right, the correction angle is 0: correction_angle = 0
is looking up, the correction angle is 90: correction_angle = 90
is looking to the left, the correction angle is 180: correction_angle = 180
is looking down, the correction angle is 270: correction_angle = 270

Rotate the player with pygame.transform.rotate() by the angle around its center:
(See also How do I rotate an image around its center using Pygame?)

rot_image = pygame.transform.rotate(Player_1, angle) rot_image_rect = rot_image.get_rect(center=player_rect.center) 

Minimal example: repl.it/@Rabbid76/PyGame-RotateWithMouse

import math import pygame pygame.init() window = pygame.display.set_mode((300, 300)) player = pygame.image.load("player.png").convert_alpha() # 0 - image is looking to the right # 90 - image is looking up # 180 - image is looking to the left # 270 - image is looking down correction_angle = 90 run = True while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False player_pos = window.get_rect().center player_rect = player.get_rect(center = player_pos) mx, my = pygame.mouse.get_pos() dx, dy = mx - player_rect.centerx, my - player_rect.centery angle = math.degrees(math.atan2(-dy, dx)) - correction_angle rot_image = pygame.transform.rotate(player, angle) rot_image_rect = rot_image.get_rect(center = player_rect.center) window.fill((255, 255, 255)) window.blit(rot_image, rot_image_rect.topleft) pygame.display.flip() pygame.quit() exit() 
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.