304. Renderizar un sprite 8x8

Renderizar un sprite 8x8 en datos de framebuffer.

Lección 304 de 356 · tests/chapter_09_sprite_rendering/test_304_render_one_sprite_8x8.py

Archivo a actualizar

emulator/rendering/sprite_renderer.py

Por qué existe este paso

Los pasos anteriores de sprites decodificaron entradas OAM, decodificaron atributos de sprite y construyeron paletas de sprite. Ahora dibujamos un sprite en un Framebuffer puro.

Este es el primer paso de sprites que produce datos de píxeles visibles, pero sigue siendo pequeño y controlado:

one SpriteEntry
one CHR tile
one selected sprite palette
one Framebuffer target

Ejemplo de implementación sugerido

def render_sprite_8x8_to_framebuffer(
    framebuffer: Framebuffer,
    sprite: SpriteEntry,
    pattern_table: bytes,
    sprite_palettes: SpritePalettes,
) -> None:
    attributes = decode_sprite_attributes(sprite.attributes)

    tile_start = sprite.tile_index * 16
    tile_end = tile_start + 16

    if tile_end > len(pattern_table):
        raise ValueError("Pattern table does not contain sprite tile bytes")

    tile_bytes = pattern_table[tile_start:tile_end]
    color_indexes = decode_chr_tile(tile_bytes)
    palette = sprite_palettes[attributes.palette_id]

    for tile_y in range(8):
        for tile_x in range(8):
            color_index = color_indexes[tile_y][tile_x]

            if color_index == 0:
                continue

            screen_x = sprite.x + tile_x
            screen_y = sprite.y + tile_y

            if not (0 <= screen_x < framebuffer.width):
                continue
            if not (0 <= screen_y < framebuffer.height):
                continue

            framebuffer.set_pixel(screen_x, screen_y, palette[color_index])

Regla importante de transparencia

Para los sprites, el índice de color CHR 0 es transparente. No debe sobrescribir el píxel existente del framebuffer.

Simplificación importante de la posición Y

El posicionamiento real de sprites en la NES tiene una peculiaridad de hardware en la que el valor Y almacenado en OAM no es exactamente la línea de barrido superior visible. Este paso del tutorial usa el valor crudo sprite.y como posición Y en pantalla. Un comportamiento de posición/temporización más preciso puede añadirse posteriormente.

Fuera del alcance

  • soporte de volteo horizontal/vertical
  • prioridad de sprite detrás del fondo
  • renderizado de los 64 sprites
  • política de composición fondo/sprites
  • sprite 0 hit
  • sprite overflow
  • pygame

Ejecutar esta lección

uv run pytest tests/chapter_09_sprite_rendering/test_304_render_one_sprite_8x8.py -v