05 июля 2015

    Скриптование в демо
Alone Coder 

  Интересность демы сильно зависит от си─
нхронизации с музыкой,ритмичности и напол─
ненности  движением. Хороший монтаж обычно
не видно. Зато хорошо видно, когда он пло─
хой и бьёт по глазам.Особенно это касается
синхронизации.
  Как и в кино, монтаж может отнимать бо─
льше  времени, чем сбор материала (в нашем
случае написание самого кода).
  Какие решения могут быть предложены?

                  * * *

   На Спектруме мы очень далеки от написа─
ния  дем  одной  мышкой (а если дойдём, то
ценность таких дем будет невелика). Скрип─
тованием занимается каждый сам.
   Наши постоянные читатели уже могли поз─
накомиться с керналем от JtN (ACNews #54),
который  привязывает  каждую  часть демы к
определённой  секунде  и  передаёт  ей ка─
кие-либо данные. Это простейший вид монта─
жа. Там он был устроен через стек, где по─
мещались адреса процедур и все нужные дан─
ные. Каждая часть по командеret переходит
в следующую по этому стеку.
   Разумеется,мы не можем расписать каждый
чих  каждого  эффекта  через  такой  общий
скрипт  (даже если расположить его в стра─
нице), да  это  и  не требуется. Для этого
существует  раздельная  настройка  каждого
эффекта.
   Самый  классический вид настройки отде─
льного эффекта - ритмичное движение. Можно
подгонять  ритм  движения к ритму музыки и
получать  удовлетворительный результат. Но
на такое трудно смотреть долго. Вряд ли мы
можем  написать достаточно эффектов, чтобы
менять сцены каждые 5 секунд, как в упомя─
нутом  кино  (подразумевается, что мы всё-
таки пишем дему, а не конвертим анимацию),
поэтому  надо делать движение более разно─
образным.
   Эффекты  могут прыгать под ударники или
менять направление движения,объекты на эк─
ране могут в нужное время прилетать и уле─
тать, могут меняться цветовые решения, на─
стройки эффектов, появляться новые объекты
и т.д. Всё  это - не нарушая базового рит─
ма. Например, для  туннеля  в  Dizzzruptor
музыка была специально написана в 4-м тем─
пе, чтобы попасть в ритм квадратиков.
   Сами  траектории  могут  быть настроены
по-разному,  начиная  от  самых  простых -
прямых, парабол  и синусов. Так, в послед─
ней  части Nedodemo использовалось псевдо─
случайное прямолинейное движение,для кото─
рого  было подобрано среднее время измене─
ния  и  начальное  значение для генератора
псевдослучайных  чисел. В панорамной части
The Board II  использовались отрезки сину─ 
соид  и  случайные  числа  для  молнии.  В
Wolfenstein-части  Critical Error была за─
пись  нажатия  клавиш. В  The Board - тоже
запись, но  отредактированная  вручную.  В
панорамной  части  демы  New Wave 48K были
проезды между ключевыми точками. А в её же
финале сам текст скроллинга был специально
подготовлен так, чтобы соответствовать му─
зыке в семи разных точках.
   Вариантов много. Чем больше ручного ре─
дактирования, тем больше работы надо сове─
ршить  и тем лучше может быть результат. В
кубике  для  Mission Highly Improbable всё
прописывалось вручную месяц подряд, в ито─
ге  мы  можем, не  особо уставая, смотреть
этот один эффект целых 70 секунд :)

                  * * *

   Процедуру обработки скрипта логично вы─
зывать в начале построения кадра. При этом
мы используем значение глобального таймера
(timer), который тикает по прерыванию. На─
пример, так:

   LD HL,(timer)
   LD (oldtimer),HL
effectloop
   CALL scripting;обработка скрипта
   CALL effect;вызов эффекта
waithalt
   LD HL,(timer)
