В интернете много статей показывающих как скролить фон на два экрана (скажем так это стандартно), а вот как скролить фон на несколько экранов вперед как например в марио, или чип и дейле мало где можно найти а если и находиться такое в интернете то объяснение как это сделать? Довольно сложное для понимания человеку который только начинает свой путь в nes…
Давайте разберем алгоритм как сделать прокрутку к примеру на 4 экрана:
-
Переключить nametable на следующий экран от позиции аппаратной прокрутки
-
Проверить сместился ли персонаж в право (или лево) на 8 пикселей и нужно ли отрисовать (буду писать отрисовать на самом деле имея ввиду заполнить) новый столбец в nametable
-
Заполнить attribute table для нового столбца (то есть указать его цвет)
-
Обновить карту коллизий на экран (более подробно будет описано в следующей статье)
Позволю сделать себе, несколько важных замечаний, прежде чем мы перейдем к реализации задуманного плана.
-
Нам надо перерисовать следующий за позиции аппаратного скролинга (либо перед ним через 32 тайла, а проще говоря в следующим nametable 2 ($2400-$27FF), либо если позиция скрола находиться в nametable 2 то надо перерисовать фон в nametable 1 ($2000-$23FF).
-
Nametable — как следует из названия представляет с собой таблицу, но в ней 4 строки и 255 столбцов, если можно так выразиться возьмем к примеру nametable 1:
-
$2000 — $20FF
-
$2100 — $21FF
-
$2200 — $22FF
-
$2300 — $23FF
-
-
Разрешение экрана nes 32х32 тайла ( с допущением последние просто обрезаются)
Давайте наглядно посмотрим на nametable и выведем формулу по которой будем вычислять

На самом деле, замечание номер 2 было для большего понимания как заполняется nametable в первую очередь рассмотрим реализацию алгоритма определения того что прокрутка сместилась на 8 пикселей. Тут надо вспомнить что 8 а точнее 7 (0-7) это %00000111 то есть в последних цифрах числа содержится цифра 7 и если ее отфильтровать через операцию END и если в этом случае будет установлен флаг Z то получается что позиция прокрутки увеличилось на 8 (либо содержит 8)
LDA scrollPosition AND #%00000111 BNE NewColumnCheckDone JSR drawNewCollumnNametable
Алгоритм заполнения nametable тоже довольно прост, нам всего лишь надо вычислить младший и старший байт текущего nametable, оговорюсь что надо определить в каком nametable находиться прокрутка и просто переключить текущую таблицу имен на следующую и обратно. В момент когда прокрутка находиться в позиции $FD это из разрешения nes по горизонтали у нас 253 точки.
LDA #%1000100 ; set to increment +32 mode EOR nameTable STA $2000 LDA $2002 ; read PPU status to reset the high/low latch LDA colHig STA $2006 ; write the high byte of column address LDA colLow STA $2006 ; write the low byte of column address
Пока все просто, но надо вычислить еще позицию столбца для заполнения нового nametable. Формула довольно простая scroll/8 номер тайла это младший байт, старший байт будет равен nametable*4 + $24 в ассемблере нету операций умножения и деления но можно смещать вправо и влево что приведет к умножение на 2 и соответственно деление на 2 каждое смещение числа, в коде это выглядит так:
LDA scrollPosition LSR A LSR A LSR A STA colLow STA columnNumber LDA nameTable ASL A ASL A CLC ADC #$24 STA colHig
Карту я определил в секции RODATA как массив байт пример вы сможете увидите в репозитории на github.com где есть полный листинг программы, ссылку приведу ниже. Я не стал заморачиваться и писать еще несколько десятков строк что бы переворачивать массив таким образом что бы можно было и человеку читать и программе. По этому определил его как повернутым на 90% и в зеркальном отражение. В общем строка в массиве ровна столбцу в таблице имен. Для того что бы его прочитать с определенного момента и загрузить в таблицу имен, нам просто необходимо вычислить младший и старший байт да бы начать чтение с младшего байта и загрузить 32 байта в память приставки. Формула тоже простая collumnNumber * 32 = sourceLow и старший бит, и тут я посмотрел в код и он не нужен на данный момент мне. Реализация данной формулы тоже проста:
LDA columnNumber ASL A ASL A ASL A ASL A ASL A STA sourceLow
И на последок загружаем все в память
LDX #$1E ; copy 30 bytes LDY #$00 DrawColumnLoop: LDA (sourceLow), y STA $2007 INY DEX BNE DrawColumnLoop LDA #%10010000 EOR nameTable STA $2000
В этой статье я привел основные моменты перерисовки фона для динамической его прокрутки, в исходном коде вы найдете еще несколько строк обвязки которые подготавливают данные. Увеличивают сначала х координату игрока и если игрок на середине экрана то увеличивается прокрутка, есть математика которая определяет загрузку attribute table и обновляет атрибуты для новых колонок, так же алгоритм вычисления колизий с картой. Все это будет описано в следующих статьях.
Ссылки:
-
Видео/аудио версия статьи https://youtu.be/RMgCD15M5io
-
Репозиторий с кодом https://github.com/lnroma/newGameNes
-
https://habr.com/ru/post/577620/ — вычисление коллизий
-
https://habr.com/ru/post/553848/ — граффика денди
-
https://habr.com/ru/post/552604/ — считывание контроллера
-
https://habr.com/ru/post/551488/ — вводная статья программирование на nes/dendy
ссылка на оригинал статьи https://habr.com/ru/post/715994/
Добавить комментарий