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