Читатель-читателю - Программирование аркадной игры со скроллингом экрана.

ZX Review #7-8-9-10
(c) М.Татаринов, г.Соликамск,
    Пермская обл.

  Всем, кто задумал  делать  ар-
кадную игру, посвящается...

   Если вы решили написать  игру
со скроллингом экрана и не знае-
те, как это  сделать, то  данная
статья поможет  вам.  Хочу  ска-
зать,  что  ограниченный   обьем
статьи не позволяет мне  привес-
ти  конкретные  примеры  (полный
пакет процедур для работы с кар-
той занял у меня около 2-х кило-
байт), но если вы  решились  пи-
сать игру, то вы достаточно ква-
лифицированный   программист   и
разберетесь, что  к  чему.  Все,
сказанное в статье, имеет  прак-
тическое  подтверждение, поэтому
смело пишите свои процедуры.

        1. Формат карты

   Минимальным элементом на кар-
те является блок размером  32x32
пикселя,  т.е.  4x4  знакоместа.
Такие размеры выбраны не случай-
но, вот их преимущества:
   1) 4 - это одна  из  степеней
числа 2, так что  все  арифмети-
ческие операции предельно  упро-
щаются за счет использования ко-
манд сдвига.
   2) Огромный плюс - это  малые
размеры памяти, занимаемой  кар-
той. Так, карта размером 1024x16
знакомест (8x8)  занимает  всего
лишь  1024  байта, карта  же  из
блоков 8x8 займет в 16 раз боль-
ше, т.е. 16 Кб.
   3) 1 блок 4x4 печатается быс-
трее, чем  16  отдельных  знако-
мест.

   Хотя размер одного блока  ра-
вен 16x8+16=144  байта  (если  с
атрибутами), 30-40 блоков  впол-
не хватает и не  занимает  много
памяти.

 2. Немного о графическом наборе

   В каком формате  хранить  ин-
формацию о графических шаблонах,
дело ваше. Я же предлагаю формат 
растрового имиджа, который печа-
тается быстрее.

         4 линии              ───────>
        ┌────────┐ │         ┌────────┐ │
        ├───────>┤ │         │        │ │
        ├───────>┤ │         ├────────┤ │
        ├───────>┤ │         │        │ │
16 линий├───────>┤ │+атрибуты├────────┤ │
        ├───────>┤ │         │        │ │
        ├───────>┤ │         ├────────┤ │
        ├───────>┤\│/        │        │\│/
        └────────┘ │         └────────┘ │

   Стрелками показано  направле-
ние печати, т.е. линия за  лини-
ей.
   Информацию  об  атрибутах   я
предлагаю хранить в виде отдель-
ного файла, который приписывает-
ся в конец BitMap'а. Сделано это
опять же для  упрощения  расчета
адреса блока. Т.к. размер  блока
без атрибутов равен 128  байтам,
то вычислить  смещение  в  файле
шаблонов чрезвычайно просто:

;в B - номер шаблона
        ADD  A,A
;X2 - в данном случае уместно, т.к. для
;файла, в котором больше 127 блоков, вам
;места не хватит
        LD   H,0
        LD   L,A
        ADD  HL,HL  ;x4
        ADD  HL,HL  ;x8
        ADD  HL,HL  ;x16
        ADD  HL,HL  ;x32
        ADD  HL,HL  ;x64
        ADD  HL,HL  ;x128
        LD   DE,TABL
        ADD  HL,DE

   3. Вывод карты с точностью до
знакоместа по произвольным коор-
динатам (имеются в виду  коорди-
наты на карте, а не на экране).

   Внимательный читатель  навер-
няка  заметил, что  дискретность
карты составляет блок  4x4, сле-
довательно, и  скроллинг  должен
быть не меньше, чем на 4  знако-
места. Такая дискретность подхо-
дит для  Map Editor'а, но  никак
не для  игры, поэтому  необходи-
мо разработать  прием, позволяю-
щий вывести в окно  произвольный
участок  карты  с  точностью  до
знакоместа.

  0┌────┬────┬─ Y  LENGTH ──┬────┬────┬─
  1│0   │1   │2   │3   │4   │5   │6   │
  -│----│----│----│----│----│----│----│-
  2│    │   ╔══════════════════════╗  │
  3│    │   ║│    │    │    │    │ ║  │
  ─┼────┼───║┼────┼────┼────┼────┼─║──┼─H
  0│7   │8  ║│9   │10  │11  │12  │13  │ E
  1│    │   ║│    │    │    │    │ ║  │ I
  -│----│---║│----│----│----│----│-║--│─G
  2│    │   ║│    │    │    │    │ ║  │ H
  3│    │   ║│    │    │    │    │ ║  │ T
X ─┼────┼───║┼────┼────┼────┼────┼─║──┼─
  0│14  │15 ║│16  │17  │18  │19  │20  │
  1│    │   ║│    │    │    │    │ ║  │
  -│----│---║│----│----│----│----│-║--│-
  2│    │   ║│    │    │    │    │ ║  │
  3│    │   ║│    │    │    │    │ ║  │
  ─┼────┼───║┼────┼────┼────┼────┼─║──┼─
  0│21  │22 ╚═23═══24═══25═══26═══27  │
  1│    │    │    │    │    │    │    │
  -│----│----│----│----│----│----│----│-
  2│    │    │    │    │    │    │    │
  3└────┴────┴────┴────┴────┴────┴────┴─
  0123 0123 0123 0123 0123 0123 0123

   Взгляните на  рисунок, и  вам
сразу станет  понятно, как  рас-
считать требуемую позицию окна с
координатами  (X,Y)  на   карте.
Если вы  не  поняли, то  привожу
следующие выводы:

   1) Очевидно, что  если  карта
