учебники, программирование, основы, введение в,

 

Классы и клипы

Мы с вами разобрались уже практически во всех нюансах ActionScript. Однако не нужно забывать, что Flash MX - это все-таки в первую очередь дизайнерская среда. А потому необходимо, чтобы наши классы (унаследованные, скажем, от MovieClip) обладали всеми преимуществами, которыми обладают обычные клипы. То есть мы должны уметь поместить на сцену экземпляр нового клипа, и при этом Флэш должен сообразить, что этому клипу нужно вызвать новый конструктор и что мы можем пользоваться всеми новыми функциями. Для этого существует механизм, называемый регистрацией классов.
Регистрация классов
Для того чтобы зарегистрировать класс, то есть, фактически, ассоциировать наш класс с каким-то клипом, достаточно выполнить одну строчку кода специального вида. После этого Флэш будет знать, что клип, с которым мы ассоциировали наш класс, является не просто объектом MovieClip, а экземпляром нашего класса (в большинстве случаев унаследованного от MovieClip) и вести будет себя с ним соответствующим образом: при создании (как с помощью простого помещения на сцену, так и с помощью использования attachMovie) клипа вызывается конструктор нашего класса; клипы, вложенные в наш клип, становятся доступны как поля класса и т. п.
Код для регистрации
Итак, какой же код регистрирует класс?
За это отвечает единственный "статический" метод класса Object:
Object.registerClass (<LinkageIdentifier>, <ConstructorFunction>);

Здесь <LinkageIdentifier> - это имя идентификатора (строка), заданное в поле Identifier параметров символа клипа, а <ConstructorFunction> - функция-конструктор нашего класса.
Пример:
function MyClass {
//...
}
//....
Object.registerClass ("MyClassSymbol", MyClass);

Где размещаем определение класса
Допустим, мы сделали класс Circle с двумя методами:
function Circle() {
}
Circle.prototype = new MovieClip();
Circle.prototype.show = function () {
this._visible = true;
}
Circle.prototype.hide = function () {
this._visible = false;
}

И связали его с клипом CircleClip, который является простым кружком:
Object.registerClass ("CirlceClip", Circle);

Спрашивается: где поместить весь этот код?
Классическое решение для Flash MX - поместить его в первом слое Actions клипа CircleClip в первом кадре.
С одной стороны, это неплохо: в любом случае, определение класса "не потеряется", оно привязано к клипу, с которым связан класс. Но редактировать код таких классов не очень удобно. Более удачным решением будет вынесение собственно кода в отдельный файл, который подключается в указанном выше месте с помощью строки #include <filename>.as. Такой способ ближе к подходу, используемому во Flash MX 2004.
Но это еще не все. В любом случае, определение класса нужно помещать внутри блока #initlclip.
Используем initclip
#initclip означает начало блока инициализации (который заканчивается #endinitclip). Что такое блок инициализации? Флэш гарантирует, что весь код, помещенный внутрь блоков #initclip, будет выполнен раньше кода, не помещенного в блоки #initclip. Это очень удобно; например, если мы помещаем все определения классов внутрь блоков #initclip (так всегда и следует поступать), то мы всегда можем быть спокойны, что при любом использовании имени класса вне контекста #initclip оно уже известно флэш-плееру.
Поэтому вот как должен выглядеть первый кадр слоя Actions клипа CircleClip (или include-файл, который мы подключаем в этом же месте):
#initclip
function Circle() {
}
Circle.prototype = new MovieClip();
Circle.prototype.show = function () {
this._visible = true;
}
Circle.prototype.hide = function () {
this._visible = false;
}
Object.registerClass ("CirlceClip", Circle);
#endinitclip

Включаем необходимые опции
Если вы теперь проверите, как работают наши класс и клип, то может случиться, что они и дальше никак не связаны.
В чем же дело? Вспомним, что для того, чтобы к символам библиотеки можно было обращаться программно (из кода ActionScript), нужно включить флажок "Export for ActionScript" в параметрах клипа. Только после этого Identifier, заданный там же, начинает что-то значить для компилятора Флэш МХ, иначе в ActionScript символ абсолютно недоступен.

