Март 2011
Руководство по actionscript. часть 4, стр. 130
I Дополнительную информацию о системе обновления экрана среды выполнения Flash [%; А щ можно найти в дневнике Тиника Уро (Tinic Uro), инженера, работающего над созданием —SyJ приложения Flash Player. Этот дневник расположен по адресу: http://www. kaourantin. net/2006/05/frame-rates-in-flash-player. html.
ГЛАВА 24
Программная анимация
В этой главе описываются базовые методики создания анимации с помощью языка ActionScript. Основное внимание уделяется интеграции кода для создания анимации с автоматическими обновлениями экрана среды выполнения Flash. Однако в этой главе не рассматриваются углубленные вопросы, связанные с анимацией, например программирование движения, подчиняющегося законам физики, перемещение по траектории, обнаружение столкновения, эффекты растровой анимации или преобразование цветов.
Никаких циклов
Чтобы создать анимацию на ActionScript, мы постоянно изменяем графическое содержимое с течением времени, создавая иллюзию движения. Например, чтобы анимировать объект TextField, перемещая его по экрану в горизонтальном направлении, мы будем постоянно увеличивать или уменьшать значение переменной экземпляра х этого объекта. В некоторых языках программирования естественным механизмом для постоянного изменения значения переменной экземпляра является инструкция цикла. Таким образом, новички в программировании на языке ActionScript могут подумать, что анимация создается с помощью цикла while, как та, что показана в следующем коде:
public class TextAnimation extends Sprite { public function TextAnimation ( ) { // Создаем объект TextField var t:TextField = new TextField( ); t. text = «Hello»:
t. autoSize = TextFieldAutoSize. LEFT: addChild(t):
// Циклически обновляем горизонтальную позицию объекта TextField // и останавливаемся в тот момент, когда объект достигнет // координаты 300 по оси х while (t. x <= 300) { t.x += 10;
}
}
}
Предыдущий цикл while многократно увеличивает значение переменной экземпляра х объекта TextField, но, как в варианте создания анимации, в нем допущена роковая ошибка: при выполнении тела цикла обновление экрана не происходит.
Руководство по actionscript. часть 4, стр. 131
На каждой итерации цикла горизонтальное положение объекта TextField изменяется, однако визуальный эффект данного изменения не отображается на экране. Визуализация изображения происходит только после завершения последней итерации цикла и выхода из функции-конструктора класса TextAnimation. Следовательно, в тот момент, когда произойдет визуализация изображения, объект TextField уже будет находиться в точке с координатой 300 по оси X.
В языке ActionScript инструкции цикла не могут быть использованы для создания анимации. Помните, что обновление экрана никогда не происходит внутри блока кода. Дополнительную информацию можно найти в подразд. «Никаких обновлений экрана внутри блоков кода» разд. «Запланированные обновления экрана» гл. 23.
В языке ActionScript анимация создается не с помощью циклов, а с помощью многократного вызова функций, которые вносят визуальные изменения и затем завершаются, позволяя выполнить обновление экрана. Существует два механизма для многократного вызова подобных функций: события Event. ENTER_FRAME и TimerEvent. TIMER.
Создание анимации с помощью события
ENTER FRAME
Среда Flash осуществляет диспетчеризацию события Event. ENTER_FRAME всякий раз, когда выполняется проверка запланированного обновления экрана (как было описано в гл. 23). Любая функция, зарегистрированная для получения уведомлений о возникновении события Event. ENTER_FRAME, выполняется многократно с частотой, определяемой текущей скоростью кадров среды выполнения Flash. Визуальные изменения, вносимые любой функцией-приемником события Event. ENTER_FRAME, отображаются сразу после завершения этой функции.
Функция может быть зарегистрирована для получения уведомлений о возникновении события Event. ENTER_FRAME из любого экземпляра класса Di splayOb j ect, независимо от того, находится в настоящий момент этот экземпляр в списке отображения или нет. В качестве примера используем событие Event. ENTER_FRAME, чтобы реализовать анимацию, рассмотренную в предыдущем разделе, — объект TextField перемещается по экрану в горизонтальном направлении до точки с координатой 300 по оси X. Начнем с создания класса TextAnimation, который создает объект TextField и затем добавляет его в список отображения.
public class TextAnimation extends Sprite { private var t:TextField:
public function TextAnimation ( ) { // Создаем объект TextField t = new TextField( ); t. text = «Hello»:
t. autoSi ze = TextFi eldAutoSi ze. LEFT:
addChild(t);
}
}
Теперь создадим функцию-приемник moveTextRight ( ) для события Event. ENTER_FRAME, которая перемещает объект TextField t вправо на 10 пикселов. Многократный вызов функции moveTextRight ( ) создаст эффект анимации. Обратите внимание, что, поскольку функция moveTextRight ( ) является функцией-приемником события Event. ENTER_FRAME, она определяет единственный обязательный параметр, типом данных которого является Event.
Руководство по actionscript. часть 4, стр. 132
Public function moveTextRight (e:Event):void { t. x += 10;
}
Наконец, внутри функции-конструктора класса TextAnimation мы регистрируем функцию moveTextRight ( ) для событий Event. ENTER_FRAME. Как только она будет зарегистрирована в качестве функции-приемника событий Event. ENTER_FRAME, она будет многократно вызываться с течением времени, в соответствии со скоростью кадров среды выполнения Flash. Новый код выделен полужирным шрифтом:
public function TextAnimation ( ) { // Создаем объект TextField t = new TextField( ); t. text = «Hello»;
t. autoSize = TextFieldAutoSize. LEFT; addChild(t);
// Регистрируем функцию moveTextRight( )
// для получения уведомлений
// о возникновении события Event. ENTERFRAME
addEventListenerCEvent. ENTER_FRAME. moveTextRight);
}
В листинге 24.1 продемонстрирован класс TextAnimation, дополненный методом moveTextRight ( ).
Руководство по actionscript. часть 4, стр. 133
Листинг 24.1. Анимация объекта TextField по горизонтали
public class TextAnimation extends Sprite { private var t:TextField;
public function TextAnimation ( ) { // Создаем объект TextField t = new TextField( ); t. text = «Hello»;
t. autoSize = TextFieldAutoSize. LEFT; addChild(t);
// Регистрируем функцию moveTextRight( ) для получения уведомлений // о возникновении события Event. ENTER_FRAME addEventLi stener(Event. ENTER_FRAME. moveTextRi ght);
public function moveTextRight (e:Event):void { t. x += 10;
}
}
Всякий раз, когда при выполнении кода из листинга 24.1 среда Flash проверяет запланированное обновление экрана, она осуществляет диспетчеризацию события Event. ENTER_FRAME. В результате вызывается функция moveTextRight ( ), и среда выполнения Flash обновляет экран. С течением времени многократный вызов функции moveTextRight ( ) создает анимацию объекта TextField, перемещая его по экрану. Однако пока функция moveTextRight ( ) перемещает объект TextField t вправо до бесконечности. Чтобы объект TextField не перемещался дальше позиции с координатой 300 по оси X, мы должны изменить метод moveTextRight ( ) — значение 10 должно прибавляться к значению переменной t. х только в том случае, когда значение переменной t. х меньше или равно 300, как показано ниже.
Руководство по actionscript. часть 4, стр. 134
Public function moveTextRight (e:Event):void { // Добавляем 10 к значению переменной t. x только в том случае, когда // значение переменной t. x меньше или равно 30Q. if (t. x <= 300) { t.x += 10;
// Не допускаем, чтобы значение переменной t. x превысило 300 if (t. x > 300) { t. x = 300;
}
}
}
Предыдущий код справляется с задачей остановить объект TextField в точке с координатой 300 по оси X, но при этом он позволяет методу moveTextRight (‘ ) бесполезно продолжать свое выполнение даже после того, как объект TextField достигнет заданной координаты. Чтобы избежать ненужных вызовов функции, мы отменяем регистрацию приемника moveTextRight ( ) для событий Event. EVENT_FRAME, когда объект достигает точки с координатой 300 по оси X. Вот этот код:
public function moveTextRight (e:Event):void { if (t. x <= 300) { t.x += 10; if (t.x > 300) { t. x = 300;
}
} else {
// Прекращаем получать уведомления о возникновении события // Event. ENTERFRAME
removeEventListener(Event. ENTER_FRAME, moveTextRight);
}
Теперь, когда мы изменили метод moveTextRight ( ), чтобы получение уведомлений о возникновении события Event. ENTER_FRAME происходило только при необходимости, наш простой класс TextAnimation завершен. В листинге 24.2 показан окончательный код.
Руководство по actionscript. часть 4, стр. 135
Листинг 24.2. Анимация объекта TextField по горизонтали до координаты 300 по оси X
package { import flash. display.*; import flash. events.*; import flash. text.*;
public class TextAnimation extends Sprite { private var t:TextField;
public function TextAnimation ( ) { // Создаем объект TextField t = new TextField( ); t. text = «Hello»;
t. autoSize = TextFieldAutoSize. LEFT; addChild(t);
// Регистрируем функцию moveTextRight( ) для получения уведомлений // о возникновении события Event. ENTER_FRAME addEventLi stener(Event. ENTER_FRAME. moveTextRi ght);
}
public function moveTextRight (e:Event):void { if (t. x <= 300) { t.x += 10; if (t.x > 300) { t. x = 300;
}
} else {
// Прекращаем получать уведомления о возникновении события // Event. ENTERFRAME
removeEventLi stener(Event. ENTER_FRAME. moveTextRi ght);
}
}
}
}
Обратите внимание, что в листинге 24.2 анимация одного объекта (TextField) осуществляется под управлением другого объекта (класса TextAnimation). Структура, в которой «один объект управляет анимацией другого объекта», является типичной для приложений с централизованным управлением анимацией. В подобных приложениях один класс выступает в роли режиссера для всех анимаций в приложении, регистрируя единственный метод для уведомлений о возникновении события Event. ENTER_FRAME и вызывая процедуры для создания анимации над всеми подчиняющимися объектами. В отличие от этого, в приложении с децентрализованным управлением анимацией отдельные классы управляют своей собственной анимацией самостоятельно, определяя свои собственные методы-приемники события Event. ENTER_FRAME. Для сравнения в листинге 24.3 показан подкласс TextTo300 класса TextField. Здесь, как в предыдущем примере, объект перемещается в точку с координатой 300 по оси X, но делает это самостоятельно. Обратите внимание, что класс TextTo300
определяет методы start ( ) и stop ( ), которые могут быть использованы для воспроизведения и остановки анимации.
Руководство по actionscript. часть 4, стр. 136
Листинг 24.3. Децентрализованное управление анимацией
package { import flash. display.*; import flash. events.*; import flash. text.*;
public class TextTo300 extends TextField { public function TextTo300 ( ) { }
public function moveTextRight (e:Event):void { if (x <= 300) { x += 10;
if (t. x > 300) { t. x = 300;
}
} else { stop( );
}
}
public function start ( ):void { // Начинаем воспроизведение анимации addEventLi stener(Event. ENTER_FRAME. moveTextRi ght);
}
public function stop ( ):void { // Останавливаем анимацию
removeEventLi stener(Event. ENTER_FRAME, moveTextRi ght);
}
}
}
Следующий код демонстрирует основной класс SWF-файла, в котором применяется класс TextTo300 из листинга 24.3:
package { import flash. display.*; import flash. text.*;
public class TextAnimation extends Sprite { private var t:TextTo300;
public function TextAnimation ( ) { // Создаем экземпляр класса TextTo300 t = new TextTo300( ); t. text = «Hello»;
t. autoSize = TextFieldAutoSize. LEFT; addChild(t);
// Запускаем анимацию t. startC );
}
}
}
Влияние скорости кадров на анимации, создаваемые с помощью события Event. ENTER_FRAME. Поскольку событие Event. ENTER_FRAME синхронизируется со скоростью кадров, для более высокой скорости функции-приемники события Event. ENTER_FRAME будут вызываться чаще по сравнению с более низкой скоростью. Таким образом, если мы перемещаем объект по экрану, используя функцию-приемник события Event. ENTER_FRAME, увеличение скорости кадров может предвещать увеличение скорости анимации.
Руководство по actionscript. часть 4, стр. 137
Например, при программировании перемещения объекта t в предыдущем разделе мы неявно задавали его скорость относительно скорости кадров. Наш код говорит: «При прохождении каждого кадра перемещаем объект t на 10 пикселов вправо»:
ball_mc._x += 10:
Следовательно, скорость объекта t зависит от скорости кадров. Если скорость кадров равна 12 кадрам в секунду, то скорость перемещения объекта t составит 120 пикселов в секунду. Если скорость кадров равна 30 кадрам в секунду, скорость перемещения объекта t составит 300 пикселов в секунду!
При создании анимаций на базе сценариев вы можете поддаться искушению рассчитать расстояние перемещения элемента с учетом назначенной скорости кадров. Например, если назначенная скорость кадров составляет 20 кадров в секунду и мы хотим, чтобы элемент перемещался со скоростью 100 пикселов в секунду, естественно предположить, что скорость объекта должна составлять 5 пикселов за один кадр (5 пикселов х 20 кадров в секунду = 100 пикселов в секунду).
Однако у этого подхода есть два существенных недостатка.
? Всякий раз, изменяя назначенную скорость кадров, мы должны модифицировать весь код, который вычисляет скорость с учетом данной скорости кадров.
? Как было сказано в гл. 23, среда выполнения Flash не всегда достигает назначенной скорости кадров. Если компьютер слишком медленный, чтобы отображать кадры со скоростью, соответствующей назначенной, анимация будет замедляться. Это замедление может варьироваться даже в зависимости от загрузки системы; если запущены другие программы или если среда Flash выполняет некую задачу с интенсивной загрузкой процессора, то скорость кадров может упасть на короткий промежуток времени, после чего будет восстановлен нормальный темп.
В некоторых случаях анимация, воспроизводимая со слегка различающимися скоростями, может считаться допустимой. Но когда визуальная точность имеет не последнее значение или когда мы создаем, скажем, игру с активными действиями, гораздо более подходящим вариантом является определение расстояния для перемещения объекта с учетом прошедшего времени, а не в зависимости от назначенной скорости кадров. Дополнительную информацию можно найти далее, в разд. «Анимация, основанная на скорости».
Создание анимации с использованием события TimerEventJIMER
В предыдущем разделе рассказывалось о том, каким образом использовать событие Event. ENTER FRAME для создания анимаций, которые синхронизируются со скоростью кадров среды выполнения Flash. В этом разделе мы познакомимся с тем, как синхронизировать анимации с произвольным промежутком времени, задаваемым с помощью класса flash. utils. Timer.
Руководство по actionscript. часть 4, стр. 138
Класс Т ime г — это универсальный служебный класс, предназначенный для выполнения кода через указанный промежуток времени. Каждый объект Timer осуществляет диспетчеризацию событий TimerEvent. TIMER с частотой, устанавливаемой программистом. Функции, которые желают выполняться с данной частотой, регистрируются в объекте Timer для событий TimerEvent. TIMER.
«г *
Класс Timer не гарантирует частоту, с которой выполняются его функции-приемники. Если система или среда выполнения Flash оказалась занята в тот момент, когда объект Timer запланировал выполнить свои функции-приемники, выполнение функций будет отложено. Информацию о том, как избежать подобных задержек в анимации, можно найти в разд. «Анимация, основанная на скорости».
Для использования класса Timer необходимо выполнить следующую общую последовательность действий.
1. Создать новый объект Т ime г:
var timer:Timer = new Timer( );
2. Установить частоту (в миллисекундах), с которой должны генерироваться события TimerEvent. TIMER. Например, следующий код устанавливает частоту, равную 100 мс (за одну секунду осуществляется диспетчеризация десяти событий TimerEvent. TIMER):
timer. del ay = 100;
3. Установить общее количество генерируемых событий TimerEvent. TIMER. Например, следующий код говорит таймеру сгенерировать всего пять событий TimerEvent. TIMER:
timer. repeatCount = 5;
Значение 0 сбрасывает ограничение (генерировать события TimerEvent. TIMER постоянно или до тех пор, пока не скажут остановиться):
timer. repeatCount = 0: // Бесконечные события TimerEvent. TIMER
4. Создать одну или несколько функций, которые будут периодически вызываться в ответ на события TimerEvent. TIMER объекта Timer. Функции, регистрируемые для событий TimerEvent. TIMER, должны определять единственный параметр с типом данных TimerEvent.
Руководство по actionscript. часть 4, стр. 139
Function timerListener (e:TimerEvent):void { // Этот код будет выполняться при возникновении события TimerEvent. TIMER
5. Зарегистрировать функцию, созданную на шаге 4, в объекте Timer для событий TimerEvent. TIMER:
timer. addEventLi stenerdimerEvent. TIMER, timerListener);
6. Использовать метод экземпляра start ( ) класса Timer, чтобы запустить таймер. Как только таймер будет запущен, он начнет генерировать события TimerEvent. TIMER в соответствии со значениями, указанными для переменных delay и repeatCount:
timer. start( );
В качестве альтернативы шагам 2 и 3 из приведенного списка установить значения переменных delay и repeatCount можно через параметры конструктора класса Timer, как показано в следующем коде:
var timer:Timer = new TimerdOO, 5); // Переменной delay присваиваем
// значение 100, а переменной // repeatCount — значение 5
Следующий код демонстрирует применение предыдущих шагов в простой реальной ситуации: отображение слова «GO!» после трехсекундной задержки. Обратите внимание, что вторым аргументом функции-конструктора класса Timer является значение 1. Это значит, что произойдет диспетчеризация только одного события TimerEvent. TIMER. Подобный код может быть использован в начале теста с ограниченным временем выполнения или гоночной игры.