Sprites, Collisions e Animations

Curso de Desenvolvimento de Jogos com Pygame

Felipe A. Moreno-Vera

🎬 Animations

Como funciona a animation 2D?

A animation funciona exatamente como um flipbook: uma sequΓͺncia de imagens (frames) que mudam a uma certa velocidade para criar a ilusΓ£o de movimento.

Frame 0    Frame 1    Frame 2    Frame 3
  🧍         🚢         🧍         🚢
  ↑          ↑          ↑          ↑
 100ms      100ms      100ms      100ms   β†’ repeats

Importante

A velocidade de animation β‰  FPS do jogo. A 60 FPS, vocΓͺ pode animar a 10 FPS (mudar de frame a cada 6 frames do jogo).

Spritesheet

Um spritesheet Γ© uma ΓΊnica imagem que contΓ©m todos os frames de todas as animations. Γ‰ usado para:

  • Reduzir o nΓΊmero de arquivos individuais
  • Carregar a memΓ³ria da GPU de forma mais eficiente
  • Simplificar a gestΓ£o das animations
spritesheet.png:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [idle_0][idle_1][idle_2][idle_3]                 β”‚  ← row 0: idle
β”‚ [walk_0][walk_1][walk_2][walk_3][walk_4][walk_5] β”‚  ← row 1: walk
β”‚ [jump_0][jump_1][jump_2]                         β”‚  ← row 2: jump
β”‚ [attack_0][attack_1][attack_2][attack_3]         β”‚  ← row 3: attack
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Spritesheet

Um spritesheet Γ© uma ΓΊnica imagem que contΓ©m todos os frames de todas as animations. Γ‰ usado para:

  • Reduzir o nΓΊmero de arquivos individuais
  • Carregar a memΓ³ria da GPU de forma mais eficiente
  • Simplificar a gestΓ£o das animations
spritesheet.png:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [idle_0][idle_1][idle_2][idle_3]                 β”‚  ← row 0: idle
β”‚ [walk_0][walk_1][walk_2][walk_3][walk_4][walk_5] β”‚  ← row 1: walk
β”‚ [jump_0][jump_1][jump_2]                         β”‚  ← row 2: jump
β”‚ [attack_0][attack_1][attack_2][attack_3]         β”‚  ← row 3: attack
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Para extrair um frame use Surface.subsurface(rect):

sheet = pygame.image.load("spritesheet.png").convert_alpha()
SIZE = 64  # Frame size

# Extract frame at column=2, row=1
frame = sheet.subsurface(pygame.Rect(2 * SIZE, 1 * SIZE, SIZE, SIZE))

Animation Timer

O timer controla quando avanΓ§ar para o prΓ³ximo frame. Existem duas abordagens:

Animation Timer

O timer controla quando avanΓ§ar para o prΓ³ximo frame. Existem duas abordagens:

Abordagem 1 β€” Frame counter (mais simples)

self.current_frame = 0
self.counter       = 0
self.anim_speed    = 8  # Change frame every 8 game frames

def update(self):
    self.counter += 1
    if self.counter >= self.anim_speed:
        self.counter = 0
        self.current_frame = (self.current_frame + 1) % len(self.frames)
    self.image = self.frames[self.current_frame]

Animation Timer

O timer controla quando avanΓ§ar para o prΓ³ximo frame. Existem duas abordagens:

Abordagem 1 β€” Frame counter (mais simples)

self.current_frame = 0
self.counter       = 0
self.anim_speed    = 8  # Change frame every 8 game frames

def update(self):
    self.counter += 1
    if self.counter >= self.anim_speed:
        self.counter = 0
        self.current_frame = (self.current_frame + 1) % len(self.frames)
    self.image = self.frames[self.current_frame]

Abordagem 2 β€” Millisecond timer (recomendado)

self.current_frame  = 0
self.last_update    = pygame.time.get_ticks()  # current ms
self.frame_duration = 100  # ms per frame (100ms = 10 anim FPS)

def update(self):
    now = pygame.time.get_ticks()
    if now - self.last_update >= self.frame_duration:
        self.last_update = now
        self.current_frame = (self.current_frame + 1) % len(self.frames)
    self.image = self.frames[self.current_frame]

Animation Timer

O timer controla quando avanΓ§ar para o prΓ³ximo frame.

Dica

Approach 2 Γ© preferido porque se o FPS do jogo cair, a animation continua sendo reproduzida na mesma velocidade em tempo real na tela.

Animation State Machine

Um personagem tem mΓΊltiplas animations dependendo do seu state. A state machine decide qual reproduzir.

                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚       IDLE          β”‚
                 β”‚  (standing still)   β”‚
                 β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚       β”‚
               move    β”‚       β”‚ airborne
               key     ↓       ↓
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”‚   WALK    β”‚ β”‚   JUMP    β”‚
               β”‚ (walking) β”‚ β”‚ (jumping) β”‚
               β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                     β”‚ release     β”‚ land
                     β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                            ↓
                          IDLE

Animation State Machine – cΓ³digo

class Player(pygame.sprite.Sprite):
    def __init__(self):
        ...
        self.state = 'idle'   # Current state
        self.animations = {
            'idle':   [frame_idle_0, frame_idle_1, frame_idle_2],
            'walk':   [frame_walk_0, frame_walk_1, frame_walk_2, frame_walk_3],
            'jump':   [frame_jump_0, frame_jump_1],
        }

    def change_state(self, new_state):
        if self.state != new_state:        # Only if it actually changed
            self.state = new_state
            self.current_frame = 0         # Reset animation on state change

    def update(self):
        # Determine state
        keys = pygame.key.get_pressed()
        if self.airborne:
            self.change_state('jump')
        elif keys[pygame.K_LEFT] or keys[pygame.K_RIGHT]:
            self.change_state('walk')
        else:
            self.change_state('idle')

        # Animate
        frames = self.animations[self.state]
        self.image = frames[self.current_frame % len(frames)]

State Machine Simulation

SimulaΓ§Γ£o da state machine:
=============================================
  idle[1/2] β†’ idle[2/2] β†’ idle[0/2]
  walk[1/3] β†’ walk[2/3] β†’ walk[3/3] β†’ walk[0/3] β†’ walk[1/3]
  jump[1/1] β†’ jump[0/1] β†’ jump[1/1] β†’ jump[0/1]
  idle[1/2] β†’ idle[2/2]
  attack[1/3] β†’ attack[2/3] β†’ attack[3/3] β†’ attack[0/3] β†’ attack[1/3]
  idle[1/2] β†’ idle[2/2] β†’ idle[0/2]

TransiΓ§Γ΅es de state:
  idle β†’ walk
  walk β†’ jump
  jump β†’ idle
  idle β†’ attack
  attack β†’ idle

Animations β€” Resumo

  • Spritesheet = uma ΓΊnica imagem contendo todos os frames
  • ms timer = independente do FPS (approach preferido)
  • State machine = determina qual animation reproduzir
  • Reinicie current_frame = 0 sempre que o state mudar