Порядок инициализации клипов

Интересно, в каком порядке инициализируются клипы? Например, у нас есть несколько клипов: Сircle, Triangle и Rectangle, а также связанные с ними классы, код которых мы поместили в первые кадры клипов. О каких классах Флэшу раньше становится известно?
А какая нам разница? В данном случае, видимо, никакой.
Но посмотрим, какие проблемы здесь в принципе могут возникнуть.

Трудности с порядком инициализации

Пусть у нас есть клип RoundedRectangle, который мы хотим наследовать от Rectangle.
Например, пусть в клипе Rectangle (первый кадр слоя Actions) имеем такой код:

#initclip
function Rectangle() {
//....
}
Rectangle.prototype = new MovieClip();
Rectangle.prototype.getArea() {
   return this.width * this.height;
}
//...
#endinitclip
        

А в клипе RoundedRectangle (первый кадр слоя Actions) - такой код:

#initclip
function RoundedRectangle () {
//....
}
RoundedRectangle.prototype = new Rectangle();
RoundedRectangle.prototype.getArea() {
   //...
   return somethingMoreComplex;
}
//...
#endinitclip
        

Мы запускаем программу, и может оказаться, что с классом Rectanlge какие-то проблемы, он как будто не отнаследовался от Rectangle. Почему?
Дело в том, что в тот момент, когда флэш-плеер наткнулся на строчку

RoundedRectangle.prototype = new Rectangle();
        

он еще не знал, что такое Rectangle, а следовательно, не смог создать его экземпляр.
В таких случаях порядком инициализации нужно управлять. Это делается с помощью инструкций #initclip специального вида.

Что означают числа в initclip

Как мы помним, Флэш гарантирует, что весь код, помещенный внутрь блоков #initclip, будет выполнен раньше кода, не помещенного в эти блоки.
А что, если мы используем имя класса внутри другого контекста #initclip? Это и есть проблема из предыдущего параграфа. Мы использовали имя Rectangle в одном блоке #initclip, а RoundedRectangle - в другом, поэтому мы можем получить уже описанную выше проблему.
Все, что нам нужно, - это как-то сообщить Флэшу, что один блок #initclip нужно обрабатывать прежде, чем другой.
Для этого и существует вторая форма использования директивы #initclip:
#initclip N, где N - целое число.
Флэш гарантирует, что блок с меньшим числом будет обработан раньше.


Примеры правильного и неправильного использования
Приведем пример неправильного использования #initclip (такое вполне можно сделать по ошибке). Для каждого нового класса-клипа заводим уникальный номер #initclip. В результате очень быстро эти номера начинают идти через 10 (потому что никто не помнит, какой последний - 13-й или 14-й), затем через 100 - и через некоторое время появляются #initclip с номерами 100000.
Ничего особо плохого, кроме путаницы, в этом нет, но ничего хорошего - тоже.
Правильной же стратегией является следующая:
  • Нигде, если это не необходимо, номера #initclip не ставятся.
  • Если нужно указать, что один клип должен быть инициализирован первым (видимо, это означает, что он является базовым классом для второго клипа или каким-нибудь другим образом от него зависит), для первого нужно указать #initclip 0, для второго - #initclip 1 и т. п.
  • Для каждой новой иерархии классов нумерацию нужно начинать с 0.

Исправим наш пример:
Клип Rectangle (первый кадр слоя Actions):
#initclip 0
function Rectangle() {
//....
}
Rectangle.prototype = new MovieClip();
Rectangle.prototype.getArea() {
return this.width * this.height;
}
//...
#endinitclip

