Sprites, Collisions e Animations

Pygame Game Development Course

Felipe A. Moreno-Vera

💥 Collisions

O que é uma Collision?

Uma collision ocorre quando dois objetos se sobrepõem no espaço. Detectá-la é essencial para: receber dano, coletar itens, bloquear o movimento, atirar, e mais.

Pygame oferece vários níveis de precisão, com um trade-off claro:

Faster ←──────────────────────────────→ More Precise

  Rect-Rect      Circle-Circle      Pixel-perfect
  (AABB)         (distance)         (mask)

Rect-Rect Collision (AABB)

AABB = Axis-Aligned Bounding Box. O método mais rápido e mais usado.

# Method 1: rect.colliderect()
if player.rect.colliderect(enemy.rect):
    print("Collision!")

# Method 2: rect.collidepoint() — point inside rect
if player.rect.collidepoint(pygame.mouse.get_pos()):
    print("Mouse is over the player")

# Method 3: rect.collidelist() — against a list of rects
index = player.rect.collidelist([rect1, rect2, rect3])
# Returns the index of the first collided rect, or -1

Rect-Rect Collision (AABB)

AABB = Axis-Aligned Bounding Box. O método mais rápido e mais usado.

# Method 1: rect.colliderect()
if player.rect.colliderect(enemy.rect):
    print("Collision!")

# Method 2: rect.collidepoint() — point inside rect
if player.rect.collidepoint(pygame.mouse.get_pos()):
    print("Mouse is over the player")

# Method 3: rect.collidelist() — against a list of rects
index = player.rect.collidelist([rect1, rect2, rect3])
# Returns the index of the first collided rect, or -1

Implementação Manual de AABB

AABB Collision — tests:
=======================================================
  Parcialmente sobrepostos               → ✅ COLLISION
  Separados horizontalmente              → ❌ sem COLLISION
  B totalmente dentro de A               → ✅ COLLISION
  Apenas se tocando (borda)              → ✅ COLLISION
  Separados por 1px                      → ❌ sem COLLISION

Circle-Circle Collision

Mais preciso para objetos redondos (balas, moedas, personagens circulares). A lógica: há collision entre dois círculos se a distância entre seus centros for menor que a soma de seus raios.

    A            B
   (●)          (●)
       ←── d ──→

  If d < rA + rB → collision

Circle-Circle Collision

Mais preciso para objetos redondos (balas, moedas, personagens circulares). A lógica: há collision entre dois círculos se a distância entre seus centros for menor que a soma de seus raios.

    A            B
   (●)          (●)
       ←── d ──→

  If d < rA + rB → collision
import math

def circle_collision(cx1, cy1, r1, cx2, cy2, r2):
    distance = math.sqrt((cx2 - cx1)**2 + (cy2 - cy1)**2)
    return distance < r1 + r2

# Optimization: avoid sqrt using squared distance
def circle_collision_fast(cx1, cy1, r1, cx2, cy2, r2):
    dx = cx2 - cx1
    dy = cy2 - cy1
    dist_sq = dx*dx + dy*dy
    return dist_sq < (r1 + r2)**2   # No sqrt needed

spritecollide() — Group Collisions

Esta é a função de collision mais poderosa do Pygame.

# Detect collision of ONE sprite against a GROUP
hits = pygame.sprite.spritecollide(
    player,    # individual sprite
    bullets,   # group to check against
    True       # True = remove colliding sprites from the group
)
# hits is a list of sprites that collided
if hits:
    health -= len(hits)

# Detect collisions between TWO GROUPS
clashes = pygame.sprite.groupcollide(
    bullets,   # group 1
    enemies,   # group 2
    True,      # remove from group 1
    True       # remove from group 2
)
# clashes is a dict: {bullet: [enemies_hit], ...}

# Using circular collision (more precise)
hits = pygame.sprite.spritecollide(
    player, bullets, True,
    collided=pygame.sprite.collide_circle
)

# Using pixel masks (maximum precision, slowest)
hits = pygame.sprite.spritecollide(
    player, bullets, True,
    collided=pygame.sprite.collide_mask
)

Collision Response

Detectar a collision é apenas metade do trabalho. A outra metade é o que fazer quando isso acontece.

Collision Response

Detectar a collision é apenas metade do trabalho. A outra metade é o que fazer quando isso acontece.

Padrão 1 — Separation (platforms, paredes)

# Move first, then correct
player.rect.x += vel_x
for platform in platforms:
    if player.rect.colliderect(platform.rect):
        if vel_x > 0:   # Moving right
            player.rect.right = platform.rect.left
        elif vel_x < 0: # Moving left
            player.rect.left = platform.rect.right
        vel_x = 0

Collision Response

Detectar a collision é apenas metade do trabalho. A outra metade é o que fazer quando isso acontece.

Padrão 1 — Separation (platforms, paredes)

# Move first, then correct
player.rect.x += vel_x
for platform in platforms:
    if player.rect.colliderect(platform.rect):
        if vel_x > 0:   # Moving right
            player.rect.right = platform.rect.left
        elif vel_x < 0: # Moving left
            player.rect.left = platform.rect.right
        vel_x = 0

Padrão 2 — Bounce (balas, bolas)

if bullet.rect.left <= 0 or bullet.rect.right >= WIDTH:
    bullet.vel_x *= -1   # Reverse horizontal velocity

Collision Response

Detectar a collision é apenas metade do trabalho. A outra metade é o que fazer quando isso acontece.

Padrão 1 — Separation (platforms, paredes)

# Move first, then correct
player.rect.x += vel_x
for platform in platforms:
    if player.rect.colliderect(platform.rect):
        if vel_x > 0:   # Moving right
            player.rect.right = platform.rect.left
        elif vel_x < 0: # Moving left
            player.rect.left = platform.rect.right
        vel_x = 0

Padrão 2 — Bounce (balas, bolas)

if bullet.rect.left <= 0 or bullet.rect.right >= WIDTH:
    bullet.vel_x *= -1   # Reverse horizontal velocity

Padrão 3 — Destruction (item coletado, inimigo eliminado)

items_picked = pygame.sprite.spritecollide(player, items, True)  # True = kill
score += len(items_picked) * 10

Comparação de Métodos de Collision

Collision method comparison
============================================================

  Method                 Cost     Precision                  When to use
  --------------------------------------------------------------------------------
  AABB (Rect-Rect)       O(1)     Baixa — caixa invisível    Plataformas, itens, a maioria dos objetos
  Circle-Circle          O(1)     Média — circular           Balas, moedas, personagens redondos
  collide_rect_ratio     O(1)     Média — rect reduzido      Hitbox menor que o sprite
  collide_mask           O(n)     Máxima — pixel a pixel     Bosses, formas irregulares (usar com moderação)

Regra geral: comece com AABB. Troque apenas quando a imprecisão
for perceptível. Reserve collide_mask para pouquíssimos sprites.

Collisions — Resumo

  • AABB (colliderect) = o mais rápido, suficiente para a maioria dos casos
  • spritecollide(sprite, group, kill) = collision de um contra muitos
  • groupcollide(g1, g2, k1, k2) = collision de group contra group
  • A response depende do tipo: separate, bounce, ou destroy