oldtimer=$+1
   LD DE,0
   LD (oldtimer),HL
   OR A
   SBC HL,DE
   JP Z,waithalt ;на случай, если эффект
        ;отрисовался меньше чем за 1 фрейм
   LD B,L
timeractionO
   CALL timeraction;обработка параметров
            ;(например, движение объектов)
   DJNZ timeractionO ;делаем это столько
              ;раз, сколько прошло фреймов
   LD HL,(timer)
effectendtime=$+1
   LD DE,0      ;время окончания эффекта
   OR A
   JP C,effectloop;крутим эффект до
                    ;этого времени

   Более точная, но более медленная привя─
зка  для любой скорости компьютера получа─
ется  при  вызове scripting  перед каждым
timeraction, при  условии, что вscripting 
и timeraction  мы будем манипулировать не
значением (timer), а собственноручно инк─
рементируемым счётчиком - иначе на медлен─
ных  компьютерах это значение будет повто─
ряться, а потом резко меняться.
   Вызов scripting и timeraction вне пре─
рывания  позволяет  иметь общий обработчик
прерывания  на  протяжении  всей демы и не
бояться превышения времени обработчика.

                  * * *

   Прописать скрипты (время,значение) мож─
но для каждого параметра эффекта,но так мы
рискуем ограничить число параметров и сде─
лать эффект однообразным.
   Более  гибкий  вариант  был  в  интре к
ZX-Guide #4.5: скрипт  содержит время, ад─ 
рес  переменной и её новое значение. Можно
прописать  несколько  изменений  с одной и
той  же меткой времени (там использовалось
время  относительно  предыдущей точки, что
показало себя неудобным по сравнению с аб─
солютным  временем).  В  частности,  такой
скрипт   может  подменять  адреса  в CALL
effect  и CALL timeraction, то есть можно 
полностью менять весь эффект.
   Ещё универсальнее будет вызывать произ─
вольные процедуры - сразу,а не ожидаяCALL
effect. Такой скрипт будет состоять из за─
писей  (время, адрес), а  процедура  может
делать что угодно - от смены параметра или
адреса  какого-то  обработчика  до очистки
экрана. Типичный обработчик такого скрипта
выглядит так (из New View 48K ):

scripting
curscripttime=$+1
   LD DE,0
   LD HL,(timer);тикает по прерыванию
   OR A
   SBC HL,DE
   RET C
curscriptaddr=$+1
   LD HL,script
   LD E,(HL)
   INC HL
   LD D,(HL)
   INC HL
   LD C,(HL)
   INC HL
   LD B,(HL)
   INC HL
   LD (curscripttime),BC
   LD (curscriptaddr),HL
   EX DE,HL
   JP (HL)

   Сам скрипт выглядит примерно так:

script
   DW initcube
   DW 192,startcube   ;после наступления
             ;192 фрейма вызовем startcube
   DW 192+96,spincube ;после наступления
           ;192+96 фрейма вызовем spincube
И т.д.

   Можно  реализовать  и  считывание пара─
метра  для процедуры (соответственно, надо
будет   добавить   этот  параметр  в  каж─
дую   строчку   скрипта) - добавьте  после
LD (curscripttime),BC:

   LD C,(HL)
   INC HL
   LD B,(HL)
   INC HL

   Получается  неожиданный  порядок (адрес
процедуры, время следующего события, пара─
метр процедуры), но никто не мешает реали─
зовать  строчку скрипта через макрос - так
будет  и  проще  изменять её формат. (Один
пример скриптования через макросы приведён
в разделе "Игры" .) Пример такого макроса:

oldtimeaddr=0 ;первое событие не содержит
              ;времени
   MACRO event time,proc,data
   DW proc;адрес процедуры
_=$
   IF oldtimeaddr
   ORG oldtimeaddr;поле времени в
                    ;предыдущей записи
   DW time
   ORG _;поле времени текущей записи
   ENDIF