Клип RoundedRectangle (первый кадр слоя Actions):
#initclip 1
function RoundedRectangle () {
//....
}
RoundedRectangle.prototype = new Rectangle();
RoundedRectangle.prototype.getArea() {
//...
return somethingMoreComplex;
}
//...
#endinitclip

Дерево владения и механизм attachMovie
Механизм attachMovie - это еще одно средство динамического создания объектов (наравне с duplicateMovieClip) во Флэш, который появился в версии Флэш 5. Хотя, пожалуй, для всех программистов это наиболее понятный и естественный способ: создание объекта из класса, или экземпляра символа из символа библиотеки.
Создается объект следующим образом:
myMovieClip.attachMovie(idName, newName, depth [, initObject] );

Здесь myMovieClip - это клип, в который нужно поместить новый клип, idName -идентификатор, указанный в свойствах символа, экземпляр которого вы хотите создать (строка), newName - имя нового объекта, depth - уровень, на который нужно поместить экземпляр символа (см. ниже), а initObject - что-то вроде параметров конструктора, а точнее, уже созданный объект, полями которого нужно проинициализировать вновь создаваемый объект (см. ниже).
Пример:
_root.attachMovie ("MySymbol", "my_mc", 1);

Для того чтобы использовать attachMovie, нужно обязательно установить галочку Export For ActionScript в свойствах символа.
Заметим, что объекты, созданные с помощью attachMovie, помещаются в то же дерево владения, что и объекты, перенесенные на сцену непосредственно, а также объекты, созданные с помощью duplicateMovieClip.
Поскольку про duplicateMovieClip мы уже подробно рассказывали в третьей лекции, часть материала вам сейчас покажется знакомой, и мы надеемся, что за счет этого то, что мы описываем здесь, будет понятнее. В конце концов, все время листать книгу, возвращаясь к третьей лекции, было бы исключительно неудобно.
Разница между attachMovie и дублированием клипов
Когда же следует использовать attachMovie, а когда - duplicateMovieClip?
Если формально, то разницы практически нет, за исключением того, что для использования duplicateMovieClip нужно иметь уже готовый объект, а для использования attachMovie - нет. Причем, если готовый объект был помещен на сцену непосредственным перетаскиванием, вы его не сможете удалить с помощью removeMovieClip, а это иногда может быть неудобно.
Кроме того, используя attachMovie, можно положить новый объект в любое место, в то время как duplicateMovieClip позволяет создать его только в том же клипе, в котором лежит исходный клип.
Инициализация объектов, initObject
А как же передать параметры конструктору, если мы создаем объект с помощью attachMovie (или duplicateMovieClip)? К сожалению, такого механизма не существует. Flash MX предлагает некоторую замену, а именно дополнительный параметр функций attachMovie и duplicateMovieClip - initObject, в качестве которого можно передать готовый объект, полями которого затем будет проинициализирован вновь создаваемый объект.
Правда, нам эта функциональность кажется несколько сомнительной, и мы предлагаем следующее.
Если вы хотите добиться функциональности конструктора, сделайте отдельную функцию init вашему классу и вызывайте ее после создания клипа.
Если же вы хотите иметь конструктор копирования (функцию клонирования), лучше тоже напишите ее сами, чтобы быть уверенным, что все поля будут скопированы надлежащим образом (вы можете сделать поверхностное или глубокое копирование, в зависимости от того, что вам нужно).

Счетчики уровня
Вернемся к параметру depth. Этот параметр отвечает за уровень, на который будет помещен объект. (Если вы внимательно читали третью лекцию, то помните, что это такое. Однако эти сведения очень важны, так что позволим себе их повторить).
Зачем нужны уровни? С их помощью мы управляем тем, какой объект визуально должен быть расположен "выше", а какой "ниже". Кроме того, у них есть такое важное свойство: на одном уровне не может лежать больше одного объекта (они затирают друг друга).
Объясним более детально.
Мы знаем, что объекты можно поместить на сцену непосредственно либо динамически (с помощью duplicateMovieClip или attachMovie). Каждый объект, независимо от способа помещения на сцену, лежит на своем уровне, и чем больше номер уровня, тем "выше" лежит объект.
Если объект помещается на сцену непосредственно, то уровень объекта зависит от:

  • слоя, в который помещен объект;
  • порядка размещения объектов в одном слое, которым можно управлять из меню Modify->Arrange.

