# How do I change only the angle of a certain PNG relative to the position of the mouse? (Python, Pygame)

Basically, I'm doing a game for a school project and I am not really sure how to make a player point towards the mouse pointer in a pygame tab. also if anyone has ideas of how i could point a bullet towards the mouse and then fire in the direction of the mouse after i click fire (without continuously changing angles towards the mouse pointer) that would be awesome

Here is my code:

import pygame
import os

WIDTH, HEIGHT = 900, 500
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('First Person Shooter')

WHITE = (255,255,255)

FPS = 60
VEL = 5
BULLET_VEL = 7

CHARACTER_PLAYER_ONE = pygame.transform.rotate(pygame.transform.scale(CHARACTER_PLAYER_ONE, (55, 40)), 90)

def draw_window(one):
WIN.fill(WHITE)
WIN.blit(CHARACTER_PLAYER_ONE, (one.x, one.y))
pygame.display.update()

def one_handle_movement(keys_pressed, one):
if keys_pressed[pygame.K_a] and one.x - VEL > -15: # left
one.x -= VEL
if keys_pressed[pygame.K_d] and one.x + VEL + one.width < 925: # right
one.x += VEL
if keys_pressed[pygame.K_w] and one.y - VEL > -15: # up
one.y -= VEL
if keys_pressed[pygame.K_s] and one.y + VEL + one.height < 500: # down
one.y += VEL

def main():
one = pygame.Rect(100, 300, 55, 40)

bullets = []

clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet = pygame.Rect()

keys_pressed = pygame.key.get_pressed()
one_handle_movement(keys_pressed, one)
draw_window(one)

pygame.quit()

if __name__ == '__main__':
main()


I would really appreciate help if you are willing to provide it!

P.S. i didn't really define the bullets yet, but the player one sprite is the main thing im looking for at the moment

Much of this code is as shown in https://www.youtube.com/watch?v=jO6qQDNa2UY

-Luckee365-

• first you have to get position of mouse and gun, and calculate distance (dx, dy) between mouse and gun and value dy/dy gives you tangle of angle - and this angle you can use to rotate bullet. SO it needs some math ;) Luckly PyGame.math.Vector should have functions to calculate distance and/or angle between two points. Feb 28, 2022 at 15:27
• BTW: because one is a Rect() so instead of one.x + one.width. You can use one.right. You have also one.left, one.top, one.botton. one.center, one.centerx, one,centery, one.topleft, one.size, etc. Feb 28, 2022 at 16:48

Math: tan(angle) = dy/dx so angle = arctangle(dy/dx)

And as dx, dy you can use differnce between mouse position and gun/player position.

Get positions for mouse and gun/player.

mouse_x,  mouse_y  = pygame.mouse.get_pos()
player_x, player_y = player_rect.center


Calculate distance (dx, dy) between mouse and gun/player

dx = mouse_x - player_x
dy = mouse_y - player_y


Now dy/dx should give tangle or angle so I can use arctangle to get angle (in radians).

angle_radians = math.atan2(dy, dx)



And now rotate image with -angle (to rotate in correct direction).

player_image = pygame.transform.rotate(original_player_image, angle).convert_alpha()


To get better quality of rotated image you should always use original image.

You also have to use convert_alpha() to get colors in RGBA with transparent background.

And new Image needs new Rect with position from previous Rect.

player_rect = player_image.get_rect(centerx=player_x, centery=player_y)


Full working code. It rotates player to mouse position (even when it moves).

I used set_caption to display positions and angle in window's title.

import pygame
import os
import math

# --- constants ---  # PEP8: UPPER_CASE_NAMES for constants

WIDTH, HEIGHT = 900, 500

WHITE = (255, 255, 255)
RED   = (255, 0, 0)

FPS = 60
VEL = 5
BULLET_VEL = 7

# --- main ---  # PEP8: lower_case_names for variables

pygame.init()

WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('First Person Shooter')

original_player_image = pygame.surface.Surface((100, 100)).convert_alpha()
original_player_image.fill((255,0,0,255))
original_player_image = pygame.transform.rotate(pygame.transform.scale(original_player_image, (55, 40)), 90)

player_image = original_player_image.copy()
player_rect  = player_image.get_rect(centerx=100, centery=300)

def draw_window():
WIN.fill(WHITE)
WIN.blit(player_image, player_rect)
pygame.display.update()

def one_handle_movement(keys_pressed, one):
if keys_pressed[pygame.K_a] and one.left   - VEL > -15: # left
one.x -= VEL
if keys_pressed[pygame.K_d] and one.right  + VEL < 925: # right
one.x += VEL
if keys_pressed[pygame.K_w] and one.top    - VEL > -15: # up
one.y -= VEL
if keys_pressed[pygame.K_s] and one.bottom + VEL < 500: # down
one.y += VEL

def main():
global player_image
global player_rect

bullets = []

clock = pygame.time.Clock()
run = True
while run:

clock.tick(FPS)

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet = pygame.Rect()

# get positions

mouse_x,  mouse_y  = pygame.mouse.get_pos()
player_x, player_y = player_rect.center

# get angle
dx = mouse_x - player_x
dy = mouse_y - player_y

angle = math.degrees(math.atan2(dy, dx))

pygame.display.set_caption(f"player: {player_x}, {player_y} | mouse: {mouse_x}, {mouse_y} | angle: {angle}")

# rotate

player_image = pygame.transform.rotate(original_player_image, -angle).convert_alpha()

# assign original center to new rectagle

player_rect = player_image.get_rect(centerx=player_x, centery=player_y)

# ---

keys_pressed = pygame.key.get_pressed()
one_handle_movement(keys_pressed, player_rect)

draw_window()

pygame.quit()

if __name__ == '__main__':
main()


EDIT:

Pygame has pygame.math.Vector2.angle_to() to calculate angle (in degrees) between two verctors (points) but for some reason I got wrong results and couldn't find mistake.

mouse  = pygame.mouse.get_pos()
player = player_rect.center

angle = pygame.math.Vector2(player).angle_to(mouse)


BTW: today pygame doc is not accessible so I used link to its copy on archive.org.

EDIT:

This gives correct angle

mouse  = pygame.mouse.get_pos()
player = player_rect.center

dist  = pygame.math.Vector2(mouse) - pygame.math.Vector2(player)

angle = pygame.math.Vector2().angle_to(dist)


def main():
global player_image
global player_rect

bullets = []

clock = pygame.time.Clock()
run = True
while run:

clock.tick(FPS)

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet = pygame.Rect()

# get positions

mouse  = mouse_x,  mouse_y  = pygame.mouse.get_pos()
player = player_x, player_y = player_rect.center

# get angle

wrong_angle = pygame.math.Vector2(mouse).angle_to(player)   # gives results different then I expect

dist  = pygame.math.Vector2(mouse) - pygame.math.Vector2(player)
angle = pygame.math.Vector2().angle_to(dist)   # gives results different then I expect

pygame.display.set_caption(f"player: {player_x}, {player_y} | mouse: {mouse_x}, {mouse_y} | angle: {angle} | wrong: {wrong_angle}")

# rotate

player_image = pygame.transform.rotate(original_player_image, -angle).convert_alpha()

# assign original center to new rectagle

player_rect = player_image.get_rect(centerx=player_x, centery=player_y)

# ---

keys_pressed = pygame.key.get_pressed()
one_handle_movement(keys_pressed, player_rect)

draw_window()

pygame.quit()
$$$$
`