322. Позиция sprite zero hit в PPU
Извлечь позицию перекрытия спрайта 0 из текущего состояния PPU.
Урок 322 из 356 · tests/chapter_11_sprite_zero_hit/test_322_ppu_sprite_zero_hit_position.py
Файл для обновления
emulator/rendering/sprite_zero_hit.pyЗачем нужен этот шаг
Существующая чистая вспомогательная функция требует явных данных рендеринга
find_sprite_zero_hit_position(
sprite_zero,
pattern_table,
background_opaque_mask,
)Такой API полезен для точечных тестов, но будущий шаг Console начинается с PPU. Этот шаг добавляет небольшой адаптер, который извлекает нужные данные из текущего состояния PPU и делегирует их существующей чистой вспомогательной функции.
Хранить обе функции в sprite_zero_hit.py при текущем размере проекта разумно:
find_sprite_zero_hit_position(...)
explicit data -> overlap position
ppu_sprite_zero_hit_position(ppu)
PPU state -> explicit data -> overlap positionВторая функция — это адаптер, а не ещё один алгоритм проверки перекрытия.
Необходимое извлечение
- декодировать только запись OAM 0
- построить маску непрозрачности фона через ppu_background_to_opaque_mask(ppu)
- выбрать таблицу шаблонов спрайта, используя бит 3 PPUCTRL
- прочитать PATTERN_TABLE_SIZE байт через PpuBus
- делегировать выполнение find_sprite_zero_hit_position(...)
Пример предлагаемой реализации
from emulator.ppu.chr_decoder import PATTERN_TABLE_SIZE, decode_chr_tile
from emulator.ppu.ppu import CTRL_SPRITE_PATTERN_TABLE, PPU
from emulator.rendering.ppu_background_renderer import (
PATTERN_TABLE_0_ADDR,
PATTERN_TABLE_1_ADDR,
ppu_background_to_opaque_mask,
)
from emulator.rendering.sprite_renderer import (
SpriteEntry,
decode_sprite_attributes,
decode_sprite_entry,
)
...
def ppu_sprite_zero_hit_position(
ppu: PPU,
) -> SpriteZeroHitPosition | None:
sprite_zero = decode_sprite_entry(
oam=ppu.oam,
sprite_index=0,
)
background_opaque_mask = ppu_background_to_opaque_mask(ppu)
sprite_pattern_table_base = (
PATTERN_TABLE_1_ADDR
if ppu.ctrl & CTRL_SPRITE_PATTERN_TABLE
else PATTERN_TABLE_0_ADDR
)
sprite_pattern_table = bytes(
ppu.ppu_bus.read(sprite_pattern_table_base + offset)
for offset in range(PATTERN_TABLE_SIZE)
)
return find_sprite_zero_hit_position(
sprite_zero=sprite_zero,
pattern_table=sprite_pattern_table,
background_opaque_mask=background_opaque_mask,
)Важное различие
Непрозрачность фона использует путь выбора таблицы шаблонов фона, основанный на бите 4 PPUCTRL. Данные CHR спрайта 0 используют бит 3 PPUCTRL. Эти два выбора могут указывать на разные таблицы шаблонов.
Важная граница
Эта функция только возвращает позицию. Она не устанавливает PPUSTATUS и не вызывает PPU.set_sprite_zero_hit_position(). Подключение в Console остаётся более поздним шагом.
Вне рамок
- подключение в Console
- автоматическое планирование
- правила включения рендеринга PPUMASK
- правила отсечения левого края
- аппаратное исключение x=255
- коррекция OAM Y+1
- проверка на Super Mario Bros.
Запустить этот урок
uv run pytest tests/chapter_11_sprite_zero_hit/test_322_ppu_sprite_zero_hit_position.py -v