NES TDD

312. Приоритет спрайтов использует маску непрозрачности фона

Используем маску непрозрачности фона для применения бита 5 приоритета спрайтов.

Урок 312 из 356 · tests/chapter_09_sprite_rendering/test_312_sprite_priority_uses_background_opaque_mask.py

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

emulator/rendering/sprite_renderer.py

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

Шаг 310 построил маску непрозрачности фона для каждого пикселя. Шаг 311 прокинул эту маску через конвейер рендеринга спрайтов. Этот шаг наконец использует маску в рендерере одного спрайта.

Правило

If a sprite pixel is non-transparent,
and the sprite has priority bit 5 set,
and the background mask says that screen pixel is opaque,
then keep the background pixel and skip drawing the sprite pixel.

Важный порядок

1. sprite CHR color index 0 -> skip
2. offscreen pixel -> skip
3. behind-background sprite over opaque background -> skip
4. otherwise draw sprite pixel

Пример фрагмента реализации

def render_sprite_8x8_to_framebuffer (...) -> ...

...
... 

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

# --- ADD THIS NEW BLOCK ---
if (
    background_opaque_mask is not None
    and attributes.is_behind_background
    and background_opaque_mask[screen_y * framebuffer.width + screen_x]
):
    continue
# --- END NEW BLOCK ---

framebuffer.set_pixel(...)

Почему после отсечения? Индекс маски использует экранные координаты. Если индексировать маску до проверки того, что пиксель находится на экране, спрайты за пределами экрана могут прочитать неверный элемент маски или вызвать IndexError.

Вне рамок

  • построение маски внутри Console.render_framebuffer()
  • выбор таблицы фоновых паттернов по биту 4 PPUCTRL
  • sprite 0 hit
  • переполнение спрайтов
  • pygame

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

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