Числовые значения уровней объектов, помещенных на сцену непосредственно, мы получить не можем, но про них известно две вещи:

  • в данном клипе они всегда меньше, чем все значения уровней динамически помещенных объектов (если вы не использовали метод swapDepths);
  • они никогда не перекрываются, то есть всегда имеют разные значения, и те объекты, которые мы положили выше, имеют большие значения уровня, чем те, которые лежат ниже. Об этом заботится Флэш-среда.

Если объекты помещаются на сцену динамически, то уровень объекта зависит только от параметра depth. Значение этого параметра должно быть целым числом и может быть как положительным, так и отрицательным. Чуть ниже мы рассмотрим, зачем может понадобиться отрицательная величина depth.
Все эти уровни, как правило, располагаются выше уровней объектов, помещенных на сцену непосредственно (даже если вы зададите отрицательное значение depth). Дело в том, что непосредственно нарисованные объекты имеют очень большие по модулю отрицательные значения depth (в районе -16384).

SwapDepths
Если это зачем-то нужно, уровни двух объектов можно поменять местами с помощью метода swapDepths:
my_mc1.swapDepths (my_mc2);
   
Этот метод можно использовать для всех объектов, как помещенных на сцену непосредственно, так и динамически, в том числе менять местами уровни двух объектов разного "типа" (в смысле способа помещения на сцену).
Пример:
Пусть у нас в библиотеке есть клип "Symbol2" (синий кружок). Один его экземпляр вытащен на сцену непосредственно, назван s1 и раскрашен в красный цвет. Остальные создаются с помощью следующего кода:
attachMovie ("Symbol2", "s2", 1);
s2._x = 100;
s2._y = 200;
attachMovie ("Symbol2", "s3", 0);
s3._x = 70;
s3._y = 200;
s2._alpha = 30;
   
Вот что получается (здесь внизу - s1, слева - s3, справа - s2):
Все, как мы и ожидали, s2 - выше всех, s1 - ниже всех.
Теперь можем выполнить команду s1.swapDepths (s3);
После чего получим такую картинку:
Мы видим, что s2, как и раньше, выше всех, а s1 и s3 поменялись местами.
А сейчас представьте, что мы захотели добавить к нашей композиции фон (также динамически). Добавим такой код:
attachMovie ("Background", "bkg", -1);
   //обратите внимание, отрицательный depth
bkg._x = 50;
bkg._y = 200;
   
Результат получим следующий:
Все правильно, под клипом bkg теперь находится только синий кружок, который мы (с помощью swapDepths) поменяли местами с красным кружком, непосредственно помещенным на сцену. А у красного кружка до выполнения swapDepths был уровень непосредственно помещенного на сцену клипа, который по определению размещается на глубоких отрицательных уровнях. Вот почему bkg оказался выше синего кружка, хоть у него (у bkg) и отрицательный depth.
Реализация автоматического счетчика
Иногда бывает нужно динамически создать много объектов. В таких случаях пользуются счетчиками уровней, чтобы по ошибке не положить два объекта на один и тот же уровень. Такими счетчиками можно пользоваться также для генерирования уникального суффикса имени (ведь имена объектов тоже нельзя дублировать).
Пример:
this.depthCounter = 0;
//....
for (var i = 0; i < 10; i++)
   this.attachMovie ("SomeSymbol", "symbol"+(depthCounter++),
depthCounter);
   
