Pygame Moving Sprites

How to move a sprites in pygame

import pygame
from pygame.sprite import Sprite

class MySprite(Sprite):
    def __init__(self, image, position):
        Sprite.__init__(self)
        self.image = image
        self.rect = image.get_rect(topleft=position)

    def move(self, x, y)
        self.rect.x += x
        self.rect.y += y

Look good right. You be wrong. If the framerates change. So does sprite movement. Moving a sprite. Should have nothing to do framerates. It shell aways move the same distance per second.

How do we achieve this. There two ways. Use a timer or time it takes between frames. Time between frames is called delta time. It is in milliseconds and pygame gives you an easy way to get it. You use clock = pygame.time.Clock(). Then in mainloop delta = clock.tick(fps). Delta is the smoothest way to move a sprite.

class MySprite(Sprite):
    def __init__(self, image, position):
        Sprite.__init__(self)
        self.image = image
        self.rect = image.get_rect(topleft=position)
        # Hold floats since rect only handle ints
        self.position = pygame.Vector2(position)
        # Direction it is heading. Currently going no where.
        self.direction = pygame.Vector2(0, 0)
        self.speed = 0.1

    def move(self, delta):
        self.position.x += self.speed * delta * self.direction.x
        self.position.y += self.speed * delta * self.direction.y
        self.rect.topleft = self.position

    # Set direction from angle
    def from_angle(self, angle):
        # import math
        rads = math.radians(angle)
        self.direction = pygame.Vector2(sin(rads), cos(rads))

    # Set direction from vector
    def from_vector(self, vector):
        self.direction = (vector - self.position).normalize()

With a little math. Sprite can move smoothly at any framerate. Traveling the same distance per second. Of course speed tell has fast it moving. Delta tells how much that frame to move. Direction tells us where to move.

What pygame.Vector2 normalize doing ? It set the length to one. This gives us the same data from_angle does. With out having to get angle from 2 vectors. Then calling from_angle to set the direction.

Example

Example show you how to move with keyboard and mouse.

import pygame
from statemachine import State
from pygame.sprite import Sprite

class MySprite(Sprite):
    def __init__(self, image, position):
        Sprite.__init__(self)
        self.image = image
        self.rect = image.get_rect(topleft=position)
        # Hold floats since rect only handle ints
        self.position = pygame.Vector2(position)
        # Direction it is heading. Currently going no where.
        self.direction = pygame.Vector2(0, 0)
        self.speed = pygame.Vector2(0.1, 0.12)

    def move(self, delta):
        self.position.x += self.speed.x * delta * self.direction.x
        self.position.y += self.speed.y * delta * self.direction.y
        self.rect.topleft = self.position

    def from_angle(self, angle):
        # import math
        rads = math.radians(angle)
        self.direction = pygame.Vector2(math.sin(rads), math.cos(rads))

    def from_vector(self, vector):
        self.direction = (vector - self.position).normalize()


class Example(State):
    def __init__(self):
        self.image = pygame.Surface((16, 16))
        self.image.fill(pygame.Color('dodgerblue'))

        position = State.machine.rect.centerx - 8, State.machine.rect.centery - 8
        self.sprite = MySprite(self.image, position)

    def on_draw(self, surface):
        surface.fill(pygame.Color('black'))
        surface.blit(self.sprite.image, self.sprite.rect)

    def on_update(self, delta):
        keys = pygame.key.get_pressed()
        x = 0
        y = 0

        # Key board pressed
        if keys[pygame.K_UP]:
            y -= 1

        if keys[pygame.K_DOWN]:
            y += 1

        if keys[pygame.K_LEFT]:
            x -= 1

        if keys[pygame.K_RIGHT]:
            x += 1

        if x == 0 and y == 0:
            self.sprite.direction = pygame.Vector2(x, y)
        else:
            self.sprite.direction = pygame.Vector2(x, y).normalize()

        # Mouse button pressed
        mouse = pygame.mouse.get_pressed()
        # Left mouse button
        if mouse[0]:
            self.sprite.from_vector(pygame.Vector2(pygame.mouse.get_pos()))

        # Move the sprite
        self.sprite.move(delta)

if __name__ == '__main__':
    pygame.init()
    State.machine_setup('EntryBox Example', 800, 600, True)
    State.machine.flip(Example())
    State.machine.mainloop()