Оглавление
Оглавление
Часть I: подготовка
- Введение
- 1. Краткая история NES
- 2. Фундаментальные понятия
- 3. Приступаем к разработке
- 4. Оборудование NES
- 5. Знакомство с языком ассемблера 6502
- 6. Заголовки и векторы прерываний
- 7. Зачем вообще этим заниматься?
- 8. Рефакторинг
Часть II: графика
13. Графика фона
Содержание:
- Таблица паттернов фона
- Составление таблицы имён
- Таблица атрибутов
- Дополнительные изменения
- Использование проектов NES Lightbox
- Домашняя работа
Прежде чем мы начнём перемещать спрайты по экрану, я хотел бы рассказать, как на NES работает графика фона. Мы изучили механику графики фона в Главе 9, а в этой главе мы рассмотрим код, необходимый для отображения фонов на экране.
Таблица паттернов фона
Для начала нам нужны графические тайлы в таблице паттернов фона. PPU имеет две таблицы паттернов для тайлов, один для спрайтов, второй для фонов. В предыдущих главах мы добавили несколько тайлов в таблицу паттернов спрайтов. Теперь мы добавим тайлы в таблицу фонов.
Для этой главы я дополнил файл CHR, добавив в него тайлы фона. Там есть тайлы для чисел и букв (позволяющие отображать текст), квадраты со сплошной раскраской с каждым из четырёх цветов палитры, а также несколько тайлов звёздного неба. Их можно просмотреть (и отредактировать) в NES Lightbox. Нашей долгосрочной целью будет создание шутера с вертикальным скроллингом в стиле Star Soldier.
[
Star Soldier, разработанная Hudson Soft и выпущенная для Famicom в 1986 году, а для NES в 1988 году — первая из игр в серии шутеров с вертикальным скроллингом.]
Новые тайлы фона в Bank B файла starfield.chr.
Чтобы использовать эти новые тайлы фона, нам нужно написать таблицу имён. По умолчанию таблица атрибутов будет заполнена одними нулями, то есть каждый тайл в таблице имён будет отрисовываться в первой палитре. Чтобы использовать другие палитры, нам нужно выполнить запись и в таблицу атрибутов.
Составление таблицы имён
Для начала давайте разберёмся, куда в таблицу имён помещать новые тайлы. Нам нужно записывать только в те адреса таблицы имён, куда мы намереваемся поместить тайл, а во всех остальных местах будет использоваться стандартный цвет фона из наших палитр. В NES Lightbox нажмите на тайл в правой стороне экрана, а затем нажмите в любом месте большой области слева, чтобы «рисовать» тайлом. Вот пример фона, который мы будем использовать:
Размещение тайлов фона в NES Lightbox.
Создав нужную вам схему, вы можете сохранить эти результаты в файл
.nam
. В меню NES Lightbox выберите Nametables → Save Nametable As..., а затем выберите имя файла и место для сохранения файла. Чтобы в дальнейшем загрузить фон для редактирования, откройте NES Lightbox, выберите Nametables → Open Nametable..., а затем найдите сохранённый ранее файл .nam
.При наведении курсора мыши на любой тайл на большой левой панели строка состояния в нижней части NES Lightbox показывает полезную информацию о записи таблиц имён и таблиц атрибутов. Как говорилось в Главе 9, NES имеет четыре таблицы имён, первая из которых начинается по адресу памяти PPU
$2000
. В нижней строке состояния NES Lightbox отображаются «смещение в таблице имён» и «смещение атрибутов» для каждой позиции тайлов, то есть то расстояние в байтах, на котором она находится в таблице имён или атрибутов. Чтобы упростить работу, в ней так же отображается адрес местоположения тайла в каждой таблице имён и таблице атрибутов.Нижняя строка состояния NES Lightbox, в которой отображаются смещения в таблицах имён и атрибутов и соответствующие адреса памяти PPU для такого смещения в каждой из четырёх таблиц имён и таблиц атрибутов.
Мы можем использовать эту информацию для заполнения таблицы имён. Как вы должно быть помните, таблица имён — это просто последовательность номеров тайлов; выбор палитры происходит в таблице атрибутов. Для создания таблицы имён нам нужно записать соответствующий номер тайла в каждый адрес таблицы имён, как показано в NES Lightbox. Давайте начнём с тайла «большой» звезды (номер тайла
$2f
, это можно увидеть, наведя курсор мыши на тайл в правом окне «Tileset» и посмотрев на строку состояния в нижней части окна приложения). Наводя курсор на места, где используется тайл большой звезды, мы видим, что нам нужно записать значение $2f
в следующие адреса памяти PPU:$206b
$2157
$2223
$2352
Процесс будет таким же, который мы использовали ранее для палитр и спрайтов: считывание из
PPUSTATUS
, запись адреса, в который нужно передать данные, в PPUADDR
(сначала «старший»/левый байт, потом «младший»/правый), а затем отправка данных в PPUDATA
. Ранее мы применяли для этого циклы, воспользовавшись тем, что последовательные операции записи в PPUDATA
автоматически увеличивают адрес на единицу. Однако на этот раз нам нужно выполнять запись в совершенно нелинейные адреса памяти, поэтому придётся повторять процесс полностью для каждого тайла фона. Вот код для записи первой «большой звезды» в таблицу имён:Здесь мы можем сэкономить на количестве вводимых команд, сохранив номер тайла в регистр, отдельный от того, который используем для считывания
PPUSTATUS
и записи в PPUADDR
; мы применяем для номера тайла регистр X, чтобы последующие операции записи того же тайла в таблицу имён не требовали его повторной загрузки.Мы можем воспользоваться той же процедурой, чтобы добавить в таблицу имён две разновидности «маленькой» звезды (
$2d
и $2e
). Чтобы использовать для тайлов разные цвета, нам также нужно будет написать и таблицу атрибутов.Таблица атрибутов
Палитры тайлов фона задаются через таблицу атрибутов, использующую один байт для хранения информации о палитре квадрата тайлов фона размером 4x4. Чтобы изменить палитры, используемые для конкретного тайла, нужно навести курсор мыши на этот тайл в левой панели таблицы имён и запомнить «Attribute offset» тайла. Далее нужно разобраться, частью какой области тайлов размером 2x2 (верхняя левая, верхняя правая, нижняя левая, нижняя правая) является тайл, который мы хотим изменить. Чтобы найти границы байта таблицы атрибутов, нажмите на «Attribute grid» внизу панели таблицы имён. При этом на неё накладывается красная сетка из пунктирных линий, демонстрирующая границы площади покрытия каждого байта таблицы атрибутов квадратов 4x4.
Например, давайте изменим палитру, используемую для отрисовки первой «большой звезды» в таблице имён. Наведя на неё курсор в панели просмотра таблицы имён, мы можем найти смещение атрибутов (
$02
), а посмотрев на наложенную сетку атрибутов, мы увидим, в пределах какого набора тайлов 4x4 расположен выбранный тайл (здесь это область 2x2 внизу справа).Поиск информации о смещении атрибутов для «большой звезды» наверху таблицы имён.
Каждый байт таблицы атрибутов хранит информацию о палитрах для четырёх областей 2x2 тайлов фона, используя для каждой области по два бита. Слева направо восемь битов байта таблицы атрибутов содержат номер палитры для нижней правой, нижней левой, верхней правой и верхней левой областей 2x2.
Схема битов в байте таблицы атрибутов.
В данном случае мы хотим изменить палитру для нижней правой части байта таблицы атрибутов, поэтому нам нужно изменить два самых левых бита в байте. По умолчанию таблица атрибутов состоит из одних нулей, то есть каждая область 2x2 фона использует первую палитру фона. Давайте изменим наш тайл, чтобы он использовал вторую палитру (
01
) вместо первой (00
). Чтобы изменить только нижнюю правую область 2x2, а другие части этой области 4x4 оставить с первой палитрой, мы запишем байт %01000000
в соответствующий адрес памяти PPU ($23c2
). Вот как это должно выглядеть:Помните, что так мы присвоим вторую палитру всем тайлам в этой области 2x2. В данном случае задаваемые нами тайлы фона расположены довольно далеко друг от друга, но если ваши фоны будут расположены более плотно, то вам придётся тщательно продумывать, где выполнять переход от одной палитры фона к другой. При выборе палитры (нажатием на любой цвет в палитре) перед размещением тайла в панели таблицы имён NES Lightbox обновит связанную с ней таблицу атрибутов и все тайлы в соответствующей области 2x2, чтобы они использовали новую палитру, что поможет вам выявить потенциальные конфликты таблицы атрибутов.
Дополнительные изменения
Когда наши игры будут становиться более сложными, нужно будет внести небольшие изменения в обработчики сброса и NMI, чтобы предотвратить странные (и сложные в отладке) графические баги. В обработчик сброса я добавил цикл, задающий позиции Y всех спрайтов так, чтобы они находились за экраном (т. е. присваивающий любой значение больше
$ef
).При запуске состояние памяти процессора может находиться в случайном состоянии, из-за чего части буфера OAM по адресу
$0200
могут содержать ложные спрайтовые данные. Этот цикл скрывает все спрайты с экрана, пока мы не зададим их все явным образом, благодаря чему эти «фантомные» спрайты не будут видны игроку.Во-вторых, обработчик NMI должен будет задавать позицию скроллинга таблиц имён. Подробнее мы поговорим о скроллинге в одной из следующих глав, но пока вам достаточно знать, что мы задаём позицию скроллинга для отображения первой таблицы имён без скроллинга. Если не задать эту позицию скроллинга явным образом, то мы можем случайно отобразить комбинацию таблиц имён в непредсказуемой точке скроллинга.
Теперь, когда мы используем и спрайты, и фоны, нам нужно задать все восемь палитр. Для этого я расширил определения палитр в сегменте
RODATA
следующим образом:Первые четыре палитры — это палитры фонов, а второй набор из четырёх палитр — это палитры спрайтов. Обратите внимание, что я также изменил цвета, использованные в первой палитре спрайтов, чтобы «космический корабль» выглядел лучше.
Поскольку теперь нам нужно записывать больше, чем просто четыре байта палитры, я изменил записывающий палитры цикл так, чтобы в нём использовалось
CPX #$20
(16 значений) вместо CPX #$04
.Использование проектов NES Lightbox
Прежде чем мы перейдём к домашней работе по этой главе, давайте рассмотрим функцию «Project» программы NES Lightbox. «Проект» — это список файлов, составляющих рабочую среду (набор тайлов, палитры, таблица имён) и текущий выбранный банк набора тайлов. Файл проекта использует полные пути к каждому файлу, то есть файлы проектов, к сожалению, нельзя переносить между компьютерами.
Для создания файла проекта выберите Project → Save Project As… в меню NES Lightbox. Введите имя файла проекта и нажмите на «OK». Все ранее открытые файлы будут сохранены как часть файла проекта. Любой ранее не сохранённый набор тайлов, набор палитр или таблица имён будут сохранены с именем проекта. Если ваш проект называется «starfield.nesproj», то ранее не сохранённая таблица имён будет сохранена как «starfield.nam». После сохранения (или загрузки) проекта можно выбрать Project → Save All Project Files, чтобы сохранить файлы .chr, .nam и .pal проекта с текущими данными в приложении.
Чтобы в дальнейшем восстановить рабочую среду, выберите Project → Open Project… и найдите ранее сохранённый файл .nesproj. Если какие-то файлы проекта изменили местоположение на диске, то можно просто отредактировать файл .nesproj любым текстовым редактором — это обычный файл JSON, содержащий путь к каждому файлу.
Домашняя работа
Чтобы вам быстрее было начать, скачайте весь код из этой главы. Вот как пока выглядит наш проект при запуске в эмуляторе:
В качестве домашней работы добавьте новый тайл в таблицу паттернов фона («Bank B» в NES Lightbox), используйте новый тайл в таблице имён и дополните таблицу атрибутов, чтобы новый тайл использовал несколько палитр. Можно использовать имеющийся файл
.nam
для быстрой подготовки среды NES Lightbox. Не забудьте сохранить изменённый файл .chr
и пересобирайте все файлы .asm при каждом внесении изменений.