Интересным здесь является также тот момент, что счетчик хранится в клипе, в который кладутся новые объекты. То есть, если когда-нибудь позже мы вернемся к созданию новых объектов в клипе, в котором до того уже создавали динамические объекты, мы продолжим считать уровни со старого значения счетчика, сохраненного в этом клипе.
Сложности с удалением клипов и счетчиками
Все объекты, созданные с помощью attachMovie или duplicateMovieClip, можно благополучно удалять посредством removeMovieClip. Здесь все достаточно очевидно, за исключением того, что при удалении определенных объектов у вас будут образовываться "дырки", то есть пустые уровни, а счетчик уровней будет расти, и в конце концов, уровни закончатся. Примером может быть известная игра "жизнь", в которой общее число объектов невелико, но постоянно рождаются новые и умирают старые. Для реализации такой игры нам понадобился бы специальный механизм - механизм распределения уровней.
Распределители уровней
Распределители уровней могут использоваться с разными целями. Вот пример некоторых полезных функций, которые они могли бы выполнять:

  • Вернуть любой свободный уровень;
  • Вернуть свободный уровень, выше (ниже) которого гарантированно ничего не лежит.
  • Вернуть свободный уровень, который гарантированно находится между уровнями двух заданных объектов (или просто выше какого-то объекта или ниже какого-то объекта).

Даже только одной первой функции уже достаточно для реализации игры "жизнь", поскольку там не важно, как размещены объекты друг относительно друга по вертикальной оси.
Для реализации такого распределителя достаточно хранить самый верхний занятый уровень, а также массив "дырок", и при каждом запросе возвращать любой элемент массива, если он не пустой, или самый верхний занятый уровень плюс 1 в противном случае.
Опишем два ключевых момента в реализации более сложного распределителя уровней, который поддерживает все три функции (из перечисленных выше).
Во-первых, такой распределитель должен хранить оптимизированную для поиска структуру данных с "дырками" (свободными уровнями), например, сортированный массив. Это может понадобиться, скажем, для того, чтобы для двух любых заданных уровней можно было определить дырку между ними.
Во-вторых, нужно помнить, какой объект на каком уровне лежит. Это можно сделать двумя способами: с помощью эффективной хеш-таблицы или же просто запоминать уровень объекта в самом объекте.
Слежение за переменными
Допустим, что вам нужно контролировать изменения значений какого-то поля объекта. Например, нужно запретить изменение значения какой-либо переменной либо "навесить" дополнительное поведение к уже готовому коду.
Классическим решением данной проблемы является использование свойств вместо полей, в таком случае всю обработку изменения вы можете произвести в setter-методе.
Свойства, свои и чужие объекты
Если объект "свой", то есть мы можем ему завести setter-метод, то все достаточно тривиально, и мы это обсуждать не будем.
Допустим, что все обстоит несколько сложнее, а именно: у объекта уже есть поле, которое всеми активно используется, а вам зачем-то очень нужно контролировать его изменение, при этом сам объект "чужой", то есть вы не хотите/не можете изменять его исходный код.
Хорошо было бы иметь функцию, которая позволяет следить за полями в "чужом" объекте. Рассмотрим первый возможный вариант. Напишем функцию watchFunction, которая стирает существующее поле в объекте, сохраняя его значение, создает в объекте вместо него свойство с таким же именем и определяет такие getter и setter, чтобы можно было передать функцию реакции:
function reaction (sender, oldValue, newValue){ //функция реакции
trace ("Object: " + sender + ", old value: "
+ oldValue + ", newValue: " + newValue);
return newValue; //нужно вернуть значение,
//которое будет присвоено свойству
}
circle.myField = 5; //исходная переменная в объекте
//заводим функцию прямо в Object.prototype, чтобы можно было
//вызывать ее для любого объекта.
Object.prototype.watchFunction = function (propName, reaction) {
var tmpValue = this[propName];             //сохраняем значение
delete this[propName];                            //удаляем переменную
this["get" + propName] = function () {     //определяем getter
return this["field" + propName]; //"field" + propName -
// новое имя переменной
};
this["set"+propName] = function (newValue) {
//определяем setter
this["field" + propName] =
reaction(this, this["field" + propName], newValue);
// вызываем функцию реакции, возвращенное значение устанавливаем
// как новое значение свойства
};
//регистрируем новое свойство
this.addProperty (propName, this["get"+propName], this["set"+propName]);
this["field"+propName] = tmpValue;
// восстанавливаем исходное значение
}
circle.watchFunction ("myField", reaction); //включаем слежение
circle.myField = 6;
circle.myField = 7;

