Manual Extendido de Sprites para SNES

Introducción a los Sprites en SNES

Los sprites (también llamados OBJ - Objects) son elementos gráficos que se mueven independientemente de los fondos. La SNES tiene capacidades avanzadas para manejar sprites:

Arquitectura del Sistema de Sprites

Estructura de Memoria OAM

La memoria de atributos de objetos (OAM) tiene dos partes:

Dirección Tamaño Propósito
$0200-$03FF 512 bytes Tabla baja (atributos básicos)
$0400-$041F 32 bytes Tabla alta (tamaño y bits extra de X)

Formato de la Tabla Baja OAM

Cada sprite usa 4 bytes:
Byte 0: X (bits 0-7)
Byte 1: Y
Byte 2: Número de tile
Byte 3: Atributos:
   Bit 0-2: Paleta (0-7)
   Bit 3: Prioridad (0=alta, 1=baja)
   Bit 4-5: Sin uso
   Bit 6: Flip horizontal (1=activado)
   Bit 7: Flip vertical (1=activado)
        

Formato de la Tabla Alta OAM

Cada byte controla 4 sprites (2 bits por sprite):
Bits 0-1: Sprite 0
Bits 2-3: Sprite 1
...
Valores:
  00: Tamaño pequeño (8x8 o 16x16 según OBJSEL)
  01: Tamaño mediano (8x16 o 16x32)
  10: Tamaño grande (16x16 o 32x32)
  11: Tamaño extra grande (16x32 o 32x64)
        

Configuración Inicial de Sprites

Registro OBJSEL ($2101)

Bit 0-2: Dirección base de tiles en VRAM (en bloques de $2000)
Bit 3: Selección de nombres de sprite (0=$0000, 1=$1000)
Bit 4-5: Tamaño de sprites pequeños:
  00: 8x8 y 16x16
  01: 8x8 y 32x32
  10: 8x8 y 64x64
  11: 16x16 y 32x32
Bit 6-7: Tamaño de sprites grandes (combinación con bits 4-5)
        

Ejemplo de Configuración

; Configurar sprites:
; - Tiles en VRAM $0000-$1FFF
; - Sprites pequeños: 16x16
; - Sprites grandes: 32x32
lda #%01000010      ; Bits 6-7: 01, Bits 4-5: 00, Bit 3: 0, Bits 0-2: 010
sta $2101           ; Registro OBJSEL
        

Carga y Manejo de Sprites

Cargar Datos de Sprite

Los sprites se cargan en VRAM (memoria de video) usando DMA:

; Cargar gráficos de sprite en VRAM $0000
LoadSpriteGraphics:
    lda #$80
    sta $2115        ; Incremento de VRAM +1 después de $2119
    
    lda #$00         ; Dirección VRAM $0000
    sta $2116
    lda #$00
    sta $2117
    
    lda #$01         ; DMA desde banco $01
    sta $4304
    lda #$18         ; Puerto $2118 (VRAM data)
    sta $4301
    ldx #sprite_data ; Dirección fuente
    stx $4302
    ldx #$2000       ; Tamaño: 8KB
    stx $4305
    lda #$01         ; Modo 1 (transferencia de bytes)
    sta $4300
    lda #$01         ; Iniciar DMA canal 1
    sta $420B
        

Actualizar Posición de Sprites

; Actualizar posición de sprite 0
UpdateSpritePosition:
    lda sprite_x     ; Variable de posición X
    sta $0200        ; Byte bajo X
    lda #$00         ; Byte alto X (bit 9)
    sta $0201
    
    lda sprite_y     ; Variable de posición Y
    sta $0202
    
    lda #$01         ; Tile número 1
    sta $0203
    
    lda #%00110011   ; Paleta 3, prioridad 1, no flip
    sta $0204
    
    ; Actualizar OAM mediante DMA
    lda #$02         ; DMA canal 2
    sta $420B
        

Técnicas Avanzadas de Sprites

Sprite Multiplexing

Técnica para mostrar más de 32 sprites por línea:

; Ejemplo básico de multiplexing
HandleSpriteMultiplexing:
    ldx #$00         ; Contador de sprites
    ldy #$00         ; Índice OAM
    
ProcessSprite:
    lda sprite_y,x   ; Obtener Y del sprite
    cmp scanline     ; Comparar con línea actual
    bcc SkipSprite   ; Si está arriba, saltar
    
    ; Copiar datos del sprite a OAM
    lda sprite_x,x
    sta $0200,y
    ; ... copiar otros atributos
    
    iny
    iny
    iny
    iny              ; Avanzar 4 bytes por sprite
    
SkipSprite:
    inx
    cpx #$80         ; Máximo 128 sprites
    bne ProcessSprite
        

Sprites de 16 colores

Para sprites con más detalle:

; Configurar paleta para sprite de 16 colores
; Paleta 0: 16 colores (2 tiles consecutivos)
lda #%00000000       ; Paleta 0, prioridad 0, no flip
sta $0203            ; Atributos del sprite

; En la paleta:
; Color 0 es transparente
; Colores 1-15 son los colores visibles
        

Sprites Grandes

; Configurar sprite grande (32x32)
; Primero configurar OBJSEL para tamaños grandes
lda #%01100010       ; Sprites grandes: 32x32
sta $2101

; Luego en OAM high table:
lda #%01010101       ; Todos los sprites en este grupo como grandes
sta $0400            ; Configura 4 sprites a la vez

; Los tiles deben organizarse en VRAM:
; Tile 0: esquina superior izquierda
; Tile 1: superior derecha
; Tile 2: inferior izquierda
; Tile 3: inferior derecha
        