имеет  размеры  Length * Height,
то для грубого  расчета  позиции
окна  (X,Y)  стартовое  смещение
распечатки будет равно X+Length*
Y (координаты заданы с точностью
4x4). Чтобы  увеличить  точность
позиционирования окна, мы вводим
смещение окна отдельно по X и Y.
Оно может быть от 0 до 3  и  по-
казывает  смещение  относительно
начала текущего блока. На рисун-
ке  эти  координаты = (1.2;1.1),
где целая часть - номер блока на
карте, дробная - смещение.
   2) Чтобы вывести карту на эк-
ран, нужно рассчитать  координа-
ты  печати  на  экране.  Дробные
части координат X и Y (в  данном
случае это 2 и 1), если их обра-
ботать командой  NEG, дадут  нам
стартовые координаты  печати  на
экране или в буфере, т.е. 2 ста-
нет  -2, 1 станет  -1 (#FE, #FF,
если угодно).

   4. Как работает  подпрограмма
печати блока и как ускорить  вы-
вод карты.

   Наверняка  вы  заметили,  что
реальное  количество  знакомест,
печатаемых  в  окне, меньше, чем
количество знакомест, входящих в
состав окна, поэтому число выво-
димых блоков должно  быть  на  1
больше, чем длина и высота окна,
деленные  на  4.  То  есть, если
размеры окна 32x16, то число пе-
чатаемых блоков  32/4+1=9; 16/4+
1=5. Из этого следует, что  под-
программа  печати  блока  должна
уметь отсекать невидимые  знако-
места (клиппирование). Хочу ска-
зать, что операции  сравнения  и
увеличенное число  блоков  резко
снижают  быстродействие  печати.
Для увеличения скорости я приме-
нил  прием  использования   двух
подпрограмм  печати.  Посмотрите
на рисунок. Мы видим, что не все
блоки требуют  отсечения, поэто-
му мы создаем еще одну  подпрог-
рамму печати, которая  не  отсе-
кает элементы  блока.  Эта  под-
программа  должна   иметь   цикл
только  в 4 знакоместа, а 8 лиш-
них  выводятся  без  цикла с по-
мощью LDI. Привожу вам "портрет"
окна 32x1 знакомест.  Clip - от-
секать элемент, No clip - не от-
секать:

 0 9 блоков clip, но если смеще-
   ние=0, то 9 блоков no clip.
 4 первый - clip, 7 no clip, 1
   clip.
 8 аналогично.
12 аналогично.
16 так же, как 0, только  при
   смещении = 0 не печатается.

         5. Скроллинг

   Ну, это элементарно. Рассмот-
рим скроллинг вперед на 1 знако-
место. Пусть переменные  HPOS  и
HDISP играют  роль  координат  и
смещения. Проверяем HDISP на  3.
Если = 3,  то  HDISP:=0,  HPOS:=
HPOS+1; иначе увеличиваем HDISP.
В другие направления - аналогич-
но.
   Напоследок скажу, что  печать
карты должна идти в буфер, кото-
рый я организую с  адреса  #E000
(подробнее  об  этом - см. книгу
"Прикладная графика"), и в  свя-
зи с этим формат  буфера  задает
сам программист. Наиболее  удоб-
ная раскладка буфера - линия  за
линией, что существенно упрощает
процедуру печати.
   И еще.  Если  вам  необходимо
больше  быстродействия,  советую
пропускать нулевые элементы  при
печати (пробелы), но  тогда  вам
придется  использовать  подпрог-
рамму очистки буфера. Этот метод
наиболее  эффективно    повышает
быстродействие, но в  результате
скорость игры не постоянна.

   P.S. Если вы собираетесь  ис-
пользовать  стек  для  повышения
быстродействия  и  намереваетесь
использовать звуковое  сопровож-
дение, работающее  по  IM  2, то
скорость  воспроизведения  звука
будет  "плавать".  Это   вызвано
тем, что при  работе  со  стеком
необходимо запрещать прерывания.
При написании предыдущей  игруш-
ки я столкнулся с  этой  пробле-
мой, т.к. печать блока  и  очис-
тка буфера использовали стек.

    Попутно  хочу  ответить   на
вопрос Васильева Антона  (N 4-5,
1996). Я тоже занимался  пробле-
мами  обработки    оцифрованного
звука на AY, и вот что я  делал.
Способ записи звука  практически
не отличается  от  предложенного
Сергеем Беловым  (читаем  шестой
бит, вращаем регистр), только  я
использовал псевдокомпрессию. На
AY оцифрованный  звук  выводится
через регистры амплитуды, а мак-
симальное значение амплитуды  не
превышает 15, т.е.  четырех  би-
тов. В связи с  этим, в  1  байт
можно поместить в 2 раза  больше
информации:

 1) 0000xxxx=E - 1 цикл чтения
 2) xxxxyyyy=E - 2 цикл чтения
 3) (HL)=E     - запоминание
    INC HL

   В регистрах 11 и 12  задается
скорость смены амплитуды при ис-
пользовании встроенного  генера-
тора огибающей.

   Прим.ред.: Спасибо Максиму за эту весь-
ма интересную статью. Только  вот  хочется
добавить пару слов по  поводу  использова-
ния стека при выводе  карты.  Да, действи-
тельно, прерывания при  этом  должны  быть
запрещены,  но  избавиться  от  "плавания"
звука при этом все же возможно. Для  этого
вывод  карты  надо  начинать  сразу  после
HALT, а сам вывод должен умещаться в коли-
чество тактов между прерываниями. Тогда вы
успеете разрешить прерывания как раз к мо-
менту, когда следующее  прерывание  должно
произойти.

           *   *   *