Sprites, Collisions y Animations

Pygame Game Development Course

Felipe A. Moreno-Vera

💥 Collisions

¿Qué es una Collision?

Una collision ocurre cuando dos objetos se superponen en el espacio. Detectarla es esencial para: recibir daño, recoger objetos, bloquear el movimiento, disparar, y más.

Pygame ofrece varios niveles de precisión, con un trade-off claro:

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

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

Rect-Rect Collision (AABB)

AABB = Axis-Aligned Bounding Box. El método más rápido y más 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. El método más rápido y más 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

Implementación Manual de AABB

AABB Collision — tests:
=======================================================
  Parcialmente superpuestos              → ✅ COLLISION
  Separados horizontalmente              → ❌ sin COLLISION
  B completamente dentro de A            → ✅ COLLISION
  Apenas tocándose (borde)               → ✅ COLLISION
  Separados por 1px                      → ❌ sin COLLISION

Circle-Circle Collision

Más preciso para objetos redondos (balas, monedas, personajes circulares). La lógica: hay collision entre dos círculos si la distancia entre sus centros es menor que la suma de sus radios.

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

  If d < rA + rB → collision

Circle-Circle Collision

Más preciso para objetos redondos (balas, monedas, personajes circulares). La lógica: hay collision entre dos círculos si la distancia entre sus centros es menor que la suma de sus radios.

    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 es la función de collision más poderosa de 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 la collision es solo la mitad del trabajo. La otra mitad es qué hacer cuando sucede.

Collision Response

Detectar la collision es solo la mitad del trabajo. La otra mitad es qué hacer cuando sucede.

Patrón 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 la collision es solo la mitad del trabajo. La otra mitad es qué hacer cuando sucede.

Patrón 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

Patrón 2 — Bounce (balas, pelotas)

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

Collision Response

Detectar la collision es solo la mitad del trabajo. La otra mitad es qué hacer cuando sucede.

Patrón 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

Patrón 2 — Bounce (balas, pelotas)

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

Patrón 3 — Destruction (objeto recogido, enemigo eliminado)

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

Comparación de Métodos de Collision

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

  Method                 Cost     Precision                  When to use
  --------------------------------------------------------------------------------
  AABB (Rect-Rect)       O(1)     Baja — caja invisible      Plataformas, ítems, la mayoría de los objetos
  Circle-Circle          O(1)     Media — circular           Balas, monedas, personajes redondos
  collide_rect_ratio     O(1)     Media — rect reducido      Hitbox más pequeño que el sprite
  collide_mask           O(n)     Máxima — píxel por píxel   Bosses, formas irregulares (usar con moderación)

Regla general: empieza con AABB. Cambia solo cuando la imprecisión
sea notable. Reserva collide_mask para muy pocos sprites.

Collisions — Resumen

  • AABB (colliderect) = el más rápido, suficiente para la mayoría de los casos
  • spritecollide(sprite, group, kill) = collision de uno contra muchos
  • groupcollide(g1, g2, k1, k2) = collision de group contra group
  • La response depende del tipo: separate, bounce, o destroy