Info Guide #11
05 июля 2015

Code - Скриптование в демо: синхронизация эффектов под музыку и не только.

    Скриптование в демо
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
timeraction0
   CALL timeraction;обработка параметров
            ;(например, движение объектов)
   DJNZ timeraction0 ;делаем это столько
              ;раз, сколько прошло фреймов
   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 (если речь об эффектах, а
не об анимации).
   Идеальный керналь бы выглядел как реда─
ктор  ключевых точек, который можно запус─
тить на реале  вместе с демой, перематывая
туда и  сюда (вместе  с музыкой) для наст─
ройки этих точек,а потом сохранить резуль─
тат  в файл. Но тут те же самые проблемы с
ограничением числа параметров. Допустим,мы
хотим сделать пролёт в пространстве, в это
время, допустим,падают звёзды (и ещё много
что происходит).Пролёт описывается коорди─
натами и поворотами камеры по ключевым то─
чкам, нам достаточно реализовать в кернале
сплайны. А  вот  падающие  звёзды придётся
для начала добавить в код, потом ввести их
ключевые  точки  в  керналь, да так, чтобы
старая настроенная траектория от этого до─
бавления не сломалась.
   Я вижу такое решение: керналь будет по─
сылать  в эффект координаты камеры и собы─
тия  с номерами, а эффект будет по нумеро─
ванным  событиям запускать соответствующий
код (ронять звезду, моргать фонарём,запус─
тить по улице машину...).Но у такого реше─
ния  есть  проблема - как настраивать сами
события? Мы  получим совершенно разные ре─
зультаты со звёздами, падающими быстро или
медленно, под  одним  углом  или другим, с
ускорением или замедлением... Выходит, что
керналь  должен ещё настраивать нумерован─
ные переменные (и как бы эффект не вывали─
лся  от ошибочного  значения  переменной!)
или даже  генерировать нумерованные траек─
тории. Чтобы не утонуть в номерах, придёт─
ся  экспортировать  в  керналь  символьные
имена - причём  ещё и как-то автоматизиро─
вать этот процесс.
   Так что работы ещё предстоит много.

                  * * *

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



Другие статьи номера:

Об оболочке - журнал состоит из разделов, а разделы из статей.

От авторов - предисловие: Прошло 8 лет с момента выхода прошлого номера Info Guide. Что изменилось на Спектруме за это время?

Комьюнити - Spectrum в глубинке: в городе, население которого не превышает 15 тысяч человек, появление компьютера было сравнимо с изготовлением атомной бомбы в гараже.

Комьюнити - Forever 2015: отчет с демопати для всех 8-битных компьютеров.

Комьюнити - DiHalt 2015: отзывы от Lilka и Louisa.

Комьюнити - Как это было в Бразилии: история развития Спектрума в Бразилии от Paulo Silva.

Комьюнити - Беседа с Tiboh/Debris - программистом из Красноярска, долгие годы занимавшимся обработкой архивов спектрумовских программ.

Комьюнити - интервью с Raver/Phantasy взятое на irc.forestnet.org

Code - Этюды: Вызов функции по номеру, Поиск текста по номеру, Определение наличия музыкального сопроцессора, Установка пикселя на ATM Turbo 2, Библиотеки процедур в ALASM, Короткий генератор случайных чисел, Ускорение LD:PUSH.

Code - точка зрения: проекция пространства на экран из одной точки.

Code - чанковый эффект: Magnets stretching

Code - О мерцающем бордере: использование мерцание для повышения разрешения на бордере.

Code - Скриптование в демо: синхронизация эффектов под музыку и не только.

Графика - режиссура в демо: палитра изобразительных средств в Демомейкинге.

Графика - Мини-опрос художников: Dimidrol, Einar Saukas, Sand, Rion, riskej.

Графика - интервью с художником RayNoa/MAYhEM.

Музыка - Синхронизация музыки: nq рассказывает о создании треков под таймлайн.

Музыка - Беседа с MmcM/Sage group, известным AY-музыкантом, о его знаменитой технике.

Музыка - Беседа с Manwe/SandS - известным композитором, одним из старейших демосценеров России.

Музыка - Однобитная музыка: почему бипер ZX Spectrum продолжает вызывать восхищение?

Музыка - Горизонты турбосаунда: Cj Splinter делится опытом работы с TurboSound.

Музыка - Снова о плейерах Pro Tracker 3.x

Музыка - Музыкальный движок Muse 128b.

Системки - Как приручить IAR C Compiler.

Системки - Оберон для ZX Spectrum: Тонкости при разработке на Обероне в среде ZXDev (часть 1).

Системки - Оберон и ассемблер: Сопряжение с ассемблером (часть 2).

Системки - ZX-Basic Compiler: расширяемый кросс-компилятор.

Системки - Программы с поддержкой HDD, или "Linux" для Спектрума с винтом (или SD-картой).

Системки - iS-DOS/TASiS: о базовых принципах программирования под ОС iS-DOS/TASiS (часть 1).

Системки - iS-DOS/TASiS: как писать игры под iS-DOS/TASiS (часть 2).

Системки - iS-DOS/TASiS: Работа с палитрой и переключение графических режимов в TASiS (часть 3).

Металлолом - о строении экрана 6912 с аппаратной точки зрения.

Металлолом - Палитра для ZX Spectrum в различных графических режимах.

Металлолом - Эмуляция контроллера дисковода 1818ВГ93.

Дикий ум - Генерация и оптимизация кода в компилятора (часть 1)

Дикий ум - Генерация и оптимизация кода в компилятора (часть 2).

Дикий ум - ловля багов: самые типичные ошибки, при разработке на ассемблере Z80 (часть 1).

Дикий ум - ловля багов: самые типичные ошибки, при разработке на ассемблере Z80 (часть 2).

Дикий ум - алгоритм сжатия видео - 16 цветов на точку.

Игрушки - Разработка игр на Evo SDK (часть 1).

Игрушки - Разработка игр на Evo SDK (часть 2).

Игрушки - секрет успеха игры Jet Set Willy выпущенной в 1984 году.

Игрушки - Metal Man Reloaded: История создания от Oleg Origin.

Игрушки - Строение скриптового движка игры на примере L7 script engine.

Мыльница - Секретные кнопки в играх: Project ROBO, Ninjajar!, Uwol, Quest for Money, Zooming Secretary, Game About Squares.

Мыльница - письма: Kq, elfh, mig'95, wbr^NOT-Soft.

Мыльница - errata: Работа над ошибками.

Мыльница - об авторах журнала.


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

Похожие статьи:
PRICE - Прайс лист на продукцию фирмы Скорпион.
Лодырь - Полное прохождение игр MONSTRLAND и Homer Simpson в России.
... и здесь - Анфиса, Ёжик.

В этот день...   21 июля