oldtimeaddr=_
   DW #ffff ;на случай последней записи
              ;в скрипте
   DW data;параметр процедуры
   ENDM

   Если  мы  вызываем  процедуру обработки
скрипта в начале кадра и кадр строится до─
лго, то  в простейшем варианте из двух уже
готовых  событий  (если они наступят очень
близко  друг  от  друга) второе может быть
отложено до следующего кадра. Это не смер─
тельно. Но можно исправить это методом вы─
зова scripting  в  цикле - добавьте после
RET C:

   LD HL,scripting
   PUSH HL

   В этом случае можно прописать несколько
вызовов с одной и той же меткой времени.

    У  синхронизации методом задания адре─
сов процедур (по сравнению с методом зада─
ния адресов переменных) есть свой недоста─
ток - при  макетировании эффектов на языке
высокого  уровня  этот скрипт использовать
нельзя. Скриптовать можно будет только по─
сле переноса на целевую платформу.
   Другой  недостаток, уже  по сравнению с
таблицей значений - если эффект действите─
льно  длинный, то просматривать его каждый
раз  с самого начала неудобно. Но этот не─
достаток  преодолим. Я делаю так: события,
привязанные  к  одной  ключевой  точке (во
время  неё или в последующих движениях), я
прописываю с выражением  в  поле  времени.
К  примеру, (48*3)+32+diamondbasetime+192
(пример из того же New View 48K ). При не─
обходимости я могу выключить часть скрипта
условной компиляцией и сдвинуть эту ключе─
вую точку на начало временной оси.
   Если  скриптуется несколько независимых
элементов, то  во  множестве вызовов можно
запутаться. В  этом случае можно использо─
вать несколько скриптов и несколько проце─
дур scripting - каждую  из них вызывать в
начале   кадра.  Можно  сделать  отдельный
скрипт, например, для  перебора  изображе─
ний, он  будет  вызываться  по процедуре в
основном  скрипте и не будет содержать ме─
ток времени.

                  * * *

   Существует  проблема насчёт запаса вре─
мени на переключение между эффектами. Если
дема идёт достаточно плотно,то паузы могут
быть заметны, если сами эти паузы не подс─
троить  под  музыку. Если  это невозможно,
то  лучшее решение - подгонять первый кадр
нового эффекта под акцент в музыке, а пре─
дыдущий эффект пусть заканчивается на нес─
колько фреймов раньше положенного.
   Чтобы подобрать и зафиксировать это ко─
личество фреймов,мы прописываем в основном
скрипте  (который в кернале) и время нача─
ла, и  время  конца  эффекта. В простейшем
случае  для этого можно просто читать лиш─
нее число из стека, который выполняет роль
скрипта керналя.
   Так  можно  не только найти оптимальные
паузы, универсальные для всех типов машин,
но  и сделать несколько вариантов запуска─
льщика,например,под49 Hz и 50 Hz.Так было
сделано  в  The Board II, потому  что ско─
рость General Sound не зависит от скорости
самого  компьютера. При  очень агрессивном
синке могут понадобиться разные запускаль─
щики  для реала и эмулятора (где звук идёт
с задержкой) - может  быть, даже несколько
вариантов. Зато  сами файлы демы останутся
одинаковыми.
   При таком типе синхронизации важно учи─
тывать, что если эффект опоздал запустить─
ся, он не должен весь смещаться на столько
фреймов, на  сколько  он опоздал. В идеале
эффект должен работать так же, как на быс─
трых  машинах, но  с пропуском первых кад─
ров. К  сожалению, не  всегда это реально.
Простейшее  решение - привязывать всю дему
(в  том  числе  локальные  скрипты) к гло─
бальному  таймеру. Конечно, не обязательно
прописывать  глобальное  время в локальных
скриптах  (DW basetime+время,процедура ),
его  может  учесть  обработчик  скрипта, а
время  начала  эффекта (именно то, которое
должно быть; реальное - в таймере) ему пе─
редаёт керналь.
   Другой выход состоит в том,чтобы распа─