Animación de Sprites

Animación Básica por Tiles

; Variables para animación
animation_timer: .db 0
animation_frame: .db 0

UpdateAnimation:
    dec animation_timer
    bne NoFrameChange
    
    lda #$08         ; Velocidad de animación
    sta animation_timer
    
    inc animation_frame
    lda animation_frame
    and #$03         ; Ciclar entre 0-3
    sta animation_frame
    
    ; Calcular tile basado en frame
    asl a            ; Multiplicar por 2 (tiles de 16x16)
    clc
    adc #$01         ; Tile base
    sta $0203        ; Actualizar tile del sprite
    
NoFrameChange:
        

Animaciones Complejas

; Tabla de animaciones
AnimationTable:
    .dw WalkRightFrames, JumpFrames, AttackFrames

WalkRightFrames:
    .db $01, $02, $03, $02  ; Secuencia de tiles
    .db $FF                 ; Marcador de fin
    
; Sistema de animación avanzado
UpdateComplexAnimation:
    ldx animation_state
    lda AnimationTable,x
    sta animation_ptr
    lda AnimationTable+1,x
    sta animation_ptr+1
    
    ldy animation_frame
    lda (animation_ptr),y
    cmp #$FF
    bne ValidFrame
    
    ; Reiniciar animación
    ldy #$00
    lda (animation_ptr),y
    
ValidFrame:
    sta $0203        ; Actualizar tile
    iny
    sty animation_frame
        

Detección de Colisiones

Detección Básica por Cajas

; Verificar colisión entre sprite A y B
CheckCollision:
    ; Coordenada X derecha de A >= X izquierda de B
    lda sprite_a_x
    clc
    adc #$0F         ; Ancho del sprite (16px)
    cmp sprite_b_x
    bcc NoCollision
    
    ; Coordenada X izquierda de A <= X derecha de B
    lda sprite_b_x
    clc
    adc #$0F
    cmp sprite_a_x
    bcc NoCollision
    
    ; Repetir para Y (vertical)
    lda sprite_a_y
    clc
    adc #$0F
    cmp sprite_b_y
    bcc NoCollision
    
    lda sprite_b_y
    clc
    adc #$0F
    cmp sprite_a_y
    bcc NoCollision
    
    ; Colisión detectada
    sec
    rts
    
NoCollision:
    clc
    rts
        

Uso del Registro de Colisión

La SNES tiene un registro que detecta colisiones entre sprites y fondos:

; Esperar colisión
WaitForCollision:
    lda $4212        ; Registro HVBJOY
    and #$40         ; Comprobar flag de colisión
    beq WaitForCollision

    ; Leer resultados
    lda $4218        ; Coordenada X del sprite que colisionó
    lda $4219        ; Coordenada Y
    lda $421A        ; Número de sprite (bits 0-6)
                     ; Bit 7: 1=colisión con fondo
        

Optimización de Sprites

Ordenar Sprites por Y

; Ordenar sprites por coordenada Y para optimizar
SortSpritesByY:
    ldx #$00
    ldy #$00
    
SortLoop:
    lda sprite_y,x
    cmp sprite_y,y
    bcc NoSwap
    
    ; Intercambiar posiciones
    pha
    lda sprite_y,y
    sta sprite_y,x
    pla
    sta sprite_y,y
    
    ; Intercambiar otros atributos...
    
NoSwap:
    iny
    cpy #$80
    bne SortLoop
    
    inx
    cpx #$7F
    bne SortLoop
        

Uso Eficiente de OAM

Ejemplos Prácticos

Sprite de Personaje Jugable

; Variables del jugador
player_x: .dw $0080
player_y: .dw $00A0
player_frame: .db $00
player_dir: .db $00  ; 0=derecha, 1=izquierda

UpdatePlayerSprite:
    ; Actualizar posición X
    lda player_x
    sta $0200
    lda player_x+1
    sta $0201
    
    ; Actualizar posición Y
    lda player_y
    sta $0202
    
    ; Determinar tile basado en dirección y frame
    lda player_dir
    beq FacingRight
    
    ; Mirando izquierda (usar tiles 4-7)
    lda player_frame
    clc
    adc #$04
    bra SetTile
    
FacingRight:
    lda player_frame
    
SetTile:
    sta $0203
    
    ; Configurar atributos
    lda player_dir
    beq NoFlip
    lda #%01000000   ; Flip horizontal
    bra StoreAttrs
    
NoFlip:
    lda #%00000000
    
StoreAttrs:
    ora #%00110000   ; Paleta 3, prioridad 1
    sta $0204
        

Sistema de Partículas

; Estructura de partícula (4 bytes)
.struct Particle
    y_pos    .byte
    x_pos    .byte
    tile     .byte
    attrs    .byte
.endst

; Inicializar partículas
InitParticles:
    ldx #$00
    lda #$F0         ; Y fuera de pantalla
    
InitLoop:
    sta particle_y,x
    inx
    cpx #MAX_PARTICLES*4
    bne InitLoop

; Actualizar partículas
UpdateParticles:
    ldx #$00
    
ParticleLoop:
    lda particle_y,x
    cmp #$F0
    beq NextParticle
    
    ; Mover partícula
    dec particle_y,x
    
    ; Verificar si salió de pantalla
    cmp #$F0
    bne NextParticle
    
    ; Desactivar partícula
    lda #$F0
    sta particle_y,x
    
NextParticle:
    txa
    clc
    adc #.sizeof(Particle)
    tax
    cmp #MAX_PARTICLES*.sizeof(Particle)
    bne ParticleLoop