295. Visualización manual de fondo en pygame desde main
Añadir la visualización manual de fondo en pygame a main_only_background.py.
Lección 295 de 356 · tests/chapter_08_manual_main/test_295_manual_main_pygame_background_display.py
Archivo a crear/actualizar en la carpeta raíz
main_only_background.pyPor qué existe este paso
core_validator.py demuestra que el emulador puede arrancar una ROM local y avanzar fotogramas sin pygame. main_only_background.py es el ejecutor visual manual histórico solo de fondo: debe usar pygame para mostrar el Framebuffer de fondo producido por el emulador tras cada fotograma.
Flujo de trabajo recomendado
Empieza copiando la estructura funcional de core_validator.py y añade solo las piezas de pygame/visualización que falten:
- importar pygame
- importar draw_framebuffer desde tools.show_framebuffer
- definir SCALE
- crear un framebuffer inicial para las dimensiones de la ventana
- abrir una ventana de pygame
- procesar eventos pygame.QUIT
- tras cada paso de fotograma, renderizar el framebuffer de fondo
- dibujar el framebuffer y volcar la pantalla
- llamar a pygame.quit() en finally
Límite importante
pygame está permitido en main_only_background.py porque es un punto de entrada manual/frontend. pygame no debe importarse en los módulos del núcleo del emulador.
Regla legal/de pruebas importante
El repositorio del tutorial no debe incluir archivos de ROM comerciales. Las pruebas automatizadas no deben requerir MarioBros.nes ni abrir una ventana real de pygame.
Hash de referencia usado durante el desarrollo del tutorial [Mario Bros. (World).nes]:
MD5 5d7bcc400a2fb5fa27346da345d3bb62 MarioBros.nes
SHA1 314b6e46e814f955b52ac954f67dab849582fe77Este hash es solo una referencia manual. Las pruebas no deben requerir este archivo ni este hash exacto, porque los usuarios pueden tener volcados/revisiones legales distintos.
Ejemplo de implementación sugerido
from pathlib import Path
import pygame
from emulator.bus.cpu_bus import CpuBus
from emulator.cartridge.cartridge import Cartridge
from emulator.console import Console
from emulator.cpu.cpu import CPU
from tools.show_framebuffer import draw_framebuffer
ROM_PATH = Path("MarioBros.nes")
debug_mode = False
SCALE = 3
def main() -> None:
if not ROM_PATH.exists():
raise FileNotFoundError(
"MarioBros.nes not found. Provide your own legal local copy. "
"This file is intentionally not included in the tutorial repository."
)
cartridge = Cartridge.from_ines_bytes(ROM_PATH.read_bytes())
cpu_bus = CpuBus(cartridge=cartridge)
cpu = CPU(cpu_bus)
console = Console(cpu=cpu, ppu=cpu_bus.ppu)
cpu.reset()
framebuffer = console.render_background_framebuffer()
print(f"Loaded {ROM_PATH}")
print(f"CPU reset PC = ${cpu.pc:04X}")
print("Starting frame loop. Close the window or press Ctrl+C to stop.")
pygame.init()
try:
window = pygame.display.set_mode(
(framebuffer.width * SCALE, framebuffer.height * SCALE)
)
pygame.display.set_caption("NES Background")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
executed = console.step_until_next_frame()
framebuffer = console.render_background_framebuffer()
draw_framebuffer(window, framebuffer, SCALE)
pygame.display.flip()
if debug_mode:
print(
f"frame={console.ppu.frame} "
f"pc=${cpu.pc:04X} "
f"instructions={executed}"
)
except KeyboardInterrupt:
print("Detenido por el usuario.")
finally:
pygame.quit()
if __name__ == "__main__":
main()Comando manual
uv run python main_only_background.pyComportamiento manual esperado
main_only_background.py abre una ventana de pygame y muestra el framebuffer de fondo actual. La ventana puede verse incompleta porque los sprites no están implementados en este ejecutor histórico. Cierra la ventana o pulsa Ctrl+C para detenerlo.
Ejemplo aproximado de la expectativa visual
+------------------------------+
| |
| MARIO BROS. |
| |
| 1 PLAYER GAME A |
| 1 PLAYER GAME B |
| 2 PLAYER GAME A |
| 2 PLAYER GAME B |
| |
| background is shown |
| sprites are missing |
| |
+------------------------------+Tras 30 segundos - 1 minuto, también deberías ver un fondo/composición similar al escenario clásico de Mario Bros. de 1983. Los sprites siguen faltando, pero la escena de fondo debería hacer que el emulador se sienta vivo:
+------------------------------+
| I-0000 TOP-0000 II-0000 |
| |
| ==== ==== |
|== ==|
| |
| ────────────── |
|───── ─────|
| |
| |
| ─────────── POW ─────────── |
|==== ====|
|------------------------------|
+------------------------------+Esto es solo un boceto ASCII aproximado. La señal manual importante es que los tiles de fondo/título/escenario aparezcan y cambien con el tiempo. Es esperable que falten los personajes/enemigos en movimiento hasta que se implemente el renderizado de sprites.
Nota de rendimiento
El ejecutor manual de pygame puede sentirse lento en este momento. Eso es esperable en esta etapa. El ayudante draw_framebuffer actual es deliberadamente simple y dibuja muchos rectángulos escalados desde Python. Una futura optimización podrá sustituirlo por una vía de subida de framebuffer más rápida, pero este paso se centra en la salida visual esperada y en los límites de arquitectura, no en la velocidad.
Por qué esta prueba no llama a main()
main_only_background.py abre una ventana real de pygame y ejecuta un bucle manual. Las pruebas automatizadas deben permanecer finitas y deben limitarse a inspeccionar la estructura.
Fuera de alcance
- Optimización de subida rápida de framebuffer
- Mapeo de teclado/mando de pygame
- Renderizado de sprites
- Verificar píxeles visuales exactos de una ROM comercial
- Llamar a main() desde pytest
Ejecutar esta lección
uv run pytest tests/chapter_08_manual_main/test_295_manual_main_pygame_background_display.py -v