Здарова, сынки!
Сегодня я расскажу вам о более правиль-
ных методах программирования с оптимизаци-
ей по скорости.То есть,чтобы,к примеру,де-
мки писать (делать вам, сказал бы я,нечего
;).В качестве положительного примера сове-
тую дему Power Up (ту самую,которая пофик-
сенная). В качестве отрицательного можно
взять Refresh того же автора. Вообще-то
странно: почему Exploder после красочной,
быстрой и оптимизированной демы написал
тормозную, чёрно-белую и глючную?
Итак,возьмём два простейших эффекта:по-
ворот (Rotation, включая его разновидность
ZoomRotation) и освещение (BumpMapping)
Rotation
Нам требуется построить проекцию карты
(Map) на экран или его заменитель - массив
чанков (ChunkMap) под углом.Причём каждому
пикселу экрана должен соответствовать пик-
сел на карте,т.е. главный цикл заключается
в переборе пикселов экранной строки.
!
При проектировании эффекта следует в пер-
вую очередь думать о скорости внутреннего
цикла (inner loop) - цикла, тело которого
выполняется максимальное количество раз. В
данном случае inner loop перебирает все
чанки экрана.Для оценки скорости программы
можно использовать нижнюю границу времени
выполнения - в тактах на чанк (пиксел, ат-
рибут, байт - смотря чем мы оперируем)
!
Данные об оттенке пиксела копируются из
карты (координаты {x,y} заданы с точностью
1/256 пиксела) в ChunkMap,после чего коор-
динаты на карте модифицируются прибавлени-
ем некоего вектора - направления строки.
Вектор этот равен {m*cos a;m*sin a}, где a
- угол поворота (0 - нет поворота,т.е. го-
ризонталь на карте соответствует горизон-
тали на экране), а m - масштаб уменьшения.
(При переходе на следующую строку коорди-
наты меняются на вектор, перпендукулярный
этому: {-m*sin a;m*cos a}.Если он будет не
перпендикулярен, то проекция произойдёт с
искажением. А что? Вращение с искажением -
тоже эффект!)
Первым делом в голову приходит примерно
такой участок программы:
EXX
ADD IX,BC ;Y+=dY
ADD HL,DE ;X+=dX
LD A,H
EXX
LD L,A
LD A,HX
LD H,A
LDI ;(HL)->(DE++)
Итого:70 t/c (тактов на чанк)
Скорость программы во многом определяе-
тся количеством команд LD. Действительно,к
чему переливать из пустого в порожнее?
ADD A,B ;A=младшая часть y
JR NC,$+3 ;B=dY
INC H
EX AF,AF'
ADD A,C ;A'=младшая часть x
JR NC,$+3 ;C=dX
INC L
LD E,(HL)
────────────────────
ADD A,C
JR NC,$+3
INC L
EX AF,AF'
ADD A,B
JR NC,$+3
INC H
LD D,(HL)
PUSH DE
Итого в среднем 95/2=47.5 t/c
Вывод идёт уже через стек,то есть задом
наперёд. В данном случае даже не страшно
внезапное прерывание.
Тут,правда,можно делать только увеличе-
ние, без уменьшения (масштаб должен быть
меньше единицы).А для полного комфорта ну-
жно четыре таких процедурки для всех воз-
можных (inc/dec) знаков смещения по x и y.
Понятно,что между пикселями HL изменяе-
тся на какую-то величину, каждый раз раз-
ную. Но от строки к строке последователь-
ность сумматоров меняется незначительно,
что позволяет сгенерировать табличку в на-
чале построения кадра,а потом использовать
её в каждой строке:
POP BC ;SP=табличка
ADD HL,BC
LDI
(37 t/c)
Или так:
LD BC,...
ADD HL,BC
LD E,(HL)
───────────────────-
LD BC,...
ADD HL,BC
LD D,(HL)
PUSH DE
(67/2=33.5 t/c)
Или даже так (опять ограничение на мас-
штаб):
[inc h]
[inc l]
LD E,(HL)
────────────────────
[inc h]
[inc l]
LD D,(HL)
PUSH DE
(в среднем 33/2=16.5 t/c!!!)
Правда, подпрограмма,генерирующая такую
процедурку,сама кушает как минимум 5000 t,
но результат того стоит!
Если это всё же чанки,то их надо как-то
выводить.
В идеальном случае для каждого эффекта
нужно выбирать свою процедуру вывода чан-
ков - самую быструю для данного случая.Со-
ответственно выбирается и формат Chunk-
Map'a: по одному чанку на байт или по два,
и какие биты этих байтов лучше юзать...
Вот хороший и быстрый метод вывода чан-
ков by Monster/Sage,в моей,так сказать,ин-
терпретации (кстати,этот метод использован
в первой части интро).
Где-то в памяти строится массив вот та-
ких процедурок и JP'ов на них, причём JP'ы
лежат по адресам типа %11aaaa00 11bbbb00,
где aaaa,bbbb - цвета чанков:
;SP=ChunkMap
LD H,A
LD (HL),x ;это байт,или рег. B,C,D,E
INC H ;причём BC=#00FF,DE=#55AA
LD (HL),x
INC H
LD (HL),x
INC H
LD (HL),x
INC L
RET ;переход на следующую проце-
;дурку из этой серии...
(В конце чанкмэпа лежит адрес выхода в
вызывающую программу)
В нашем случае вывод чанков можно поме-
стить прямо в главный цикл:
[inc h]
[inc l]
LD E,(HL) ;%0110aaa0
────────────────────
[inc h]
[inc l]
LD D,(HL) ;%0110bbb0
LD A,(DE)
LD (BC),A
INC B
INC E
LD A,(DE)
LD (BC),A
DEC B ;<- killable
INC C
(28.5 t/c)
Здесь выводится только 2 строчки чан-
ков,так что есть выбор: либо любоваться на
полосатую картинку,либо выводить в следую-
щем кадре по недостающим строкам (со сдви-
гом в 2 пиксела по вертикали)- в этом слу-
чае за картинкой будет ползти прикольный
"хвост".
hint: нажмите Enter в конце интро к жу-
рналу :)
Можно избавиться от DEC B, помеченного
стрелочкой, если INC B (четырьмя строчками
выше) постоянно чередовать с DEC B. В этом
случае чанки будут кувыркаться от знакоме-
ста к знакоместу, но этого не будет замет-
но,если в рисунке чанка убрать одну верти-
кальную линию (цветов останется всего 7):
. . . . . . . .
1 6 2 . 4 3 5 .
4 3 5 . 1 6 2 .
. . . . . . . .
Выигрыш - 2 t/c (26.5 t/c)
Думаете,это уже всё? Как бы не так! По-
чему бы нам брать с карты пикселы не по 1,
а сразу по 2? Или по 4? (Illusion вспомни-
те,там,где Sonic крутится!)
Советую взглянуть на процедурку в при-
ложении (SCALE.H), которая печатает аж по
32 пиксела (4x8) после каждой коррекции
координат! Смотрится вполне неплохо, если
учесть скорость масштабирования!
А теперь угадайте,что это за процедура:
[inc h]
[inc l]
LD E,(HL) ;%0aa00bb0
────────────────────
[inc h]
[inc l]
LD D,(HL) ;%0cc00dd0
PUSH DE ;SP=screen!
(33 t/4c)
А эти две?
[inc/dec h] ;по синусоиде
LDI ;DE=screen!
(18.5 t/b)
[inc l]
LD E,(HL) ;%0aa00bb0 или просто байт
────────────────────
[inc l]
LD D,(HL) ;%0cc00dd0 или байт
PUSH DE ;SP=screen!
(<33 t/4c=17.5 t/b)
Советую умножить 33(t)*16(x)*96(y)=
=50688 t!
И это весь экран!
Причём при желании чанковый,в 3 цвета!
Вспомните Power Up, а особенно Rotator+
+Mirror, 50 fps Flag, 50 fps Zoom.
Выходит,что их можно ещё ускорить :)))
BumpMapping
Лучше не стройте чертёж с расчётом ин-
тенсивности отражённого света на рельефе.
Результат будет содержать синус и квадрат-
ный корень, что для наших экспериментов по
оптимизации неприемлемо.
Практический способ освещения рельефа
состоит в проецировании изображения источ-
ника света (отсюда следует, что он может
иметь любую форму) на картинку,причём фор-
ма рельефа определяет смещение проекции в
данной точке.
(Аналогично реализуются и другие эффек-
ты,связанные с проекцией:Inside Torus,Full
Screen Lens,некоторые разновидности тунне-
лей и т.п.)
Если в данной точке поверхность понижа-
ется по направлению вправо,то смещение бу-
дет с положительным X, и, чем круче склон,
тем больше смещение. То есть,если источник
правее этой горки,то проекция в данной то-
чке сместится вправо,где "ярче".А если ис-
точник левее, то проекция сместится опять-
таки вправо,где "темнее".Вполне реально ;)
;SP=relief
POP HL ;HL=смещение в данной точке
ADD HL,BC ;BC=адрес изображ-я фонаря
LDI ;(HL)->(DE++),BC--
(37 t/c)
Казалось бы, что тут сокращать? Исполь-
зованы все 3;) мультимедийные команды Z80,
они очень 16-битные и очень быстрые...
Однако же,стек можно использовать и лу-
чшим образом.
Пусть из статичной картинки рельефа со-
здаётся такая процедурка:
LD HL,...
ADD HL,BC
LD E,(HL)
────────────────────
LD HL,...
ADD HL,BC
LD D,(HL)
PUSH DE
(67/2=33.5 t/c)
Теперь поглядите: что же за константы у
нас в HL? Сильно ли они меняются от точки
к точке?
А не входят ли они все в диапазон -128Ў
+127?
Если ширина экрана около 56 чанков, а
максимальное Y-смещение по модулю не пре-
вышает 2, то очень даже входят!
Значит,имеет право на существование та-
кая процедурка:
ADD A,...
LD L,A
LD E,(HL)
────────────────────
ADD A,...
LD L,A
LD D,(HL)
PUSH DE
(47/2=23.5 t/c)
Каждая линия фонаря вместе с четырьмя
соседними (выше и ниже) занимает сектор
памяти.Этот сектор мы и будем использовать
при построении экранной строки.
Разумеется,перед построением каждой но-
вой строки нужно занести в HL адрес соот-
ветствующей линии (плюс 4 соседних) изоб-
ражения источника света.
Ну,и вариации:
LD A,L
ADD A,...
LD L,A
LD E,(HL) ;%0110aaa0
────────────────────
ADD A,...
LD L,A
LD D,(HL) ;%0110bbb0
LD A,(DE)
LD (BC),A
INC B
INC E
LD A,(DE)
LD (BC),A
DEC B ;<- killable
INC C
(42 t/c)
Или так (если юзать мультиколор):
LD A,L
ADD A,...
LD L,A
LD E,(HL) ;%0110aaa0
────────────────────
ADD A,...
LD L,A
LD D,(HL) ;%0110bbb0
LD A,(DE)
LD (BC),A
INC C
(29 t/c)
Отсюда напрямую следует, что Bump можно
сделать ничуть не менее фреймовым, чем его
коллегу Zoom Rotator...
А если во фрейм не влезет, можно чуть-
чуть убавить высоту активной части экрана.
Конечно,это всё простые эффекты,которы-
ми теперь никого не удивишь... Но я хотел
всего лишь научить вас мыслить нестандарт-
но.
Все использованные фрагменты программ
сочинены мной, в чужой код глядеть не имею
привычки. Любые совпадения случайны.
© Alone Coder
Сайт управляется системой
uCoz