331. 组合水平不透明度掩码视口
从两个相邻的名称表掩码组合出一个已滚动的不透明度掩码。
第 331 / 356 · tests/chapter_13_scrolling/test_331_compose_horizontal_opaque_mask_viewport.py
待更新文件
emulator/rendering/background_viewport.py参考文档
https://www.nesdev.org/wiki/PPU_scrolling
https://www.nesdev.org/wiki/PPU_nametables现有流程
build_background_opaque_mask() converts one nametable into a 256x240 list[bool].
Sprite priority and sprite-zero-hit detection index that mask using screen
coordinates.新行为
This step does not replace the existing mask builder. It composes two already
constructed masks into the same horizontal viewport introduced for framebuffers
in Step 330.坐标模型
left mask: logical X 0-255
right mask: logical X 256-511
logical X = (viewport X + screen X) modulo 512对于视口 X 200
screen X 0-55 <- left X 200-255
screen X 56-255 <- right X 0-199不变量
- 每个源都恰好包含 256 * 240 个布尔条目
- 结果是一个新的 256 * 240 条目列表
- 源掩码保持不变
- Y 行不移动
- 掩码映射与帧缓冲区映射完全一致
常见误解
该掩码并不是从最终的 RGB 颜色推导不透明度的。它保留的是原始背景图案颜色索引是否非零。
范围之外
- 从 CHR 或名称表字节构建掩码
- 读取 PPU 内存
- 与 Console 集成
- 与 sprite-zero-hit 集成
- 垂直滚动
- pygame
完整示例实现
# emulator/rendering/background_viewport.py
from emulator.rendering.nametable_renderer import BackgroundOpaqueMask
def compose_horizontal_opaque_mask_viewport(
left: BackgroundOpaqueMask,
right: BackgroundOpaqueMask,
viewport_x: int,
) -> BackgroundOpaqueMask:
expected_size = NAMETABLE_PIXEL_WIDTH * NAMETABLE_PIXEL_HEIGHT
if len(left) != expected_size:
raise ValueError(
f"Left background opacity mask must contain {expected_size} entries"
)
if len(right) != expected_size:
raise ValueError(
f"Right background opacity mask must contain {expected_size} entries"
)
logical_width = NAMETABLE_PIXEL_WIDTH * 2
result = [False] * expected_size
for screen_y in range(NAMETABLE_PIXEL_HEIGHT):
row_start = screen_y * NAMETABLE_PIXEL_WIDTH
for screen_x in range(NAMETABLE_PIXEL_WIDTH):
logical_x = (viewport_x + screen_x) % logical_width
if logical_x < NAMETABLE_PIXEL_WIDTH:
source = left
source_x = logical_x
else:
source = right
source_x = logical_x - NAMETABLE_PIXEL_WIDTH
destination_index = row_start + screen_x
source_index = row_start + source_x
result[destination_index] = source[source_index]
return result运行本课
uv run pytest tests/chapter_13_scrolling/test_331_compose_horizontal_opaque_mask_viewport.py -v