Теперь, если вы посмотрите на вывод программы, то увидите вот что:
Object: _level0.circle, old value: 5, newValue: 6
Object: _level0.circle, old value: 6, newValue: 7

Если не свойства, то что?
А если нам почему-то не хочется использовать такой способ?
Есть еще один вариант - во Флэш МХ есть функция watch, которая позволяет следить за переменными объекта.
Ее использование выглядит следующим образом: object.watch (propName, reactionFunction, userData), где propName - это имя свойства, за которым нужно следить (строка), reactionFunction - это функция реакции, определение которой должно выглядеть так:
function reactionFunction (propName, oldVal, newVal, userData) {
//...
return newVal;      //здесь возвращается значение,
// которое будет в итоге присвоено переменной
}

Обратите внимание, что параметр userData - это дополнительные данные, которые могут быть переданы в функцию.
Пример использования стандартной функции watch:
function reaction (sender, oldValue, newValue) { //функция реакции
trace ("Object: " + sender + ", old value: " +
oldValue + ", newValue: " + newValue);
return newValue; //нужно вернуть значение,
// которое будет присвоено свойству
}
circle = {};
circle.myField = 5; //исходная переменная в объекте
circle.watch ("myField", reaction); //включаем слежение
circle.myField = 6;
circle.myField = 7;

Рассмотрим различия в интерфейсе стандартной функции watch и "нашей" функции watchFunction, которую мы рассмотрели в предыдущем примере.
Фактически, их только два:

  • во-первых, при использовании стандартной функции watch в callback-метод реакции в качестве первого параметра передается имя поля объекта, за которым производится слежение, а в нашей функции - ссылка на объект.
  • во-вторых, в стандартной функции watch есть возможность передать дополнительные данные в функцию реакции через четвертый параметр функции реакции и функции watch. В нашей функции такая возможность не предусмотрена.

Ограничения
Функцию watch нельзя использовать для мониторинга полей get / set.
Ни один из приемов, к сожалению, нельзя использовать для мониторинга "стандартных" полей типа _x, _y и пр.
Единственное, что можно предложить для мониторинга значений таких полей, - это setInterval(), но это очень плохой способ, ибо реакцию вы получите с большой задержкой, и, видимо, после перерисовки изображения. Кроме того, значение может успеть измениться к первоначальному значению, пока сработает callback-метод setInterval.
Использование setInterval вместо линейки времени
Иногда для отслеживания времени проще обратиться к программным методам, а не делать дополнительные кадры для того, чтобы узнать, что прошло какое-то время. В таких случаях удобно бывает воспользоваться функцией setInterval, которая обеспечивает вызов нужной нам callback-функции (обработчика) с заданной частотой.
Существует две функции setInterval, первая форма предназначена для вызова методов, которые не обращаются к this, то есть вызова как бы "просто функций", а другая - для вызова методов объектов:

  • setInterval (function, interval, params);
  • setInterval (object, methodName, interval, params);

