NES TDD

311. Прокидываем маску непрозрачности фона через конвейер спрайтов

Прокидываем маску непрозрачности фона через конвейер рендеринга спрайтов.

Урок 311 из 356 · tests/chapter_09_sprite_rendering/test_311_thread_background_opaque_mask_through_sprite_pipeline.py

Файлы для обновления

emulator/rendering/frame_compositor.py
emulator/rendering/sprite_renderer.py

Зачем нужен этот шаг

Шаг 310 создал BackgroundOpaqueMask. Прежде чем применять поведение приоритета спрайтов, мы сначала делаем так, чтобы соответствующие функции принимали эту маску и передавали её дальше.

Это сохраняет постепенность курса

Step 310 -> build the mask
Step 311 -> thread the mask through function signatures
Step 312 -> use the mask to enforce sprite priority bit 5

Требуемая форма API

def composite_background_and_sprites(
    background: Framebuffer,
    oam: bytes | bytearray,
    pattern_table: bytes,
    sprite_palettes: SpritePalettes,
    # --- ADD THIS NEW LINE ---
    background_opaque_mask: BackgroundOpaqueMask | None = None,
) -> Framebuffer:
    ...
    render_oam_sprites_to_framebuffer(
        framebuffer,
        oam,
        pattern_table,
        sprite_palettes,
        # --- ADD THIS NEW LINE ---
        background_opaque_mask,
    )
    ...


def render_oam_sprites_to_framebuffer(
    framebuffer: Framebuffer,
    oam: bytes | bytearray,
    pattern_table: bytes,
    sprite_palettes: SpritePalettes,
    # --- ADD THIS NEW LINE ---
    background_opaque_mask: BackgroundOpaqueMask | None = None,
) -> None:
    ...
    render_sprite_8x8_to_framebuffer(
        framebuffer,
        sprite,
        pattern_table,
        sprite_palettes,
        # --- ADD THIS NEW LINE ---
        background_opaque_mask,
    )


def render_sprite_8x8_to_framebuffer(
    framebuffer: Framebuffer,
    sprite: SpriteEntry,
    pattern_table: bytes,
    sprite_palettes: SpritePalettes,
    # --- ADD THIS NEW LINE ---
    background_opaque_mask: BackgroundOpaqueMask | None = None,
) -> None:
    ...

Также добавьте импорт типа там, где это нужно

# --- ADD THIS NEW LINE ---
from emulator.rendering.nametable_renderer import BackgroundOpaqueMask

Важно

На этом шаге маску использовать пока не требуется. Следующий шаг добавит правило:

if sprite is behind background and background_opaque_mask[pixel] is True:
    skip drawing this sprite pixel

Почему опциональный None? Более старые тесты и более ранние шаги курса уже вызывают эти вспомогательные функции рендеринга без маски фона. Сохранение аргумента опциональным сохраняет обратную совместимость, пока мы развиваем конвейер.

Вне рамок

  • применение бита 5 приоритета спрайтов
  • изменение Console.render_framebuffer()
  • построение маски из состояния PPU
  • sprite 0 hit
  • переполнение спрайтов
  • pygame

Запустить этот урок

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