(Окончание, начало в МК № 45, 49, 5 (216, 220, 228))
Текстовые поля
Текстовые поля бывают трех типов: Static Text, Dynamic Text и Input Text. Самый простой — первый тип, это обычная надпись. Ее свойства из ActionScript изменять нельзя. Переименуем слой Layer 1 сцены в lr_Controls, в него мы будем помещать наши элементы управления. Создаем в этом слое две надписи: fr. X и fr. Y. Напротив них — две надписи (точнее, текстовых поля) типа Input Text с параметрами Variable fr_x и fr_y соответственно. Тип текстового поля и имя переменной (Variable), связанной с ним, задаются на закладке Text Options (Ctrl+T) панели Character (Рис. 1). В эти поля мы будем вводить
горизонтальные и вертикальные частоты нашей фигуры Лиссажу.
Создадим новый символ Cmv_Cap и сделаем в нем надпись (Static Text) Number of points. Затем выделим нашу надпись и разобьем ее: Modify > Break Apart (Ctrl+B). Это делается для того, чтобы можно было управлять прозрачностью надписи (свойство _alpha). ActionScript позволяет изменять свойства только для символов. Свойства примитивов (фигур), нарисованных на сцене во время проектирования, с помощью ActionScript изменять нельзя.
Создадим еще один символ Cmv_EditNum и поместим в него текстовое поле типа Input Text, свойство Variable которого назовем num_pnt. Сюда мы будем вводить количество точек (шариков) для фигуры Лиссажу вида «кривая». Мы создали эту надпись в виде отдельного символа, чтобы была возможность спрятать ее (убрать за пределы рабочей области), когда фигура будет отображаться в виде «хвоста».
Помещаем объекты на сцену
Перетащим из библиотеки символы (кроме Cmv_TailBall и Cmv_CurveBall) на сцену, в слой lr_Controls, как показано на Рис. 2 (или как сочтете нужным :-)). Двум экземплярам кольца для радио-кнопок дадим имена mv_rTail и mv_rCurve. Экземпляр с надписью Number of Points получит имя mv_Cap, а экземпляр, содержащий текстовое поле с переменной val — имя mv_EditNum. Экземпляры кнопок имен не имеют. Надписи не имеют
собственных имен, но различаются именами связанных переменных (свойство Variable).
«Подгоните» все элементы по размеру и местоположению, а два экземпляра Cbt_Arrow переверните на 180 (уже знакомая нам закладка Transform панели Info), чтобы получилась кнопка «стрелка вниз».
Выровнять расположение объектов можно, отключив опцию View > Snap to Objects и вызвав панель выравнивая Align (Ctrl+K). Кнопка To Stage: на этой панели указывает, что выравнивать выбранные (черной стрелкой — напоминаем на всякий случай :-)) объекты следует относительно центра рабочей области. Эта же кнопка позволяет точно отцентровать символы при их рисовании.
Создадим еще два слоя —lr_TailBall и lr_CurveBall — и поместим в них (за пределами видимой области) экземпляры символов mv_TailBall и mv_CurveBall соответственно.
Вообще говоря, старайтесь придерживаться такого правила: символы различной природы помещайте в разные слои. Этим вы избежите возможных «глюков». Да и с точки зрения программирования это целесообразно — видна логика программы.
Скрипты
Наконец-то мы добрались до скриптов.
Создаем новый слой lr_Actions и вставляем в первых трех кадрах ключевые кадры. Как и в предыдущем примере (см. МК №45, 49 (216, 220)), будем использовать классический трехкадровый цикл. В первом кадре — инициализация переменных, во втором — собственно динамика, в третьем осуществляется переход на второй с помощью вызова функции gotoAndPlay(2).
Все скрипты мы будем писать самостоятельно, так что давайте сделаем Expert Mode режимом по умолчанию: Edit > Preferences…, поле Mode панели Actions Panel. В редакторе ActionScript переключение между обычным и «экспертным» режимом осуществляется нажатием Ctrl+N/Ctrl+E. Удобное свойство: написав скрипт в режиме эксперта, переключитесь на обычный, а затем опять на экспертный. Ваш текст станет отформатированным, если нет синтаксических ошибок. Кстати, правильность синтаксиса можно проверить, нажав кнопку в правом верхнем углу и выбрав Check Syntax (Ctrl+T) в выпавшем меню.
Нумерация строк и комментарии на русском в ActionScript не предусмотрены (отсутствие русификации — один из немногих недостатков Flash ). Тем не менее, некоторые листинги мы приводим с нумерацией и с русскими комментариями — для удобства.
В первом кадре слоя lr_Actions пишем:
1 pm_Tail = 0; 2 pm_Curve = 1; // объявляем костанты — это удобнее, чем непосредственные значения 3 Play_On = false; // начальное состояние: "Пауза" 4 PlayMode = pm_Tail; // начальный вид фигуры: "хвост" 5 mv_Dot._x = mv_rTail._x; 6 mv_Dot._y = mv_rTail._y; // "бегунок" радио-кнопки помещаем на кнопку "Tail" 7 mv_Cap._alpha = 25; // альфа-канал надписи = 1/4 8 mv_EditNum._x = 600; // "прячем" поле ввода за пределы видимости 12 NeedKill = false; // стирать кривую нет необходимости 13 size_x = 240; // размеры 14 size_y = 100; // фигуры 15 c_x = 275; // координаты центра 16 c_y = 135; // фигуры 17 fr_x = 3; // начальные x- и 18 fr_y = 1; // y-частоты 19 phase = 1.57; // начальная фаза = pi/2 20 n_tail = 0; // № очередной точки для "хвоста" 21 n_curve = 0; // то же для "кривой" 22 mv_EditNum.val = 0; 23 max_curve = 0; // <кол-во точек хвоста> == 0 24 t = 0; // обнуляем таймер 25 rate = 5; // скорость "развертки"
В третьем кадре пишем :
gotoAndPlay(2);
То есть осуществляется переход на второй (пока еще пустой) кадр.
Давайте напишем скрипты для кнопок. Выделяя кнопки и вообще любые объекты на сцене, вы заставляете Action Panel отображать Object Actions, а при выделении кадра в слое — Frame Actions.
Кнопка «Play»:
on (press) { // при событии "нажато" Play_On = true; // переводим ролик в состояние "Play" }
Кнопка «Pause»:
on (press, release) { Play_On = false; // остановить воспроизведение }
Кнопка «Restart»:
on (press) { unloadMovie (0); // выгрузить ролик // начиная с 0-го уровня loadMovie ("osc.swf", this); // загрузить ролик в активную область }
Давайте разберемся. Скрипт кнопки должен содержать реакцию на события. Обработчик события имеет вид
on (<событие1> [,<событие2>[, ...]]) { <оператор1>; ... <оператор1>; }
Так, для кнопки Play скрипт, заключенный в {}, выполнится при нажатии; для Pause — при нажатии или отпускании, а для Restart — только при отпускании.
Далее, для наших кнопок-«стрелок», стоящих справа от полей ввода частот, пишем следующие четыре обработчика (какой фрагмент для какой кнопки — вы, безусловно, разберетесь :-)):
on (press) { fr_x++; // увеличить x-частоту } on (press) { fr_x--; // уменьшить x-частоту } on (press) { fr_y++; // увеличить y-частоту } on (press) { fr_y--; // уменьшить y-частоту }
Для радио-кнопок Cbt_Curve и Cbt_Tail пишем вот что:
Кнопка Cbt_Curve:
on (press) { PlayMode = pm_Curve; // устанавливаем режим отображения "кривая" mv_Dot._x = mv_rCurve._x; mv_Dot._y = mv_rCurve._y; // устанавливаем "бегунок" радио-кнопки на "Curve" mv_Cap._alpha = 100; // альфа-канал = 100% (непрозрачный) mv_EditNum._x = 480; // делаем поле mv_EditNum видимым }
Кнопка Cbt_Tail:
on (press) { // аналогично строкам 4..8 для 0-го кадра слоя lr_Action PlayMode = pm_Tail; mv_Dot._x = mv_rTail._x; mv_Dot._y = mv_rTail._y; mv_Cap._alpha = 25; mv_EditNum._x = 600; }
Теперь можно опубликовать наш ролик, поиграться с кнопками (все работают!), закрыть Projector и вернуться к нашему барашку — второму кадру слоя lr_Actions, в котором, собственно, и происходит анимация. Давайте напишем скрипт, а затем его обстоятельно прокомментируем.
1 if (Play_On) { // состояние == "Play" ? 2 phase = parseFloat(phase); 3 t += rate; // увеличиваем таймер 4 x1 = size_x*Math.cos(6.28*fr_x*t) + c_x; 5 y1 = size_y*Math.cos(6.28*fr_y*t+phase) + c_y; 6 if (PlayMode == pm_Tail){ 7 n_tail++; 8 if (n_tail > mv_TailBall._totalframes) { 9 n_tail = 1; 10 }; 11 if (NeedKill) { // надо стереть остатки кривой? 12 for (i = 0; i <= max_curve; i++) { 13 unloadMovie ("Curve_"+i); 14 }; 15 NeedKill = false; // уже убили :-) 16 }; 17 inst_name = "Tail_" + n_tail; // имя экземпляра 18 duplicateMovieClip (mv_TailBall, inst_name, n_tail); // создали экземпляр — копию mv_TailBall 19 setProperty (inst_name, _x, x1); 20 setProperty (inst_name, _y, y1); // изменили свойства только что созданного экземпляра 21 } 22 else { // вид фигуры: кривая 23 if (isNaN(mv_EditNum.val)) {mv_EditNum.val = 0}; 24 if (mv_EditNum.val != max_curve) { // если изменилось число точек, стереть кривую перед перерисовкой 25 for (i = 0; i <= max_curve+1; i++) { 26 unloadMovie("Curve_"+i); 27 }; 28 }; 29 NeedKill = true; // при переходе к виду фигуры "хвост" надо стереть "кривую" 30 n_curve++; 31 if (n_curve > max_curve) {n_curve = 1}; 32 max_curve = parseFloat(mv_EditNum.val); 33 inst_name = "Curve_" + n_curve; 34 duplicateMovieClip (mv_CurveBall, inst_name, n_curve); 35 setProperty (inst_name, _x, x1); 36 setProperty (inst_name, _y, y1); // аналогично строкам 17 — 20 37 }; 38 }
В строке 1 проверяется, включен ли флаг Play_On; если нет, то происходит переход на 3-й кадр lr_Action, который, в свою очередь, переводит поток выполнения на 2-й. Так будет продолжаться до тех пор, пока не будет нажата кнопка Play.
В строке 2 с помощью функции parseFloat() выделяется число из переменной phase, связанной с текстовым полем. В строке 3 увеличивается значения таймера на rate условных единиц (не долларов :-)). Затем (строки 4–5) вычисляются координаты x1, y1 очередной точки (шарика), в соответствии с гармоническим (синусоидальным) законом.
Строки 7–21 выполняются, если вид отображения фигуры — «хвост»; строки 22–37, если «кривая». Вид отображения задается переменной PlayMode, которая изменяется при переключении радио-кнопок Cbt_Tail и Cbt_Curve (см. скрипты для них).
В строках 7–10 определяется номер очередного шарика для «хвоста». При этом в строке 8 используется свойство _totalframes, равное количеству кадров (в нашем случае 15) для данного мувика (см. далее о различии анимации ролика и мувика).
Строки 11–16 выполняются, если необходимо стереть «кривую» (NeedKill == true).
Функция
unloadMovie(<Имя_экземпляра>)
уничтожает экземпляр мувика с именем <Имя_экземпляра> и действует только на экземпляры, созданные динамически с помощью ActionScript. <Имя_экземпляра> — это строка. Копии экземпляров мувиков создаются функцией
duplicateMovieClip(<Имя_1>, <Имя_2>, n);
Здесь <Имя_1> — идентификатор уже существующего экземпляра мувика, <Имя_2> — желаемое имя нового экземпляра (по аналогии с unloadMovie(), это строка), n — номер уровня (не путайте со слоями), в который требуется поместить новый экземпляр.
На сцене находится множество уровней (теоретически их число не ограничено), в каждый из которых можно помещать объекты. При этом объекты, помещенные в «старший» уровень (с большим номером), перекрывают объекты в «младшем» уровне.
В одном уровне может находиться только один объект с данным именем.
Обратите внимание на строку 17. Flash ActionScript позволяет складывать строки с числами; так, "Tail_" + 12 == "Tail_12".
В строке 18 будут создаваться копии мувика mv_TailBall. У них будут имена Tail_1, Tail_2,..., и помещаться они будут в уровни 1, 2,…, в зависимости от значения переменной n_tail.
В строках 19, 20 устанавливаются значения свойств _x, _y для только что созданной копии мувика.
Функция
setProperty (<Имя>, <Свойство>, <Значение>);
устанавливает <Свойство> экземпляра с именем <Имя> в <Значение>.
Несколько слов об анимации ролика и анимации объектов (мувиков) этого ролика. Как говорят в Одессе, это две большие разницы. Если вы в 3-м кадре слоя lr_Actions напишете stop() вместо gotoAndPlay(2), вы тем самым остановите выполнение программы. Но анимированные объекты-мувики (их в нашем проекте аж один :-) —mv_TailBall) будут «жить» (трансформироваться согласно заложенной нами анимации, т.е. сжиматься) дальше! Проверьте это — поместите mv_Tail в пределах видимой области и напишите stop(); в 3-м кадре слоя lr_Actions — ролик остановится, а шарик mv_TailBall будет периодически появляться и сжиматься.
Это происходит потому, что анимация мувика mv_TailBall была создана во время проектирования, а скрипты работают во время выполнения проекта. Общее для анимации ролика и анимации его объектов одно — количество кадров в секунду (frames per second, fps). Его мы установили равным 60 в самом начале работы.
Поэтому когда фигура имеет вид «хвост», шарики (mv_TailBall) уменьшаются в размере без нашего участия, а на 15-м кадре «самоликвидируются» вызовом removeMovieClip(this);, что было заложено, так сказать, генетически :-).
Вернемся к нашему листингу. Строки 23–37 выполняются, когда PlayMode == pm_Curve (фигура имеет вид «кривая»). В целом, операции аналогичны уже описанным: создается копия mv_CurveBall с именем Curve_xx (xx == n_curve) и устанавливаются координаты этой копии.
В строке 23 проверяется, является ли значение mv_EditNum.val числом, и если нет, оно устанавливается в 0. Функция
IsNaN()
(is not a number) возвращает false, если строка содержит число, и true в противном случае.
В строках 24–28 удаляются все экземпляры-копии mv_CurveBall, если изменилось значение mv_EditNum.val. «Кривая» перерисовывается заново. В 29-й — флаг NeedKill устанавливается в true, чтобы в строках 11–16 «кривая» была стерта при переключении на «хвост».
Пытливый читатель, взглянув на строки 7–10 и 17–18 (или 30–34), вероятно, воскликнет: а как же это мы создаем экземпляр-копию mv_TailBall (или mv_CurveBall) с уже существующим именем?! При достижении переменной n_curve значения max_curve ей присваивается значение 1, а экземпляр с именем Curve_1 у нас уже имеется! Как их потом различать? Сначала надо прибить «тезку»! Delphi, например, на такой кунштюк реагирует незамедлительно и очень грубо — Exception, и вся Москва :-)!
Все правильно. В объектно-ориентированном подходе существование двух экземпляров с одинаковыми именами, как правило, недопустимо. Но Flash, равно как и web-браузеры, старается по возможности игнорировать ошибки, а не сообщать о них, прерывая выполнение (кстати, это существенно затрудняет их поиск). В данном случае при создании экземпляра с уже существующим именем старый будет уничтожен автоматически.
Ну вот, наш проект создан, прокомментирован, осталось нажать F12, подобрать фазу, частоты и количество шариков (штук эдак 200) и убедиться воочию, что бесконечно можно наблюдать не только за огнем, прибоем и работающей женщиной :-).
Теперь опубликуем наш проект в виде HTML, и вот — страничка со встроенным осциллографом готова.
Можно добавить элементы управления, задающие скорость «развертки» (переменная rate), изменяющие прозрачность шариков (свойство _alpha), их размер (_width, _heigth) и так далее. Попробуйте сделать это самостоятельно.
На очереди — drag’n’drop, звук, собственные функции, Smart Clips, обработка «мыши» и еще много чего.
Ваяйте! Слава Уанету!
P.S. Тем, кто был терпелив и последователен, дочитав до этого места, мы приготовили сюрприз: по адресу можно скачать симпатичную флэш-игру (1.4 Mб) от компании EcoPhobia, содержащую все элементы, описанные в статье. Играя, не забывайте об авторских правах :-).