ковывать  (или инициализировать) следующую
часть  во  время  гашения  предыдущей. Для
этого  надо  делать гашение уже без самого
эффекта - чтобы  отнимать  немного времени
(можно использовать сворачивающийся прямо─
угольник  экрана). Ещё  один  классический
вариант - на  время распаковки части пока─
зывать  какую-то  картинку  или, поскольку
статика сильно вредит деме, применять пол─
зущие  полоски, тот  же  разворачивающийся
прямоугольник  или  что-то  в этом роде. К
сожалению, это  делает  дему более затяну─
той. Можно  было бы на переходах использо─
вать  и многозадачность, если части не пе─
рекрывают память друг друга. В этом случае
мы  потеряемFPS (если речь об эффектах, а
не об анимации).
   Идеальный керналь бы выглядел как реда─
ктор  ключевых точек, который можно запус─
тить на реале  вместе с демой, перематывая
туда и  сюда (вместе  с музыкой) для наст─
ройки этих точек,а потом сохранить резуль─
тат  в файл. Но тут те же самые проблемы с
ограничением числа параметров. Допустим,мы
хотим сделать пролёт в пространстве, в это
время, допустим,падают звёзды (и ещё много
что происходит).Пролёт описывается коорди─
натами и поворотами камеры по ключевым то─
чкам, нам достаточно реализовать в кернале
сплайны. А  вот  падающие  звёзды придётся
для начала добавить в код, потом ввести их
ключевые  точки  в  керналь, да так, чтобы
старая настроенная траектория от этого до─
бавления не сломалась.
   Я вижу такое решение: керналь будет по─
сылать  в эффект координаты камеры и собы─
тия  с номерами, а эффект будет по нумеро─
ванным  событиям запускать соответствующий
код (ронять звезду, моргать фонарём,запус─
тить по улице машину...).Но у такого реше─
ния  есть  проблема - как настраивать сами
события? Мы  получим совершенно разные ре─
зультаты со звёздами, падающими быстро или
медленно, под  одним  углом  или другим, с
ускорением или замедлением... Выходит, что
керналь  должен ещё настраивать нумерован─
ные переменные (и как бы эффект не вывали─
лся  от ошибочного  значения  переменной!)
или даже  генерировать нумерованные траек─
тории. Чтобы не утонуть в номерах, придёт─
ся  экспортировать  в  керналь  символьные
имена - причём  ещё и как-то автоматизиро─
вать этот процесс.
   Так что работы ещё предстоит много.

                  * * *

   Можно  испортить  даже  хорошие эффекты
плохим  монтажом, что  случается и в демах
раскрученных  авторов. А  хороший монтаж с
синком  может  занять  месяцы и может быть
реально  скучной  работой, даже  если  это
время распределить между написанием кусков
кода.
   Но если вы попали на демосцену, то надо
выбирать: либо  тратить своё время на соз─
дание  ВЕЩЕЙ, либо тратить ещё больше вре─
мени  на  самораскрутку в Интернете, иначе
результат  будет  невелик... Даже  если не
ставить  вопрос морали, хоть в краткосроч─
ной  перспективе побеждают вторые, в исто─
рической-то - первые.  Ведь  ценен  именно
продукт, а  не его реклама! Но есть и тре─
тьи - кто  просто  развлекается. К сожале─
нию, в  развлечении  все  средства хороши,
особенно если веселящийся не отягощён теми
самыми моральными принципами. Если человек
не  настроен сознательно на делание добра,
то от делания зла его останавливает только
угроза  наказания. Всегда  стоит  вопрос -
куда мы идём?



Other articles:


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Similar articles:
List BBS - The list of stations BBS.
Likbez - full description + full disassembler ROM (part 3).
Mosaic - Again on the Amiga.

В этот день...   21 November