pygame.Vector2 is a pair of floats, x and y, with the math you need for movement built in. Positions, velocities and directions are all vectors:
pos = pygame.Vector2(100, 200)
velocity = pygame.Vector2(3, -1)
pos += velocity # (103, 199)
Because a vector holds floats, it also avoids the Rect rounding trap: keep the position in a Vector2 and copy it into the Rect for drawing and collisions.
Same speed in every direction
Pressing Right and Down together moves (1, 1) per step, which is about 1.41 long, so diagonal movement is 41% faster. Normalize the direction first, which keeps it pointing the same way but makes its length 1:
Press Play, click the game and steer with the arrows. The if matters: normalizing (0, 0) raises ValueError: Can't normalize Vector of length zero.
Chasing a target
Subtract two positions to get the arrow from one to the other. Normalize it and multiply by a speed to take one step along it. This enemy follows the mouse:
Move the pointer over the game. When the enemy is closer than one step, it lands on the target instead of overshooting and jittering. pygame-ce does the same in one call: enemy.move_towards_ip(target, speed).
Useful methods
| Method | Returns |
|---|---|
v.length() | The length (also v.magnitude()) |
v.normalize() | Same direction, length 1 |
v.scale_to_length(n) | Sets the length to n, in place |
a.distance_to(b) | The distance between two points |
v.rotate(degrees) | A copy turned by that angle |
a.angle_to(b) | The angle in degrees from a to b |
a.lerp(b, t) | The point t of the way from a to b (0 to 1) |
a.move_towards(b, step) | A step from a toward b, never past it (pygame-ce) |
a.dot(b) | The dot product |
v.reflect(normal) | v bounced off a surface with that normal |
+, -, * by a number, / by a number, == and v.x, v.y all work as you would expect. A Vector2 can be passed anywhere pygame wants a position: pygame.draw.circle(screen, color, pos, 10) or rect.center = pos (the Rect rounds it).
Aiming and rotation
To point a ship at the mouse, get the angle of the direction and rotate the image by it. as_polar() returns the length and the angle in degrees:
direction = pygame.Vector2(pygame.mouse.get_pos()) - ship_pos
_, angle = direction.as_polar()
rotated = pygame.transform.rotate(ship_image, -angle - 90)
The minus sign is there because pygame's y axis points down while rotate turns counterclockwise; the - 90 depends on which way the image faces. pygame.transform shows how to blit the rotated image around its center.
Frequently Asked Questions
What is Vector2 in pygame?
pygame.Vector2 (also pygame.math.Vector2) holds two floats, x and y. You can add, subtract and multiply vectors, and it has methods for length, direction, distance and rotation. Games use it for positions and velocities.
Why does my player move faster diagonally?
Moving 5 pixels right and 5 down at once covers about 7 pixels. Put the input in a Vector2 and normalize it before multiplying by the speed: if direction.length() > 0: direction = direction.normalize().
How do I make an enemy follow the player?
Subtract to get the direction, normalize it and step along it: step = (player_pos - enemy_pos).normalize() * speed. pygame-ce also has enemy_pos.move_towards(player_pos, speed), which stops exactly on the target.
Why do I get Can't normalize Vector of length zero?
normalize() divides by the length, and a vector of (0, 0) has none. Check if v.length() > 0: (or if v:) before normalizing.