Search code examples
pythonpython-3.xvectorpygamenormalize

How do I move an object towards the mouse without having the mouse position affect speed in Pygame?


So I'm trying to test out a simple combat system in Pygame where the player can basically shoot a projectile towards an area based on where the mouse position is. So for example when he clicks on the top left of his screen, the projectile moves towards there at a steady speed. I've created a function that will move each bullet in a list, here's the function.

def move_bullet(bullet_pos, direction):
    # TODO Make the speed of the bullet the same no matter which direction it's being fired at

    bullet_pos[0] += direction[0]/50
    bullet_pos[1] += direction[1]/50

    bullet_rect = pygame.Rect((bullet_pos[0], bullet_pos[1]), BULLET_SIZE)
    return bullet_rect

The direction is calculated by subtracting the mouse's vector position by the player's vector position when the mousebuttondown event is triggered.

However, I have noticed that the closer I get to the player/origin of the bullet, the slower the bullet goes because the direction vector is smaller so the speed is differs depending on your mouse's position. I've heard of Vector normalization but I have no idea how to implement it because after doing a bit of research, apparently you normalize Vectors by getting it's magnitude and dividing the X and Y values by the magnitude? I got it from Khan Academy but it doesn't actually work. And I'm pulling my hair out over this so I have no choice but to ask this question here.

TL; DR

How do I normalize a Vector in Pygame?


Solution

  • If you have to points

    x1 = 10
    y1 = 10
    
    x2 = 100
    y2 = 500
    

    then you can calculate distance and use pygame.math.Vector2

    import pygame
    
    dx = x2-x1
    dy = y2-y1
    
    distance = pygame.math.Vector2(dx, dy)
    

    or

    v1 = pygame.math.Vector2(x1, y1)
    v2 = pygame.math.Vector2(x2, y2)
    
    distance = v2 - v1
    

    and then you can normalize it

    direction = distance.normalize()
    

    It should always gives distance 1

    print('distance:', direction[0]**2 + direction[1]**2)  # 0.999999999999
    # or
    print('distance:', direction.length() )
    

    And then you move object using speed

    pos[0] += direction[0] * speed
    pos[1] += direction[1] * speed
    

    EDIT:

    If you will use Rect

    SIZE = (10, 10)
    bullet_rect = pygame.Rect((0, 0), SIZE)
    bullet_rect.center = (x1, y1)
    

    then you can also calculate

    distance = v2 - bullet_rect.center
    
    direction = distance.normalize()
    

    and move it with one line

    bullet_rect.center += direction * speed
    

    Rect has many useful functions. But has one minus - it keeps position as integers so it rounds float values and sometimes it gives strange moves or lost one pixel every few moves.


    Doc: PyGame.math