Search code examples
pythonanimationpygamesprite-sheet

Changing Sprite animation Speed (pygame)


I managed to animate my player using my 'animate' function that I had created - Cycles through the list of character images per frame while the character is moving. However, I also found that this happens at the same speed in which the game is running; and was hoping there was a simple way to change it so that the sprite animates at a slower speed than the game FPS.

Here is my sprite class code:

class Civilian(pg.sprite.Sprite):
    def __init__(self, game, x, y):
        self.groups = game.all_sprites, game.player1group, game.bothplayers
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self._layer = PLAYER1_LAYER
        self.image = pg.Surface((61, 67))
        self.rect = self.image.get_rect()
        self.hit_rect = PLAYER_HIT_RECT
        self.hit_rect.center = self.rect.center
        self.playerspeed = 90
        self.vel = vec(0, 0)
        self.pos = vec(x , y)
        self.move = 0
        self.speedboost = False
        self.last_dir = 'down'#
        self.anim_speed = 0

    def animate(self, direction):
        if direction == 'right':
            self.spritesheet = pg.image.load('walk right civ.png') # Loading the right directional movement spritesheet into the variable
        if direction == 'left':
            self.spritesheet = pg.image.load('walk left civ.png')
        if direction == 'up':
            self.spritesheet = pg.image.load('walk up civ.png')
        if direction == 'down':
            self.spritesheet = pg.image.load('walk down civ.png')
        self.frames = [] # List which will contain each cell of the spritesheet
        # Adding the cells to the list #
        self.frames.append(self.spritesheet.subsurface(pg.Rect(0, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(61, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(122, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(183, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(244, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(305, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(366, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(427, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(488, 0, 61, 67)).convert_alpha())
        # Number of frames/cells
        self.frames_number = len(self.frames)
        # Current animation frame
        self.current_frame = 0
        # Frame rectangle
        self.frame_rect = self.frames[0].get_rect()
        self.last_dir = direction

    def get_keys(self):
        self.vel= vec(0, 0)
        keys = pg.key.get_pressed()

        if keys[pg.K_a]:    # Const. subtracts player speed from velocity (E.g. Moves sprite to the left)
            self.vel.x= -self.playerspeed
            self.move += 1
            self.moving = 'left' # Uses different spritesheet depending on direction
        elif keys[pg.K_d]:    # Const. adds player speed value to velocity (E.g. Moves sprite to the right)
            self.vel.x= self.playerspeed
            self.move += 1
            self.moving = 'right'
        elif keys[pg.K_w]:    # Const. subtracts player speed value from y velocity (Moves player upwards; opposite)
            self.vel.y= -self.playerspeed
            self.move += 1
            self.moving = 'up'
        elif keys[pg.K_s]: # Const. adds player speed value to y velocity (Moves player downwards; opposite)
            self.vel.y= self.playerspeed
            self.move += 1
            self.moving = 'down'

    def add_speed(self):
        pass

    def collide_with_player2(self, dir, ifColliding):
        if dir == 'x':
            collides = pg.sprite.spritecollide(self, self.game.player2group, False, collide_player_hit_rect)
            if collides:
                if self.vel.x > 0:
                    self.pos.x = collides[0].hit_rect.left - self.hit_rect.width / 2
                if self.vel.x < 0:
                    self.pos.x = collides[0].hit_rect.right + self.hit_rect.width / 2
                self.vel.x = 0
                self.hit_rect.centerx = self.pos.x
                print("collide x")
                if random.randint(0, 100) <= 4:
                    random.choice(self.game.thief_hit_sounds).play()
                self.ifColliding = True

        if dir == 'y':
            collides = pg.sprite.spritecollide(self, self.game.player2group, False, collide_player_hit_rect)
            if collides:
                if self.vel.y > 0:
                    self.pos.y = collides[0].hit_rect.top - self.hit_rect.height / 2
                if self.vel.y < 0:
                    self.pos.y = collides[0].hit_rect.bottom + self.hit_rect.height / 2
                self.vel.y = 0
                self.hit_rect.centery = self.pos.y
                print("collide y")
                if random.randint(0, 100) <= 4:
                    random.choice(self.game.thief_hit_sounds).play()
                self.ifColliding = True

    def collide_with_walls(self, dir):
        if dir == 'x':
            collides = pg.sprite.spritecollide(self, self.game.walls, False, collide_hit_rect)
            if collides:
                if self.vel.x > 0:
                    self.pos.x = collides[0].rect.left - self.hit_rect.width / 2
                if self.vel.x < 0:
                    self.pos.x = collides[0].rect.right + self.hit_rect.width / 2
                self.vel.x = 0
                self.hit_rect.centerx = self.pos.x
        if dir == 'y':
            collides = pg.sprite.spritecollide(self, self.game.walls, False, collide_hit_rect)
            if collides:
                if self.vel.y > 0:
                    self.pos.y = collides[0].rect.top - self.hit_rect.height / 2
                if self.vel.y < 0:
                    self.pos.y = collides[0].rect.bottom + self.hit_rect.height / 2
                self.vel.y = 0
                self.hit_rect.centery = self.pos.y

    def update(self):
        # frame updates
        self.anim_speed += 1
        self.moving = 'idle'
        self.animate(self.last_dir) # Sets the down spritesheet as default
        self.get_keys()
        if self.moving == 'up':
            self.animate(self.moving) # Uses the up-movement spritesheet if char moving upwards
        if self.moving == 'down':
            self.animate(self.moving) # Same as above, different direction
        if self.moving == 'left':
            self.animate(self.moving)
        if self.moving == 'right':
            self.animate(self.moving)
        self.ifColliding = False




        self.rect.center = self.pos
        self.pos += self.vel * self.game.dt
        self.hit_rect.centerx = self.pos.x
        self.collide_with_walls('x'), self.collide_with_player2('x', self.ifColliding)
        self.hit_rect.centery = self.pos.y
        self.collide_with_walls('y'), self.collide_with_player2('y', self.ifColliding)
        self.rect.center = self.hit_rect.midtop




        if self.ifColliding == True:
            Thief.health -= COL_DAMAGE
            print(Thief.health)
        self.current_frame = (self.current_frame + self.move) % self.frames_number
        if self.moving == 'idle':
            self.current_frame = 0
        self.image = self.frames[self.current_frame] # Image of sprite changes as program cycles through the sheet

Solution

  • You can decouple the animation rate from the frame rate by tying it to time in some way.

    One way is to use pygame.time.get_ticks() to return the number of milliseconds since pygame.init() was called. If you store this value, you can measure how much time has elapsed and animate appropriately.

    def update(self):
        self.elapsed = pygame.time.get_ticks() - self.elapsed
        if self.elapsed > 500: # animate every half second
            self.animate()
    

    Note: you'll also need to initialise self.elapsed in the constructor.