Если пользоваться первой версией setInterval, то в функции function, переданной в качестве параметра setInterval, this будет не определен. Если же пользоваться второй версией setInterval, то он будет ссылаться на объект object, переданный как параметр setInterval.
Обе версии setInterval возвращают так называемый intervalID, который потом нужно передать в функцию clearInterval, чтобы прекратить вызовы callback-функции.
Обратите внимание, что в первом случае передается непосредственно функция, тогда как при использовании второй функции ожидается имя метода, то есть его нужно брать в кавычки.
Interval задается в миллисекундах.
Существуют следующие правила для вызовов setInterval:

  • Если параметр interval меньше, чем период обновления клипа (обратная величина частоте кадров), то есть, callback-функция должна вызываться чаще, чем перерисовываются кадры, то флэш-плеер будет стараться вызывать callback-функцию с частотой, возможно более близкой к заданному интервалу. В таком случае нужно вызывать функцию updateAfterEvent в обработчике, чтобы обеспечить достаточно частую перерисовку экрана. Функция updateAfterEvent не имеет параметров, и использовать ее можно только в контексте функции, переданной в качестве обработчика setInterval или в контексте обработчика onClipEvent.
  • Если параметр interval больше, чем период обновления клипа (обратная величина частоте кадров), то есть, callback-функция должна вызываться реже, чем перерисовываются кадры, то флэш-плеер будет стараться вызывать callback-функцию в моменты, предшествующие перерисовке новых кадров, чтобы минимизировать количество необходимых перерисовок.

Приведем пару примеров использования setInterval, которые также помогут нам лучше понять механизм работы этой функции.
Пример 1.
N = 10;
function code1 () {
for (var i = 0; i < N; i++);
trace ("code1 invoked");
}
function code2 () {
for (var i = 0; i < N; i++);
trace ("code2 invoked");
}
int1 = setInterval (code1, 10);
int2 = setInterval (code2, 100);

В данном случае из результатов Output видим, что все работает, как ожидается, то есть обе сallback-функции вызываются с ожидаемой частотой, примерно один вызов второй на 10 вызовов первой.
А теперь попробуем увеличить время, необходимое для выполнения кода функций code1() и code2().
Пример 2.
N = 10000;
function code1 () {
for (var i = 0; i < N; i++);
trace ("code1 invoked");
}
function code2 () {
for (var i = 0; i < N; i++);
trace ("code2 invoked");
}
int1 = setInterval (code1, 10);
int2 = setInterval (code2, 100);

Интересно, что функции оказываются в равноправных условиях, то есть теперь количество вызовов каждой из них примерно одинаково, и вызываются они по очереди.
Так происходит потому, что из-за изменения времени выполнения функций соблюсти интервалы становится невозможно (ни один, ни второй), в результате заявленные интервалы вообще перестают влиять на частоту вызовов.
А теперь проведем небольшое исследование setInterval на синхронность. Вопрос состоит в следующем: какой код может быть прерван, а какой - нет?
Создадим ролик из трех кадров, в первый кадр поместим такой код:
function code1 () {
_global.v = 1;
if (_global.v != 1)
trace ("interrupted");

}
function code2 () {
_global.v = 2;
if (_global.v != 2)
trace ("interrupted");
}
int1 = setInterval (code1, 10);
int2 = setInterval (code2, 10);

Во второй - такой:
_global.v = 3;
if (_global.v != 3)
trace ("interrupted");

И в третий - такой:
gotoAndPlay (2);

В этом примере есть три "synchronized"-фрагмента, выделенные жирным шрифтом. Допустим, что Флэш может прерывать выполнение кода из одного обработчика setInterval для вызова другого, или же прерывать выполнение кода из кадра (второго кадра, в данном случае), с той же целью. Тогда рано или поздно наступит ситуация, когда один из трех фрагментов кода будет прерван другим, и на консоль будет выведено сообщение "interrupted!". Но на практике этого не происходит, из чего следует, что весь подобный код выполняется синхронно, и не может быть прерван.
А может ли обработчик setInterval быть вызван между вызовами кода из двух разных кадров? Это логично, иначе, когда еще ему вызываться. Но проверим это, переместив две строчки из второго кадра в третий.
Теперь второй кадр выглядит так:
_global.v = 3;

А третий - так:
if (_global.v != 3)
trace ("interrupted");
gotoAndPlay (2);

И действительно, в консоль выводятся сообщения "interrupted".

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика