Одной из основных особенностей Flash является возможность движения объектов; двигающиеся объекты можно добавлять ко всему, что создается во Flash. В этой лекции мы рассмотрим некоторые из способов создания во Flash MX различных типов движения.
Некоторым читателям эти принципы уже могут быть знакомы, однако мы также будем изучать новые возможности Flash MX, позволяющие обеспечивать движение наиболее простыми и эффективными методами, нежели это было прежде. Чтобы продемонстрировать гибкость Flash, рассмотрим несколько различных типов движения, которое вы, возможно, захотели бы добавить в свои проекты, а также способы его применения. Иногда мы будем использовать ActionScript для получения следующих типов движения:
Сначала рассмотрим, как можно использовать эти типы движения для перемещения объектов со следом от движения по осям X и Y. Также обратим внимание на то, как применить это движение к другим параметрам, таким как размер объекта.
Помимо самого движения, мы рассмотрим различные способы применения движения с помощью ActionScript: как вызывать движение щелчком мыши и как лучше поддерживать движение, т.е. как начать его и как завершить.
Наконец, остановимся на учебном примере, содержащем несколько картинок, появляющихся в области просмотра при щелчке на кнопке. Подходы, описанные в упражнении, включают в себя создание маски с помощью ActionScript, а также динамическое применение кнопки.
Итак, шутки в сторону! Начнем с рассмотрения покадрового движения.
Первым типом движения, о котором мы бы хотели рассказать, является уменьшение скорости движения: объект сначала движется довольно быстро и постепенно замедляется, пока, наконец, не останавливается, достигнув цели. Этот тип движения, вероятно, наиболее прост в реализации - нужно выяснить, как далеко находится объект от цели и затем переместить объект к цели, разделив дистанцию на части. Итак, если объект находится на расстоянии 100 пикселей от конечной точки, мы можем переместить его на половину этого расстояния, после чего он будет в 50 пикселях от цели, далее - в 25, затем в 12,5 и так далее. Движение при этом будет плавным; ведь мы привыкли наблюдать постепенное замедление объектов, а не прерывистые движения.
На рисунке ниже данное движение показано в действии, причем каждая линия представляет собой кадр перемещения объекта на 200 пикселей.

С каждым кадром расстояние объекта до цели сокращается наполовину. В кадре 1 объект перемещается на 100 пикселей, в кадре 2 - на 50 пикселей, в кадре 3 - на 25 пикселей, затем на 12,5 пикселей, затем на 6,25, затем на 3,125 и так далее.
Рассмотрим практическое применение этого движения, создав объект, следующий за мышью. Ничего нового и интересного в этом нет, но наша задача - разобраться в принципе движения.
Мы используем суффикс ' _mc ' в имени согласно установленным Macromedia преобразованиям имен. Использование этого правила имеет два преимущества: во-первых, это помогает отличать разные типы объектов в коде, а во-вторых, таким образом обеспечивается появление подсказки в коде нужного типа при вводе кода в среде разработки.
Мы проделали основные действия. Теперь углубимся в процесс работы.
onClipEvent (enterFrame).Благодаря новой объектной модели, во Flash MX этого можно больше не делать. Мы можем динамически прикреплять события clipEvents к фильмам, что позволит всему или почти всему коду находиться в одном месте, а также позволит манипулировать этими событиями во время выполнения, например, включать и выключать их или изменять какую-либо функциональность.
Это достигается посредством присвоения функции управляющему элементу фильма onEnterFrame. Если фильм имеет функцию, присвоенную к его управляющему элементу onEnterFrame, то он каждый раз будет выполнять эту функцию.
Введите следующий код в кадр 1 слоя script:
ball_mc.onEnterFrame = function() {
this._x += (this._parent._xmouse-this._x) /4 ;
this._y += (this._parent._ymouse-this._y) /4;
};
Этот код сначала создает функцию, которая присваивается управляющему элементу onEnterFrame фильма 'ball_mc'. Код этой функции сначала выясняет, насколько далеко находится объект от указателя мыши, и затем добавляет четверть этого расстояния до позиции фильма. Если вам нужно было бы написать этот код, так сказать, своими словами, то получилось бы следующее: "вычислить разницу между позициями указателя по осям X и Y, затем увеличить позицию X на четверть этого значения".ball motion_01.fla и протестируйте его.Вы увидите, что объект следует за указателем мыши, так же как мы показывали на исходной диаграмме: сначала объект движется быстро, а затем его скорость уменьшается. На этом этапе имеет смысл попробовать применить другие числа вместо 4, чтобы увидеть различные результирующие особенности движения. Если вы собираетесь работать с этим типом движения, вы должны знать, как повлиять на него. Но, прежде всего, вам стоит убрать число из функции и сделать его переменной, которая позволит применять различные числа немного быстрее.
Чтобы было легче менять скорость движения объекта в вашем фильме, добавьте переменную скорости (чем меньше будет значение, тем быстрее будет двигаться объект).
script:var speed = 4;
ball_mc.onEnterFrame = function() {
this._x += (this._parent._xmouse-this._x)/speed;
this._y += (this._parent._ymouse-this._y)/speed;
};
Обратите внимание на то, что внутри функции onEnterFrame слово 'this' ссылается на фильм, которые вы назвали ball_mc, поэтому даже при обращении к переменной скорости из функции нужно вводить this._parent.speed, чтобы оказаться на том уровне, на котором находится фильм. Уровень _root level является уровнем, на котором определена переменная speed.
ball motion_02_speed var.fla и запустите его. Движение происходит таким же образом, однако теперь легче внести изменение в код.Теперь мы значительно изменим код, чтобы фильм перемещался в любое место, где пользователь щелкнет мышью. Аналогично тому, как мы присваивали функцию onEnterFrame фильму, мы можем присвоить ему функцию onMouseDown. Эта функция будет выполняться при каждом нажатии кнопки мыши в любом месте на главном фильме.
Итак, когда пользователь щелкает мышью, нужно сначала установить позиции целей фильма на местоположение щелчка, а затем выполнить функцию onEnterFrame для перемещения на эту позицию:
script:var speed = 4;
// set up target variables
ball_mc.targetX = 0
ball_mc.targetY = 0
ball_mc.moveToMouse = function() {
this._x += (targetX-this._x)/speed;
this._y += (targetY-this._y)/speed;
};
triggerMotion = function() {
// reset target variables on click
targetX = _xmouse;
targetY = _ymouse;
// set the movieclip in motion
this.onEnterFrame = this.moveToMouse;
}
ball_mc.onMouseDown = triggerMotion;
Можно видеть, что этот ActionScript кардинальным образом отличается от предыдущего ActionScript. Если раньше мы присоединяли функцию непосредственно к управляющему элементу onEnterFrame, то на этот раз мы сначала определяем функцию moveToMouse, и при щелчке мышью выражение this.onEnterFrame указывает на эту функцию и вызывает ее в каждом кадре. Функция сама по себе работает так же, как и прежде, за исключением того, что перемещение происходит не на позицию указателя мыши, а на значения переменных targetX и targetY, являющихся переменными местоположения указателя мыши, значения которых определяются при каждом щелчке мышью пользователем.
Этот подход выглядит более эффективным, так как требуется лишь один раз определить функцию, а затем активизировать ее, указав для нее onEnterFrame. Если впоследствии нужно будет выключить движение, это можно сделать, удалив this.onEnterFrame.
ball motion_03_click to move.fla и запустите его.Вы увидите, что объект перемещается на то место, где вы щелкнули мышью, и затем, дойдя до этого места, прекращает свое движение, что является хорошим примером сопоставления с тем случаем, когда шарик постоянно следовал за мышью.
Одним из дополнений, которое обеспечит четкость движения объекта, является его след, оставляемый на тех местах, где только что находился фильм. В нашем случае это полезно для понимания того, как работает замедление движения, однако такие следы могут применяться также и в других обстоятельствах: или как движущийся значок во время загрузки чего-либо, или же в виде своего рода размытости объекта, вызванной его движением. Такие следы могут быть легко созданы посредством дубликации фильма в каждом кадре с использованием функции duplicateMovieClip. Для этого нужно лишь добавить строку кода, увеличивающую значение _root и затем создающую дубликат текущего фильма.
Дубликат находится по умолчанию на той же позиции, что и исходный фильм. На самом деле, дубликат имеет все те же параметры, что и исходный фильм, такие как alpha, xscale, yscale и т.д., однако у него нет функций onEnterFrame или переменных, которые может содержать исходный фильм. Это одно из отличий от Flash 5, в котором если создавался дубликат фильма с присвоенным событием clipEvent, также создавался дубликат этого события, которое происходило и на дубликате фильма. Мы располагаем каждый дубликат на один уровень выше предыдущего, посредством добавления переменной глубины для '_root', значение которой увеличивается с каждым кадром.
DuplicateMovieClip можно интерпретировать, как производитель дубликатов, внешне идентичных исходным фильмам, в которых, однако, не повторяются никакие из исходных действий. В нашем случае, дубликаты не следуют за указателем мыши, а просто остаются там, где они были созданы.
var speed = 4;
ball_mc.moveToMouse = function() {
this._x += (this.targetX-this._x)/this._parent.speed;
this._y += (this.targetY-this._y)/this._parent.speed;
this.duplicateMovieClip("dupe"+this._parent.depth,
this._parent.depth++);
};
ball_mc.onMouseDown = function() {
this.targetX = this._parent._xmouse;
this.targetY = this._parent._ymouse;
this.onEnterFrame = this.moveToMouse;
};
При щелчке на новом месте дубликаты сначала создаются довольно далеко друг от друга, и затем становятся все ближе и ближе друг к другу до тех пор, пока не превратятся в монотонную картину у конечной точки. Однако число объектов здесь растет, пока фильм выполняется, и, чтобы избежать "мешанины", нужно постепенно уменьшать каждый дубликат до его полного исчезновения. В следующем примере мы реализуем именно это дополнение.
Чтобы не забивать фильм слишком большим количеством дубликатов одного и того же объекта, мы можем присвоить каждому дубликату свою собственную функцию enterFrame, которая будет обеспечивать уменьшение дубликатов с каждым кадром и затем удалять их по достижении определенного размера.
Так как мы собираемся работать с каждым из дубликатов, имеет смысл присвоить объекту ball_mc его собственную функцию duplicate(). Приведем код с выделенными вводимыми новыми частями:
var speed = 4;
ball_mc.moveToMouse = function() {
this._x += (this.targetX-this._x)/this._parent.speed;
this._y += (this.targetY-this._y)/this._parent.speed;
this.duplicate() ;
};
ball_mc.duplicate = function() {
var dupe = this.duplicateMovieClip("dupe"+this._parent.depth,
this._parent.depth++);
dupe.onEnterFrame = this._parent.diminish
};
ball_mc.onMouseDown = function() {
this.targetX = this._parent._xmouse;
this.targetY = this._parent._ymouse;
this.onEnterFrame = this.moveToMouse;
};
function diminish(){
// reduce scale gradually
this._xscale = this._yscale-=2;
//remove movieclip when scale goes below zero
if (this._xscale <= 0) {
this.removeMovieClip();
}
}
Итак, мы добавили вызов функции duplicate() и значительно изменили вызов duplicateMovieClip, вынеся вперед var dupe=. Несмотря на то, что в словаре ActionScript указано, что duplicateMovieClip не возвращает никакого значения, на самом деле, возвращается ссылка на созданный фильм.
Здесь мы брали это значение и помещали его в переменную dupe. Мы объявили переменную с помощью выражения var так, что это значение существует только во время выполнения функции, и по окончании ее выполнения оно исчезает за ненадобностью.
Теперь мы можем использовать dupe для ссылки на наш новый дубликат фильма и для присвоения ему функции onEnterFrame, функции diminish, определенной в _root. В функции diminish мы уменьшаем инкременты xscale и yscale с каждым кадром, и когда они достигнут нуля, фильм удаляется.
Если запустить фильм сейчас, будет намного меньше ненужных фильмов, так как каждый из клипов выполняет те действия, которые ему предписаны.
Теперь все работает четко, и количество фильмов лежит в пределах разумного. Однако вы, наверное, заметили, что фильм продолжает идти и создавать дубликаты самого себя даже по достижении конечного пункта. Мы не можем наблюдать движение, так как оно очень незначительно, и нельзя различить дубликаты, так как они находятся на той же позиции, что и исходный объект. Если нужно убедиться в этом, просмотрите переменные или объекты во время просмотра фильма. Нам же нужно выяснить, когда фильм достигнет конца, и затем выключить его. Тот факт, что этот фильм воспроизводится бесконечно, пока не является проблемой, однако если этот объект будет использоваться для отображения, например, пункта меню, и вдобавок окажется не единственным, то число выполняемых действий может оказаться слишком большим. Для их одновременной реализации затрачивалось бы слишком много ресурсов компьютера, поэтому очень важно пресекать любые замедляющие работу действия.
Итак, по мере продолжения фильма, нужно проверять, находится ли он в своей конечной точке, и, если это так, завершать его. Казалось бы, легко определить, является ли позиция фильма x равной позиции фильма y, и т.д. На самом деле, в случае с данным замедлением скорости, фильм никогда не достигает своей конечной цели, а лишь бесконечно приближается к ней до тех пор, пока расстояние не станет пренебрежимо малым.
Если вернуться к исходному примеру объекта, двигающегося от 0 до 200, с разбиением каждого отрезка дистанции на две части, то для каждого кадра будут следующие позиции: 100, 150, 175, 187,5, 193,75, 196,875, 198,4375, 199,21875, 199,609375, 199,8046875. Это называется парадоксом Зено, который заключается в том, что если объект всегда перемещается на половину предыдущей дистанции, то он никогда не достигнет цели, все приближаясь и приближаясь к ней. Так как невозможно разделить на две части один пиксель, картинка будет казаться неподвижной, однако ActionScript этого не учитывает. Вместо того чтобы проверять, находится ли фильм в своем конечном положении, мы будем проверять, находится ли он рядом со своей целью, и меньше ли заданного расстояние между объектом и его целью. При подходе объекта к конечной точке можно остановить его, удалив управляющий элемент onEnterFrame.
Начав с нашего предыдущего кода, мы добавим новую функцию checkDistance для проверки того, является ли расстояние меньшим, нежели определенное число. Фильм затем перейдет в свое конечное положение, и функция, присвоенная onEnterFrame, будет удалена. Добавьте следующий код после уже имеющейся программы:
ball_mc.checkDistance = function() {
// check movieclip is within 0.2 pixels of target
if (Math.abs(this.targetX-this._x)<0.2
Math.abs(this.targetY-this._y)<0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
};
Прежде всего, у нас есть условное выражение, проверяющее, находится ли фильм внутри отрезка длиной 0,2 пикселя от цели. В коде this.targetX-this._x представлено расстояние от фильма до цели по оси X, и мы используем Math.abs для преобразования этого значения в положительное, так как безразлично, справа или слева находится фильм от конечной точки, т.е. между -0,2 и +0,2. Наконец, в функции moveToMouse мы добавляем вызов функции checkDistance:
ball_mc.moveToMouse = function() {
this._x += (this.targetX-this._x)/this._parent.speed;
this._y += (this.targetY-this._y)/this._parent.speed;
this.duplicate();
this.checkDistance();
};
Выполнение этого кода приведет к тому же результату, что и в случае с предыдущим файлом. Однако если выбрать команду Debug>List Variables в режиме Test Movie после окончания фильма, вы увидите, что дубликаты отсутствуют, и фильм ball_mc не имеет функции onEnterFrame. По большому счету, в нашем фильме ничего не происходит - он просто находится в ожидании щелчка мыши, чтобы запуститься вновь.
Давайте теперь перейдем к рассмотрению того, как можно создать постоянное, основанное на времени, движение.
Начнем с получения движения с постоянной скоростью. Укажем в фильме, что необходимо перейти из точки A в точку B через определенное количество кадров, перемещаясь с каждым кадром на одно и то же расстояние. Этот тип движения полезен, когда нужно, чтобы что-то происходило за определенный промежуток времени. В этом движении отсутствует та плавность, которую вы наблюдали в методе уменьшения скорости, однако здесь все гораздо проще: нам известно, что объект достигнет своей цели после определенного количества кадров. Данное движение выглядит более солидно.

Расстояние, на которое перемещается объект, одинаково во всех кадрах. Пройденное расстояние равно всему расстоянию, деленному на число кадров, в данном случае, 200 пикселей делится на 5 кадров и получается 40 пикселей на каждый кадр.
Вы увидите, что в каждом кадре преодолевается одно и то же расстояние. Для начала движения необходимо знать, насколько должен переместиться фильм, и в течение скольких кадров это должно произойти. Вычисление расстояния на кадр включает в себя лишь деление расстояния на общее число кадров.
onClipEvent (enterFrame).onMouseDown для ball_mc. Эта функция будет вычислять общее расстояние по осям x и y и затем делить это число на значение переменной кадров для выяснения расстояния, на которое будет перемещаться фильм в каждом кадре.frameNum, которая будет использоваться для определения того, на какое расстояние переместился фильм - мы будем прибавлять к ее значению единицу в каждом кадре фильма, и когда она будет равна значению переменной кадров, движение будет прекращаться.this.frames = 30;
ball_mc.onMouseDown = function() {
this.frameNum = 0;
// calculate how far to move each frame on the x and у axes
this.xStep = (this._parent._xmouse-this._x) /this._parent.frames ;
this.yStep = (this._parent._ymouse-this._y) /this._parent.frames;
this.onEnterFrame = this._moveToMouse;
};
moveToMouse, которая будет сначала добавлять значения xStep и yStep, добавлять дубликат и затем проверять, достиг ли он своей конечной точки:moveToMouse = function () {
// add the step value each frame
this._x += this.xStep;
this._y += this.yStep;
this.duplicate();
this.checkTime();
};
checkTime, прибавляющая единицу к значению переменной frameNum и затем прекращающая движение по достижении конечной точки, т.е. числа кадров, указанного нами в начале:ball_mc.checkTime = function() {
this.frameNum++;
if (this.frameNum == this._parent.frames) {
delete this.onEnterFrame;
}
};
Качество движения в этом примере сильно отличается от того, которое мы рассмотрим ниже. Давайте шагнем дальше и разберем ускорение и колебание в несложных движениях.
Этот тип движения по своему эффекту похож на бегущего человека, привязанного к эластичному тросу. Представьте, что человек бежит по направлению к точке, в которой закреплен резиновый канат, и, пробежав мимо точки закрепления, старается убежать от нее как можно дальше.
В каждом кадре мы умножаем переменную speed на результат деления для уменьшения скорости. На графике ниже показано, как продолжается движение после прохождения конечной точки, и последующее возвращение объекта.

Скорость объекта увеличивается в пропорции с расстоянием, на котором он находится от конечной точки. Из рисунка видно, что сначала он стремительно ускоряется, проходит конечную точку и затем возвращается обратно. Амплитуда его колебаний около цели медленно уменьшается.
Существует множество различных эффектов, которых можно достичь с помощью этого типа движения, так как здесь применяются два следующих параметра.
Окончательный эффект может быть в виде ожидаемого колебания вперед и назад около конечной точки и гораздо более плавного исчезновения, или эффекта резкой остановки.
ball_mc и отдельным слоем для кода. Мы также можем использовать некоторый код, который был применен ранее (функции, проверяющие расстояние, создающие следы и обрабатывающие событие mouseDown ), но он представляет собой лишь само движение, и нам необходимо изменить его.ball_mc.checkDistance = function() {
if (Math.abs(this.targetX-this._x)<0.2
Math.abs(this.targetY-this._y)<0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
};
ball_mc.duplicate = function() {
var dupe = this.duplicateMovieClip
("dupe"+this._parent.depth,
this._parent.depth++);
dupe.onEnterFrame = this._parent.diminish
};
function diminish(){
// reduce scale gradually
this._xscale = this._yscale-=2;
//remove movieclip when scale goes below zero
if (this._xscale <= 0) {
this.removeMovieClip();
}
}
ball_mc.onMouseDown = function() {
this.targetХ = this._parent._xmouse;
this.targetY = this._parent._ymouse;
this.onEnterFrame = this.moveToMouse;
};
moveToMouse, переменные скорости и т.д. Сначала необходимо определить два наших параметра, которые мы будем называть acceleration (ускорение) и friction (колебание). Установим для них значения 12 и 0,8, несмотря на то, что у нас будет возможность изменить их позднее для достижения различных эффектов. Добавьте эти две строки перед определением функции, в верхней части кода:var acceleration = 12; var friction = 0.8;
Итак, acceleration будет значением, на которое мы делим расстояние перед добавлением его к скорости нашего фильма, а friction будет являться значением, которое умножается на скорость каждого кадра по порядку для уменьшения быстроты движения.
Мы проработаем код движения по оси x перед добавлением его в функцию moveToMouse.
moveToMouse.var xdif=this.targetX-this._x;
xspeed (переменная, представляющая скорость фильма по оси x ):this.xspeed+=xdif/this._parent.acceleration;
speed на переменную friction для уменьшения скорости. Если не делать этого, наш фильм будет колебаться около своей конечной точки, не приближаясь к ней. Это умножение означает, что амплитуда уменьшается с каждым разом. Если убрать эту строку из исходного кода нашего окончательного результата, фильм не остановится на своей конечной точке, а будет двигаться вперед-назад на одно и то же расстояние.this.xspeed*=this._parent.friction;
this._x+=this.xspeed
Теперь мы объединяем все это в одну функцию moveToMouse, добавляя также вызовы дубликата и checkDistance. Ниже приведен полный код программы, как для оси x, так и для оси y:
ball_mc.moveToMouse = function() {
// calculate difference on x and у axes
var xdif = this.targetX-this._x;
var ydif = this.targetY-this._y;
// increment speed values
this.xspeed += xdif/this._parent.acceleration;
this.yspeed += ydif/this._parent.acceleration;
// dampen speed values
this.xspeed *= this._parent.friction;
this.yspeed *= this._parent.friction;
// add speed values to x and у properties
this._x += this.xspeed;
this._y += this.yspeed;
// create duplicate
this.duplicate();
// check if the movieclip has reached its target
this.checkDistance();
};
swing.fla и запустите его. Вы увидите эффект колебаний, причем объект будет вращаться почти по кругу вокруг конечной точки:
Изменение значений acceleration и friction сильно повлияют на качество движения. Поэкспериментируйте и попробуйте изменить их, чтобы наглядно понять, как эти параметры могут повлиять на движение.
Вообще говоря, чем ниже значение acceleration, тем быстрее будет сначала двигаться фильм, и чем ближе к единице значение friction, тем больше фильм будет колебаться вперед и назад. Некоторые значения могут сделать этот пример очень похожим на случай с уменьшением скорости, рассмотренный нами в первую очередь. Например, попробуйте ввести значения acceleration = 17 ; и friction = 0.4 ; и сохраните ваш фильм в файле swing02.fla. Разница в том, что у нас есть переменная, сохраняющаяся во время прохождения кадров, и фильм не изменяет моментально направление своего движения, как это было в случае с исходным уменьшением скорости движения, или движением по кадрам, и поэтому эти изменения происходят плавней.
Это все работает должным образом, однако есть еще одна деталь, которую нужно изменить. Принимая во внимание предыдущий пример с замедлением движения, становится понятно, что движение фильма должно было прекратиться сразу по прохождении определенного расстояния от конечной точки, в данном случае фильм, предположительно, прошел бы прямо над конечной точкой перед переходом на противоположную сторону. Это означает, что нам нужно изменить нашу функцию checkDistance, чтобы скорость фильма была также ниже определенного числа, чтобы по ошибке не происходило внезапного прекращения движения фильма.
Это более простой случай: все, что нам нужно, это вставить выражение if в нашу функцию для проверки того, является ли скорость достаточно малой после попадания расстояния в диапазон:
ball_mc.checkDistance = function() {
// check that distance is within range
if (Math.abs(this.targetX-this._x)<0.2
Math.abs (this. targetY-this ._y) < 0.2) {
// check that speed is nearly zero
if(Math.abs(this.xspeed)<0.2 Math.abs(this.yspeed) < 0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
}
};
Приведенный выше сценарий, содержащийся в swing3.fla, теперь защищен от любых непредвиденных обстоятельств.

Вы можете использовать эти основные принципы в различных ситуациях, контролируя скорость объекта разными способами. Сейчас на скорость влияет положение конечной точки (что-то похожее на магнетизм), однако вы, например, могли бы нажать кнопку правого курсора и добавить 5 к значению xspeed. Это переместит объект сильно вправо перед тем, как его скорость будет уменьшаться до полной остановки.
Если вам интересны способы создания других типов движения, рекомендуем вам обратить внимание на уравнения замедления на сайте www.robertpenner.com. Используя эти уравнения, вы можете указывать количество кадров, которое необходимо для определенной продолжительности движения, и фильм будет замедлять свое движение от точки A до точки B. В дополнение к этому, вы можете изучить применение этих уравнений на www.gizma.com/easing. Если вам интересны эти уравнения,
До этого момента мы рассматривали различные типы замедления применительно к движению по осям x и y, однако использование этого типа движения не ограничивается лишь только этими параметрами. Можно переходить от одного значения к другому и создавать движение, основанное на изменении размеров объектов.
В данном упражнении мы реализуем упругое движение, которое только что было нами рассмотрено. Мы будем определять координаты щелчка мыши и использовать их для определения размера, который будет принимать наш фильм.
mouseDown. Сложим координаты X и Y указателя мыши, затем умножим результат на два и используем его для определения конечного размера нашего фильма. Если пользователь щелкнет на левом верхнем углу, фильм примет размер, равный нулю, а если щелчок будет в правом нижнем углу, тогда размер станет равным 1900, т.е. (550+400)*2.ball_mc.onMouseDown=function(){
this.targetScale=(this._parent._xmouse+this._parent._ymouse)*2
this.onEnterFrame=this.scaleMe
}
Итак, мы взяли это значение и записали его в переменную targetScale фильма.
scaleMe, которая будет изменять размер фильма с текущего до конечного. Это делается с помощью достижения определенной точки на осях x и y, аналогичного рассмотренному ранее. Единственная разница заключается в том, что мы будем изменять параметры _xscale и _yscale вместо _x и _y. Так как мы хотим приравнять _xscale к _yscale, мы можем проводить все вычисления с использованием _xscale и затем установить _yscale на такое же значение:this.acceleration = 12;
this. friction = 0.8;
ball_mc.scaleMe = function() {
var scaleDiff = this.targetScale-this._xscale;
this.scaleSpeed += scaleDiff/this._parent.acceleration;
this.scaleSpeed *= this._parent.friction;
this._yscale = this._xscale += this.scaleSpeed;
this.checkScale();
};
Первые три строки функции такие же, как и ранее - они вычисляют разницу между текущим размером и конечным размером, и рассчитывают, на сколько увеличивается scaleSpeed. Затем переменная скорости уменьшается посредством умножения на константу friction.
В следующей строке мы производим два действия, эквивалентные следующим строкам.
this._xscale += this.scaleSpeed; this._yscale = this._xscale;
Когда Flash обнаруживает строку кода, аналогичную той, которую мы использовали в данном примере, он начинает выполнять действия справа налево. Сначала к переменной скорости прибавляется _xscale и после этого _yscale приравнивается к _xscale.
scale.fla и запустите его - теперь объект будет принимать различные размеры, в зависимости от того, на каком месте вы щелкните мышью.checkScale. Она более или менее похожа на функцию checkDistance, с которой мы имели дело ранее, и предназначена для проверки того, что размер фильма более или менее близок к конечному размеру, и что его скорость почти равна нулю:ball_mc.checkScale = function() {
if (Math.abs(this.targetScale-this._xscale)<0.2) {
if (Math.abs(this.scaleSpeed)<0.2) {
this._xscale = this._yscale=this.targetScale;
delete this.onEnterFrame;
}
}
};
Несмотря на то, что мы использовали этот подход лишь для изменения размера объекта, он может также применяться для изменения параметров _alpha, _rotation или любого другого свойства вашего фильма.
Теперь вы уже немного разбираетесь в том, как работают рассматриваемые разнообразные методы создания сценариев движения. Мы переходим к рассмотрению различных способов управления движением. Метод, который использовался до сих пор, подразумевает использование немалого количества различных рабочих мест, а мы настроили его на работу с одним фильмом.
Было бы гораздо лучше брать любой определенный фильм и создавать myMovieClip.slideTo(100,200), чтобы фильм мог перемещаться к этой точке и останавливаться, без надобности присваивать фильму вручную управляющий элемент clipEvent или функцию для проверки достижения конечной точки, и т.д.
Далее мы будем рассматривать различные методы создания сценариев начала движения объекта. Мы не будем указывать на тот или иной определенный способ и говорить, что он является самым лучшим, а вместо этого продемонстрируем вам несколько возможностей, чтобы вы могли выбирать из них нужное для работы с вашими проектами.
ball_mc.slideTo, которая будет выполняться каждый раз при щелчке мышью пользователем.slideTo три параметра: позицию по оси x, позицию по оси y и еще одно значение, которое будет указывать скорость скольжения:_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4);
};
slideTo, которая должна будет выполнять два отдельных действия:targetX и targetY ;enterFrame фильма для обеспечения его скольжения по направлению к конечной точке.Вместо того чтобы определять эту функцию для отдельного фильма, как мы делали в предыдущих функциях ball_mc.checkDistance, мы поместим ее в . Все функции внутри доступны в равной степени всем фильмам. Все встроенные методы фильмов, такие как goToAndPlay, также присутствуют в . При вызове функции она ведет себя так, как если бы она была внутри фильма.
MovieClip.prototype.slideTo = function(x, y, speed) {
// set up targets and speed variable
this.targetX = x;
this.targetY = y;
this.speed = speed;
// create onEnterFrame function
this.onEnterFrame = function() {
// take care of motion
this._x += (this.targetX-this._x)/this.speed;
this._y += (this.targetY-this._y)/this.speed;
// check if near target
if (Math.abs(this.targetX-this._x)<0.2
Math.abs(this.targetY-this._y)< 0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
};
};
Первые три строки функции берут параметры, которые ей передаются, и сохраняют их в переменных внутри фильма. Затем присваивается функция onEnterFrame, передвигающая фильм, а также выполняется проверка достижения конечной точки.
issueCommand1.fla и запустите его. При щелчке мышью фильм будет скользить к месту щелчка.ball_mc и дать новому инстансу имя ball_mc2 с помощью Property Inspector.ball_mc2 на рабочее место и затем откройте панель Actions для изменения кода onMouseDown, согласно тексту кода, приведенному ниже:_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4);
ball_mc2.slideTo(this._xmouse+19, this._ymouse, 1);
};
issueCommand1b.fla и запустите его. Вы заметите, что второй фильм перемещается значительно правее указателя мыши и со значительно меньшей скоростью.Итак, наш фильм работает как надо, однако при более детальном рассмотрении будут выявлены некоторые проблемы при использовании данного подхода. Наиболее заметный недочет заключается в том, что любой фильм может иметь только одну функцию, присвоенную его управляющему элементу onEnterFrame: если фильм уже предположительно имел функцию, присвоенную его управляющему элементу onEnterFrame, а мы присвоили ему другую функцию, то вторая функция будет записана поверх первой. Это может случиться, если к фильму было применено плавное исчезновение, увеличение размеров или по каким-либо другим причинам.
Другая проблема состоит в том, что мы помещаем в фильм переменные, такие как targetX, targetY и speed, которые могут уже использоваться в фильме с другими целями. Они будут просто записываться поверх старых, что соответствующим образом повлияет на функциональность фильма. Несмотря на то, что мы обычно помним имена, присваиваемые объектам в нашем ActionScript, полезно использовать информативные и не лишенные смысла названия.
Теперь рассмотрим все потенциально возможные проблемы, которые могут возникнуть при создании пустого фильма внутри уже существующего. Мы будем использовать внутренний фильм в качестве объекта, в котором находятся все переменные, и он будет также являться объектом, к которому можно присвоить управляющий элемент onEnterFrame, не опасаясь выхода из строя какой-либо предыдущей функциональности.
Управляющий элемент mouseDown настраиваем так же, как и прежде:
_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4);
};
Теперь наша функция slideTo будет выглядеть почти так же, как раньше, и основным отличием является то, что прежде фильм управлял сам собой, а теперь фильм выполняет внутри себя определенные действия. Это видно из замены 'this' на 'this._parent' при доступе или изменении параметров X и Y. Наш управляющий фильм осуществляет доступ и управление параметрами своего родительского фильма, т.е. того фильма, который необходимо перемещать.
MovieClip.prototype.slideTo = function(x, y, speed) {
// create controller movieclip
var control_mc = this.createEmptyMоvieClip ("slideControl",this.depth++);
control_mc.targetX = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.onEnterFrame = function() {
// this._parent is the movieclip we're moving
this._parent._x += (this.targetX-this._parent._x) /this.speed;
this._parent._y += (this.targetY-this._parent._y)/this.speed;
if (Math.abs(this.targetX-this._parent._x)<0.2
Math.abs(this.targetY-this._parent._y) <0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.removeMovieClip();
}
};
};
Как и в отношении функции duplicate, созданной нами ранее, мы использовали ссылку, возвращаемую функцией createEmptyMovieClip для обращения к вновь созданному . Одной из возможных "ловушек" при использовании этого метода с createEmptyMovieClip является то, что пользователь может записать фильм поверх уже имеющегося внутри. Проще говоря, это то, чего нужно остерегаться при управлении фильмом; необходимо иметь некий механизм определения того, на какой глубине находятся объекты и символы. Посредством ввода переменной глубины для каждой временной линии можно эффективно реализовать данную меру предосторожности, применяя этот способ каждый раз при создании фильма. С другой стороны, можно было бы объявить глобальную переменную глубины для всех временных линий, однако все же лучше иметь одну переменную для каждой временной линии, чтобы не терять контроль над значениями. Оставляем право выбора за вами.
Вы могли бы использовать addProperty для автоматического добавления глубины каждый раз при попытке доступа, однако пришлось бы делать это для каждого фильма по отдельности, поэтому статическая переменная, увеличивающаяся на единицу с каждым разом, будет оптимальным решением. Другим возможным подходом может быть цикловое прохождение фильмов целиком в отдельном месте, и использование getDepth() в каждом из них для выяснения глубин, однако это довольно нерациональный способ, так как здесь придется делать большое количество ненужных запросов на действия.
Сохраните ваш фильм в файле issueCommand2.fla. Если вы запустите фильм на данном этапе, вы заметите, что движение будет прекращено при быстром последовательном щелчке мышью. Если выполнить команду Debug > List Variables, станет видно, что причиной этого является присутствие более чем одного инстанса slideControl в нашем фильме - все они работают с различными направлениями. Таким образом, нам нужно придумать что-то такое, с помощью чего можно будет убеждаться в присутствии в любой момент времени лишь одного инстанса этого управляющего клипа внутри нашего фильма.
Возможным решением этой проблемы является создание дубликатов slideControl каждый раз на одной и той же глубине, чтобы прежний инстанс автоматически перезаписывался новым. Однако лучше не использовать этот подход, так как он нарушает схему глубины, о которой мы только что говорили. В дополнение к этому, если мы использовали метод колебаний для обеспечения движения нашего фильма, нам совершенно не нужно будет перезаписывать любые переменные скорости внутри фильма и прерывать его движение; нам необходимо сохранение этих данных до остановки фильма.
Более разумным решением будет проверка того, имеется ли уже фильм slideControl. Если это не так, то вам нужно будет создавать его, а если он уже присутствует, то нужно будет перезаписывать переменные X и Y конечной точки и событие onEnterFrame.
Оба подхода приводят к одному и тому же результату.
MovieClip.prototype.slideTo = function(x, y, speed) {
var control_mc
if (this.slideControl) {
//if slideControl already exists
control_mc = this.slideControl;
}
else {
//if slideControl doesn't exist then create it
control_mc = this.createEmptyMovieClip
("slideControl", this.depth++);
}
control_mc.targetX = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.onEnterFrame = function() {
this._parent._x += (this.targetX-this._parent._x)/this.speed;
this._parent._y += (this.targetY-this._parent._y)/this.speed;
if (Math.abs(this.targetX-this.__parent._x)<0.2
Math.abs(this.targetY-this._parent._y)<0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.removeMovieClip();
}
};
};
Сохраните файл под именем i ssueCommand3.fla и при запуске фильма обратите внимание на то, что теперь он ведет себя нужным образом.
Несмотря на положительный результат, данный подход несостоятелен с практической точки зрения, так как мы не можем определить момент, когда фильм достигает своей конечной позиции. Движение на самом деле прекращается, но нам часто будет требоваться выполнить некоторое другое действие по окончании движения. По крайней мере, было бы неплохо знать, когда фильм находится в движении, а когда нет.
Для решения этой проблемы обратимся к функции с обратной связью. Все будет работать таким же образом, как и некоторые другие компоненты - они будут вызывать функцию при изменении значения или обновлении каких-либо данных. При прекращении движения будет вызываться указанная нами функция. Итак, прежде всего, мы создадим функцию, которая будет вызываться при окончании движения фильма. В момент, когда это потребуется, можно выводить сообщение в окне Output с указанием имени фильма, завершившего свое движение, которое мы будем использовать в качестве параметра.
function slideDone(mс) {
trace("movieClip "+mc+" has finished moving");
}
Вообще говоря, когда движение фильма будет прекращаться, будет осуществляться вызов функции slideDone в _root. При вызове функции slideTo, нам нужно будет добавить два других параметра - callBackObj и callBackFunc. callBackObj является местоположением вызываемой функции (в данном случае, _root ), а callBackFunc - именем вызываемой функции, т.е., в нашем случае, функции slideDone. Итак, сначала нужно добавить в нашу функцию два дополнительных параметра:
_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4, _root, "slideDone");
};
Затем нам потребуется настроить функцию slideTo для работы с этими двумя параметрами и для хранения их в фильме slideControl (посредством добавления кода):
MovieClip.prototype.slideTo =
function(x, y, speed, callbackObj, callbackFunc) {
var control_mc
if (this.slideControl) {
control_mc = this.slideControl;
}
else {
control_mc =
this.createEmptyMovieClip("slideControl", this.depth++);
}
control_mc.targetX = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.callBackObj = callBackObj;
control_mc.callBackFunc = callBackFunc;
};
Наконец, нам нужно выяснить, как вызывать _root.slideDone с использованием двух переменных callBackObj и callBackFunc. Это на самом деле очень просто и делается практически таким же образом, как осуществляется, например, доступ к значению в массиве, или динамическое создание ссылки на фильм:
this.callBackObj [this.callBackFunk] (this._parent)
Часть кода перед квадратными скобками является местоположением функции ( _root ). Содержимое в квадратных скобках осуществляет поиск значения, отвечающего данной строке (в нашем случае, slideDone ), и затем значение в скобках передается в виде параметра. Передаваемое здесь значение является обращением к фильму, который мы перемещали, т.е. this._parent, our ball_mc. Наша функция целиком выглядит примерно так:
MovieClip.prototype.slideTo =
function (x, y, speed, callbackObj, callbackFunc) {
var control_mc
if (this.slideControl) {
control_mc = this.slideControl;
}
else {
control_mc = this.createEmptyMovieClip
("slideControl", this.depth++);
}
control_mc.targetХ = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.callBackObj = callBackObj;
control_mc.callBackFunc = callBackFunc;
control_mc.onEnterFrame = function() {
this._parent._x += (this.targetX-this._parent._x) /this.speed;
this._parent._y += (this.targetY-this._parent._y) /this.speed;
if (Math.abs(this.targetX-this._parent._x)<0.2
Math. abs(this.targetY-this._parent._y)<0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.callBackObj[this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
Сохраните ваш фильм в файле issueCommand004.fla и при запуске фильма вы увидите, что в окне Output отобразится сообщение "movieClip_level0.mc has finished moving", т.е. сообщение о завершении движения фильма. Это будет происходить каждый раз при остановке фильма.
Вы можете изменить содержимое функции slideDone для осуществления любых нужных вам действий. Например, у вас может быть массив, содержащий данные о том, какие фильмы запущены, и вы можете использовать его для обеспечения начала выполнения следующей стадии проекта только по завершении продолжения фильма.
Функция callback позволяет нам использовать очень важную возможность обратной связи. Мы можем инициировать начало движения фильма к определенному месту и оставить этот процесс на самостоятельное завершение, так как по окончании движения от него поступит соответствующее сообщение. Нашим следующим шагом будет настройка функции, которая сможет согласоваться с любыми нужными нам параметрами, измененными в фильме, после чего фильм будет изменять все эти параметры одновременно. Мы вернемся к этой теме в одной из следующих лекций.
Сейчас настало время вновь обратиться к некоторым из уже рассмотренных подходов. То, о чем мы говорили ранее, может показаться однообразным, однако сейчас мы приведем несложный пример, использующий некоторые из изученных нами способов. В качестве примера создадим страницу Flash.
Мы планируем создать набор из нескольких фотографий так, что при щелчке на кнопке фотографии будут перемещаться под маской и двигаться таким образом, что в конечной области появится нужная фотография.


Итак, теперь у нас на рабочем месте есть инстанс в корневой временной диаграмме. Мы добавим движение, переместив фильм под маску так, чтобы нужный рисунок оказался на нужной позиции.
holder , используйте Property Inspector, чтобы задать ему имя инстанса holder и указать его значения X и Y, установив их на 0,0. После этого точка закрепления будет размещена в левом верхнем углу рабочего места. Наконец, назовите текущий слой 'pictures'.pictures и пометьте его как маску.X и Y, равные 180,89.scripts и в новом слое откройте панель Actions, чтобы добавить следующий код:holder.setMask (maska);
Посредством этого выражения фильм внутри скобок (maska) становится маской для фильма, который был вызван методом ( (набора из девяти рисунков), которые просвечиваются, а остальные будут затемнены.
background1 нарисуйте светло-серый прямоугольник 202x202 пикселя и с помощью Property Inspector задайте его значения X и Y, равные 179,88. Теперь на слое background2 нарисуйте другой серый прямоугольник более темным цветом и размером 204x244, задав для него значения X и Y, равные 178,87. Это будет границей наших фильмов.Вы можете добавить любые другие нужные вам изображения на фоновые слои.
holder , мы будем использовать функцию slide, созданную ранее. Добавьте следующий код в слой scripts над только что вставленной вами строкой кода:speed = 4;
MovieClip.prototype.slideTo =
function(x, y, speed, callbackObj, callbackFunc) {
Var mc_control
if (this.slideControl) {
mc_control = this.slideControl;
}
else {
mc_control = this.createEmptyMovieClip
("slideControl" , this. depth++) ;
}
mc_control.targetX = x;
mc_control.targetY = y;
mc_control.speed = speed;
mc_control.callBackObj = callBackObj;
mc_control.callBackFunc = callBackFunc;
mc_control. onEnterFrame = function() {
this._parent._x += (this.targetX-this._parent._x) /this.speed;
this._parent._y += (this.targetY-this._parent._y) /this.speed;
if (Math.abs(this.targetX-this._parent._x)<0.2
Math.abs(this.targetY-this._parent._y)<0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.callBackObj[this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
function slideDone(mc) {
trace("movieClip "+mc+" has finished moving");
}
Для выполнения этого кода мы создадим несколько кнопок. Вместо того чтобы динамически создавать кнопки с использованием createEmptyMovieClip и createTextField (эти подходы мы рассмотрим позднее), мы создадим символ вручную и затем динамически применим его.
butt. Выделите фильм в Library и щелкните на белом значке меню вверху панели заголовка Library. В меню выберите параметр Linkage:, чтобы открыть окно Linkage Properties.butt. Разместите поле Dynamic Text внутри фильма и дайте ему имя инстанса tf с помощью Property Inspector. Размер текста нашего поля равен 9, шрифт - Arial, без встроенных шрифтов.scripts нашей корневой временной диаграммы (под уже имеющимся кодом).holder.setMask(maska);
for (var i = 1; i<=9; i++) {
var mc_control = _root.attachMovie("butt", "butt"+i, i);
mc_control._x = 175+i*9;
mc_control._y = 375;
mc_control.mc = this.holder["c"+i] ;
mc_control.tf.text = i;
}
Этот код циклически выполняется для каждого фильма с фотографией и добавляет кнопку. В нем используется итератор 'i' для установки текстового значения поля 'tf', поэтому в поле появятся числа от 1 до 9. В дополнение к этому, используется значение 'i' для установки конечного фильма для каждой кнопки (несмотря на то, что butt является фильмом, этот объект будет кнопкой, так как мы будем присваивать ему событие onPress ). Число 9 в конце четвертой строки кода контролирует расстояние между всеми фильмами-кнопками.
Перед присвоением события onPress нам нужно решить, какое именно действие будет выполняться при нажатии кнопки, так же как и место, в которое должен переместиться для корректного отображения каждой фотографии под маской.
Все это можно сделать, рассмотрев конкретный случай. Например, если бы у нас был фильм, расположенный в точке 300,300 в фильме , где должен был бы быть расположен , чтобы правильно отображать фотографии под маской? Если бы мы расположили на том же месте рабочего стола, что и маску, то отображалось бы все, что было бы на позиции 0,0 в . Основываясь на этом, мы можем сделать вывод, что если бы нам нужно было отображать объект на позиции 300,300 в
(maska._x-300, maska._y-300);
Теперь, если мы обратимся к координатам _x и _y любого из наших фильмов с изображениями, то получим координаты фильма внутри . Следовательно, можно сказать, что для того, чтобы каждый фильм был расположен строго под маской, нам необходимо перемещать на следующее место:
(maska._x-mc._x, maska._y-mc._y);
onPress для наших кнопок:for (var 1 = 1; i<=9; i++) {
var mc_control = _root.attachMovie("butt" , "butt"+i, i);
mc_control._x = 175+i*9;
mc_control._y = 295;
mc_control.mc = this.holder["c"+i] ;
mc_control.tf.text = i;
mc_control.onPress = function() {
var x = this._parent.maska._x-this.mc._x;
var у = this._parent.maska._y-this.mc._y;
this._parent.holder.slideTo(x, y, this._parent.speed, this._parent, "slideDone") ;
};
}
Вызов функции slideTo происходит таким же образом, как и ранее, с использованием показанного ранее вычисления для указания координат X и Y конечной точки скольжения. Hам необязательно записывать значения X и Y в переменные, поэтому мы можем убрать из кода соответствующую его часть и выполнять вычисления внутри вызова функции.
for (var i = 1; i<=9; i++) {
var mc_control = _root.attachMovie("butt", "butt"+i, i);
mc_control._x = 175+i*9;
mc_control._y = 295;
mc_control.mc = this.holder["c"+i] ;
mc_control.tf .text = i;
mc_control.onPress = function() {
this._parent.holder.slideTo(this._parent.maska._x-this.mc._x, this._parent.maska._y-this.mc._y,
this._parent.speed, this._parent, "slideDone");
};
}
Сохраните фильм в файле pictureMask.fla. При его запуске вы увидите, что при скольжении каждого рисунка на его позицию происходит регистрация происходящего в окне Output. Сейчас для нас это не имеет значения, однако позже мы разберемся, как использовать эту возможность для вызова какого-либо события, например, текстового эффекта. Это лишь однa из возможных применений этого окна. Однако этот вид управления также может быть использован для вывода некоторого пояснительного текста, так, чтобы рисунки являлись фоном интерфейса, или для запуска анимации. Необходимо заметить, что мы управляем движением, которое сами же и инициировали. Мы знаем, когда оно начинается, и можем быть оповещены о его завершении, а это означает, что оно может стать частью цепи событий.
Одной из основных особенностей Flash является возможность движения объектов; двигающиеся объекты можно добавлять ко всему, что создается во Flash. В этой лекции мы рассмотрим некоторые из способов создания во Flash MX различных типов движения.
Некоторым читателям эти принципы уже могут быть знакомы, однако мы также будем изучать новые возможности Flash MX, позволяющие обеспечивать движение наиболее простыми и эффективными методами, нежели это было прежде. Чтобы продемонстрировать гибкость Flash, рассмотрим несколько различных типов движения, которое вы, возможно, захотели бы добавить в свои проекты, а также способы его применения. Иногда мы будем использовать ActionScript для получения следующих типов движения:
Сначала рассмотрим, как можно использовать эти типы движения для перемещения объектов со следом от движения по осям X и Y. Также обратим внимание на то, как применить это движение к другим параметрам, таким как размер объекта.
Помимо самого движения, мы рассмотрим различные способы применения движения с помощью ActionScript: как вызывать движение щелчком мыши и как лучше поддерживать движение, т.е. как начать его и как завершить.
Наконец, остановимся на учебном примере, содержащем несколько картинок, появляющихся в области просмотра при щелчке на кнопке. Подходы, описанные в упражнении, включают в себя создание маски с помощью ActionScript, а также динамическое применение кнопки.
Итак, шутки в сторону! Начнем с рассмотрения покадрового движения.
Первым типом движения, о котором мы бы хотели рассказать, является уменьшение скорости движения: объект сначала движется довольно быстро и постепенно замедляется, пока, наконец, не останавливается, достигнув цели. Этот тип движения, вероятно, наиболее прост в реализации - нужно выяснить, как далеко находится объект от цели и затем переместить объект к цели, разделив дистанцию на части. Итак, если объект находится на расстоянии 100 пикселей от конечной точки, мы можем переместить его на половину этого расстояния, после чего он будет в 50 пикселях от цели, далее - в 25, затем в 12,5 и так далее. Движение при этом будет плавным; ведь мы привыкли наблюдать постепенное замедление объектов, а не прерывистые движения.
На рисунке ниже данное движение показано в действии, причем каждая линия представляет собой кадр перемещения объекта на 200 пикселей.

С каждым кадром расстояние объекта до цели сокращается наполовину. В кадре 1 объект перемещается на 100 пикселей, в кадре 2 - на 50 пикселей, в кадре 3 - на 25 пикселей, затем на 12,5 пикселей, затем на 6,25, затем на 3,125 и так далее.
Рассмотрим практическое применение этого движения, создав объект, следующий за мышью. Ничего нового и интересного в этом нет, но наша задача - разобраться в принципе движения.
Мы используем суффикс ' _mc ' в имени согласно установленным Macromedia преобразованиям имен. Использование этого правила имеет два преимущества: во-первых, это помогает отличать разные типы объектов в коде, а во-вторых, таким образом обеспечивается появление подсказки в коде нужного типа при вводе кода в среде разработки.
Мы проделали основные действия. Теперь углубимся в процесс работы.
onClipEvent (enterFrame).Благодаря новой объектной модели, во Flash MX этого можно больше не делать. Мы можем динамически прикреплять события clipEvents к фильмам, что позволит всему или почти всему коду находиться в одном месте, а также позволит манипулировать этими событиями во время выполнения, например, включать и выключать их или изменять какую-либо функциональность.
Это достигается посредством присвоения функции управляющему элементу фильма onEnterFrame. Если фильм имеет функцию, присвоенную к его управляющему элементу onEnterFrame, то он каждый раз будет выполнять эту функцию.
Введите следующий код в кадр 1 слоя script:
ball_mc.onEnterFrame = function() {
this._x += (this._parent._xmouse-this._x) /4 ;
this._y += (this._parent._ymouse-this._y) /4;
};
Этот код сначала создает функцию, которая присваивается управляющему элементу onEnterFrame фильма 'ball_mc'. Код этой функции сначала выясняет, насколько далеко находится объект от указателя мыши, и затем добавляет четверть этого расстояния до позиции фильма. Если вам нужно было бы написать этот код, так сказать, своими словами, то получилось бы следующее: "вычислить разницу между позициями указателя по осям X и Y, затем увеличить позицию X на четверть этого значения".ball motion_01.fla и протестируйте его.Вы увидите, что объект следует за указателем мыши, так же как мы показывали на исходной диаграмме: сначала объект движется быстро, а затем его скорость уменьшается. На этом этапе имеет смысл попробовать применить другие числа вместо 4, чтобы увидеть различные результирующие особенности движения. Если вы собираетесь работать с этим типом движения, вы должны знать, как повлиять на него. Но, прежде всего, вам стоит убрать число из функции и сделать его переменной, которая позволит применять различные числа немного быстрее.
Чтобы было легче менять скорость движения объекта в вашем фильме, добавьте переменную скорости (чем меньше будет значение, тем быстрее будет двигаться объект).
script:var speed = 4;
ball_mc.onEnterFrame = function() {
this._x += (this._parent._xmouse-this._x)/speed;
this._y += (this._parent._ymouse-this._y)/speed;
};
Обратите внимание на то, что внутри функции onEnterFrame слово 'this' ссылается на фильм, которые вы назвали ball_mc, поэтому даже при обращении к переменной скорости из функции нужно вводить this._parent.speed, чтобы оказаться на том уровне, на котором находится фильм. Уровень _root level является уровнем, на котором определена переменная speed.
ball motion_02_speed var.fla и запустите его. Движение происходит таким же образом, однако теперь легче внести изменение в код.Теперь мы значительно изменим код, чтобы фильм перемещался в любое место, где пользователь щелкнет мышью. Аналогично тому, как мы присваивали функцию onEnterFrame фильму, мы можем присвоить ему функцию onMouseDown. Эта функция будет выполняться при каждом нажатии кнопки мыши в любом месте на главном фильме.
Итак, когда пользователь щелкает мышью, нужно сначала установить позиции целей фильма на местоположение щелчка, а затем выполнить функцию onEnterFrame для перемещения на эту позицию:
script:var speed = 4;
// set up target variables
ball_mc.targetX = 0
ball_mc.targetY = 0
ball_mc.moveToMouse = function() {
this._x += (targetX-this._x)/speed;
this._y += (targetY-this._y)/speed;
};
triggerMotion = function() {
// reset target variables on click
targetX = _xmouse;
targetY = _ymouse;
// set the movieclip in motion
this.onEnterFrame = this.moveToMouse;
}
ball_mc.onMouseDown = triggerMotion;
Можно видеть, что этот ActionScript кардинальным образом отличается от предыдущего ActionScript. Если раньше мы присоединяли функцию непосредственно к управляющему элементу onEnterFrame, то на этот раз мы сначала определяем функцию moveToMouse, и при щелчке мышью выражение this.onEnterFrame указывает на эту функцию и вызывает ее в каждом кадре. Функция сама по себе работает так же, как и прежде, за исключением того, что перемещение происходит не на позицию указателя мыши, а на значения переменных targetX и targetY, являющихся переменными местоположения указателя мыши, значения которых определяются при каждом щелчке мышью пользователем.
Этот подход выглядит более эффективным, так как требуется лишь один раз определить функцию, а затем активизировать ее, указав для нее onEnterFrame. Если впоследствии нужно будет выключить движение, это можно сделать, удалив this.onEnterFrame.
ball motion_03_click to move.fla и запустите его.Вы увидите, что объект перемещается на то место, где вы щелкнули мышью, и затем, дойдя до этого места, прекращает свое движение, что является хорошим примером сопоставления с тем случаем, когда шарик постоянно следовал за мышью.
Одним из дополнений, которое обеспечит четкость движения объекта, является его след, оставляемый на тех местах, где только что находился фильм. В нашем случае это полезно для понимания того, как работает замедление движения, однако такие следы могут применяться также и в других обстоятельствах: или как движущийся значок во время загрузки чего-либо, или же в виде своего рода размытости объекта, вызванной его движением. Такие следы могут быть легко созданы посредством дубликации фильма в каждом кадре с использованием функции duplicateMovieClip. Для этого нужно лишь добавить строку кода, увеличивающую значение _root и затем создающую дубликат текущего фильма.
Дубликат находится по умолчанию на той же позиции, что и исходный фильм. На самом деле, дубликат имеет все те же параметры, что и исходный фильм, такие как alpha, xscale, yscale и т.д., однако у него нет функций onEnterFrame или переменных, которые может содержать исходный фильм. Это одно из отличий от Flash 5, в котором если создавался дубликат фильма с присвоенным событием clipEvent, также создавался дубликат этого события, которое происходило и на дубликате фильма. Мы располагаем каждый дубликат на один уровень выше предыдущего, посредством добавления переменной глубины для '_root', значение которой увеличивается с каждым кадром.
DuplicateMovieClip можно интерпретировать, как производитель дубликатов, внешне идентичных исходным фильмам, в которых, однако, не повторяются никакие из исходных действий. В нашем случае, дубликаты не следуют за указателем мыши, а просто остаются там, где они были созданы.
var speed = 4;
ball_mc.moveToMouse = function() {
this._x += (this.targetX-this._x)/this._parent.speed;
this._y += (this.targetY-this._y)/this._parent.speed;
this.duplicateMovieClip("dupe"+this._parent.depth,
this._parent.depth++);
};
ball_mc.onMouseDown = function() {
this.targetX = this._parent._xmouse;
this.targetY = this._parent._ymouse;
this.onEnterFrame = this.moveToMouse;
};
При щелчке на новом месте дубликаты сначала создаются довольно далеко друг от друга, и затем становятся все ближе и ближе друг к другу до тех пор, пока не превратятся в монотонную картину у конечной точки. Однако число объектов здесь растет, пока фильм выполняется, и, чтобы избежать "мешанины", нужно постепенно уменьшать каждый дубликат до его полного исчезновения. В следующем примере мы реализуем именно это дополнение.
Чтобы не забивать фильм слишком большим количеством дубликатов одного и того же объекта, мы можем присвоить каждому дубликату свою собственную функцию enterFrame, которая будет обеспечивать уменьшение дубликатов с каждым кадром и затем удалять их по достижении определенного размера.
Так как мы собираемся работать с каждым из дубликатов, имеет смысл присвоить объекту ball_mc его собственную функцию duplicate(). Приведем код с выделенными вводимыми новыми частями:
var speed = 4;
ball_mc.moveToMouse = function() {
this._x += (this.targetX-this._x)/this._parent.speed;
this._y += (this.targetY-this._y)/this._parent.speed;
this.duplicate() ;
};
ball_mc.duplicate = function() {
var dupe = this.duplicateMovieClip("dupe"+this._parent.depth,
this._parent.depth++);
dupe.onEnterFrame = this._parent.diminish
};
ball_mc.onMouseDown = function() {
this.targetX = this._parent._xmouse;
this.targetY = this._parent._ymouse;
this.onEnterFrame = this.moveToMouse;
};
function diminish(){
// reduce scale gradually
this._xscale = this._yscale-=2;
//remove movieclip when scale goes below zero
if (this._xscale <= 0) {
this.removeMovieClip();
}
}
Итак, мы добавили вызов функции duplicate() и значительно изменили вызов duplicateMovieClip, вынеся вперед var dupe=. Несмотря на то, что в словаре ActionScript указано, что duplicateMovieClip не возвращает никакого значения, на самом деле, возвращается ссылка на созданный фильм.
Здесь мы брали это значение и помещали его в переменную dupe. Мы объявили переменную с помощью выражения var так, что это значение существует только во время выполнения функции, и по окончании ее выполнения оно исчезает за ненадобностью.
Теперь мы можем использовать dupe для ссылки на наш новый дубликат фильма и для присвоения ему функции onEnterFrame, функции diminish, определенной в _root. В функции diminish мы уменьшаем инкременты xscale и yscale с каждым кадром, и когда они достигнут нуля, фильм удаляется.
Если запустить фильм сейчас, будет намного меньше ненужных фильмов, так как каждый из клипов выполняет те действия, которые ему предписаны.
Теперь все работает четко, и количество фильмов лежит в пределах разумного. Однако вы, наверное, заметили, что фильм продолжает идти и создавать дубликаты самого себя даже по достижении конечного пункта. Мы не можем наблюдать движение, так как оно очень незначительно, и нельзя различить дубликаты, так как они находятся на той же позиции, что и исходный объект. Если нужно убедиться в этом, просмотрите переменные или объекты во время просмотра фильма. Нам же нужно выяснить, когда фильм достигнет конца, и затем выключить его. Тот факт, что этот фильм воспроизводится бесконечно, пока не является проблемой, однако если этот объект будет использоваться для отображения, например, пункта меню, и вдобавок окажется не единственным, то число выполняемых действий может оказаться слишком большим. Для их одновременной реализации затрачивалось бы слишком много ресурсов компьютера, поэтому очень важно пресекать любые замедляющие работу действия.
Итак, по мере продолжения фильма, нужно проверять, находится ли он в своей конечной точке, и, если это так, завершать его. Казалось бы, легко определить, является ли позиция фильма x равной позиции фильма y, и т.д. На самом деле, в случае с данным замедлением скорости, фильм никогда не достигает своей конечной цели, а лишь бесконечно приближается к ней до тех пор, пока расстояние не станет пренебрежимо малым.
Если вернуться к исходному примеру объекта, двигающегося от 0 до 200, с разбиением каждого отрезка дистанции на две части, то для каждого кадра будут следующие позиции: 100, 150, 175, 187,5, 193,75, 196,875, 198,4375, 199,21875, 199,609375, 199,8046875. Это называется парадоксом Зено, который заключается в том, что если объект всегда перемещается на половину предыдущей дистанции, то он никогда не достигнет цели, все приближаясь и приближаясь к ней. Так как невозможно разделить на две части один пиксель, картинка будет казаться неподвижной, однако ActionScript этого не учитывает. Вместо того чтобы проверять, находится ли фильм в своем конечном положении, мы будем проверять, находится ли он рядом со своей целью, и меньше ли заданного расстояние между объектом и его целью. При подходе объекта к конечной точке можно остановить его, удалив управляющий элемент onEnterFrame.
Начав с нашего предыдущего кода, мы добавим новую функцию checkDistance для проверки того, является ли расстояние меньшим, нежели определенное число. Фильм затем перейдет в свое конечное положение, и функция, присвоенная onEnterFrame, будет удалена. Добавьте следующий код после уже имеющейся программы:
ball_mc.checkDistance = function() {
// check movieclip is within 0.2 pixels of target
if (Math.abs(this.targetX-this._x)<0.2
Math.abs(this.targetY-this._y)<0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
};
Прежде всего, у нас есть условное выражение, проверяющее, находится ли фильм внутри отрезка длиной 0,2 пикселя от цели. В коде this.targetX-this._x представлено расстояние от фильма до цели по оси X, и мы используем Math.abs для преобразования этого значения в положительное, так как безразлично, справа или слева находится фильм от конечной точки, т.е. между -0,2 и +0,2. Наконец, в функции moveToMouse мы добавляем вызов функции checkDistance:
ball_mc.moveToMouse = function() {
this._x += (this.targetX-this._x)/this._parent.speed;
this._y += (this.targetY-this._y)/this._parent.speed;
this.duplicate();
this.checkDistance();
};
Выполнение этого кода приведет к тому же результату, что и в случае с предыдущим файлом. Однако если выбрать команду Debug>List Variables в режиме Test Movie после окончания фильма, вы увидите, что дубликаты отсутствуют, и фильм ball_mc не имеет функции onEnterFrame. По большому счету, в нашем фильме ничего не происходит - он просто находится в ожидании щелчка мыши, чтобы запуститься вновь.
Давайте теперь перейдем к рассмотрению того, как можно создать постоянное, основанное на времени, движение.
Начнем с получения движения с постоянной скоростью. Укажем в фильме, что необходимо перейти из точки A в точку B через определенное количество кадров, перемещаясь с каждым кадром на одно и то же расстояние. Этот тип движения полезен, когда нужно, чтобы что-то происходило за определенный промежуток времени. В этом движении отсутствует та плавность, которую вы наблюдали в методе уменьшения скорости, однако здесь все гораздо проще: нам известно, что объект достигнет своей цели после определенного количества кадров. Данное движение выглядит более солидно.

Расстояние, на которое перемещается объект, одинаково во всех кадрах. Пройденное расстояние равно всему расстоянию, деленному на число кадров, в данном случае, 200 пикселей делится на 5 кадров и получается 40 пикселей на каждый кадр.
Вы увидите, что в каждом кадре преодолевается одно и то же расстояние. Для начала движения необходимо знать, насколько должен переместиться фильм, и в течение скольких кадров это должно произойти. Вычисление расстояния на кадр включает в себя лишь деление расстояния на общее число кадров.
onClipEvent (enterFrame).onMouseDown для ball_mc. Эта функция будет вычислять общее расстояние по осям x и y и затем делить это число на значение переменной кадров для выяснения расстояния, на которое будет перемещаться фильм в каждом кадре.frameNum, которая будет использоваться для определения того, на какое расстояние переместился фильм - мы будем прибавлять к ее значению единицу в каждом кадре фильма, и когда она будет равна значению переменной кадров, движение будет прекращаться.this.frames = 30;
ball_mc.onMouseDown = function() {
this.frameNum = 0;
// calculate how far to move each frame on the x and у axes
this.xStep = (this._parent._xmouse-this._x) /this._parent.frames ;
this.yStep = (this._parent._ymouse-this._y) /this._parent.frames;
this.onEnterFrame = this._moveToMouse;
};
moveToMouse, которая будет сначала добавлять значения xStep и yStep, добавлять дубликат и затем проверять, достиг ли он своей конечной точки:moveToMouse = function () {
// add the step value each frame
this._x += this.xStep;
this._y += this.yStep;
this.duplicate();
this.checkTime();
};
checkTime, прибавляющая единицу к значению переменной frameNum и затем прекращающая движение по достижении конечной точки, т.е. числа кадров, указанного нами в начале:ball_mc.checkTime = function() {
this.frameNum++;
if (this.frameNum == this._parent.frames) {
delete this.onEnterFrame;
}
};
Качество движения в этом примере сильно отличается от того, которое мы рассмотрим ниже. Давайте шагнем дальше и разберем ускорение и колебание в несложных движениях.
Этот тип движения по своему эффекту похож на бегущего человека, привязанного к эластичному тросу. Представьте, что человек бежит по направлению к точке, в которой закреплен резиновый канат, и, пробежав мимо точки закрепления, старается убежать от нее как можно дальше.
В каждом кадре мы умножаем переменную speed на результат деления для уменьшения скорости. На графике ниже показано, как продолжается движение после прохождения конечной точки, и последующее возвращение объекта.

Скорость объекта увеличивается в пропорции с расстоянием, на котором он находится от конечной точки. Из рисунка видно, что сначала он стремительно ускоряется, проходит конечную точку и затем возвращается обратно. Амплитуда его колебаний около цели медленно уменьшается.
Существует множество различных эффектов, которых можно достичь с помощью этого типа движения, так как здесь применяются два следующих параметра.
Окончательный эффект может быть в виде ожидаемого колебания вперед и назад около конечной точки и гораздо более плавного исчезновения, или эффекта резкой остановки.
ball_mc и отдельным слоем для кода. Мы также можем использовать некоторый код, который был применен ранее (функции, проверяющие расстояние, создающие следы и обрабатывающие событие mouseDown ), но он представляет собой лишь само движение, и нам необходимо изменить его.ball_mc.checkDistance = function() {
if (Math.abs(this.targetX-this._x)<0.2
Math.abs(this.targetY-this._y)<0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
};
ball_mc.duplicate = function() {
var dupe = this.duplicateMovieClip
("dupe"+this._parent.depth,
this._parent.depth++);
dupe.onEnterFrame = this._parent.diminish
};
function diminish(){
// reduce scale gradually
this._xscale = this._yscale-=2;
//remove movieclip when scale goes below zero
if (this._xscale <= 0) {
this.removeMovieClip();
}
}
ball_mc.onMouseDown = function() {
this.targetХ = this._parent._xmouse;
this.targetY = this._parent._ymouse;
this.onEnterFrame = this.moveToMouse;
};
moveToMouse, переменные скорости и т.д. Сначала необходимо определить два наших параметра, которые мы будем называть acceleration (ускорение) и friction (колебание). Установим для них значения 12 и 0,8, несмотря на то, что у нас будет возможность изменить их позднее для достижения различных эффектов. Добавьте эти две строки перед определением функции, в верхней части кода:var acceleration = 12; var friction = 0.8;
Итак, acceleration будет значением, на которое мы делим расстояние перед добавлением его к скорости нашего фильма, а friction будет являться значением, которое умножается на скорость каждого кадра по порядку для уменьшения быстроты движения.
Мы проработаем код движения по оси x перед добавлением его в функцию moveToMouse.
moveToMouse.var xdif=this.targetX-this._x;
xspeed (переменная, представляющая скорость фильма по оси x ):this.xspeed+=xdif/this._parent.acceleration;
speed на переменную friction для уменьшения скорости. Если не делать этого, наш фильм будет колебаться около своей конечной точки, не приближаясь к ней. Это умножение означает, что амплитуда уменьшается с каждым разом. Если убрать эту строку из исходного кода нашего окончательного результата, фильм не остановится на своей конечной точке, а будет двигаться вперед-назад на одно и то же расстояние.this.xspeed*=this._parent.friction;
this._x+=this.xspeed
Теперь мы объединяем все это в одну функцию moveToMouse, добавляя также вызовы дубликата и checkDistance. Ниже приведен полный код программы, как для оси x, так и для оси y:
ball_mc.moveToMouse = function() {
// calculate difference on x and у axes
var xdif = this.targetX-this._x;
var ydif = this.targetY-this._y;
// increment speed values
this.xspeed += xdif/this._parent.acceleration;
this.yspeed += ydif/this._parent.acceleration;
// dampen speed values
this.xspeed *= this._parent.friction;
this.yspeed *= this._parent.friction;
// add speed values to x and у properties
this._x += this.xspeed;
this._y += this.yspeed;
// create duplicate
this.duplicate();
// check if the movieclip has reached its target
this.checkDistance();
};
swing.fla и запустите его. Вы увидите эффект колебаний, причем объект будет вращаться почти по кругу вокруг конечной точки:
Изменение значений acceleration и friction сильно повлияют на качество движения. Поэкспериментируйте и попробуйте изменить их, чтобы наглядно понять, как эти параметры могут повлиять на движение.
Вообще говоря, чем ниже значение acceleration, тем быстрее будет сначала двигаться фильм, и чем ближе к единице значение friction, тем больше фильм будет колебаться вперед и назад. Некоторые значения могут сделать этот пример очень похожим на случай с уменьшением скорости, рассмотренный нами в первую очередь. Например, попробуйте ввести значения acceleration = 17 ; и friction = 0.4 ; и сохраните ваш фильм в файле swing02.fla. Разница в том, что у нас есть переменная, сохраняющаяся во время прохождения кадров, и фильм не изменяет моментально направление своего движения, как это было в случае с исходным уменьшением скорости движения, или движением по кадрам, и поэтому эти изменения происходят плавней.
Это все работает должным образом, однако есть еще одна деталь, которую нужно изменить. Принимая во внимание предыдущий пример с замедлением движения, становится понятно, что движение фильма должно было прекратиться сразу по прохождении определенного расстояния от конечной точки, в данном случае фильм, предположительно, прошел бы прямо над конечной точкой перед переходом на противоположную сторону. Это означает, что нам нужно изменить нашу функцию checkDistance, чтобы скорость фильма была также ниже определенного числа, чтобы по ошибке не происходило внезапного прекращения движения фильма.
Это более простой случай: все, что нам нужно, это вставить выражение if в нашу функцию для проверки того, является ли скорость достаточно малой после попадания расстояния в диапазон:
ball_mc.checkDistance = function() {
// check that distance is within range
if (Math.abs(this.targetX-this._x)<0.2
Math.abs (this. targetY-this ._y) < 0.2) {
// check that speed is nearly zero
if(Math.abs(this.xspeed)<0.2 Math.abs(this.yspeed) < 0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
}
};
Приведенный выше сценарий, содержащийся в swing3.fla, теперь защищен от любых непредвиденных обстоятельств.

Вы можете использовать эти основные принципы в различных ситуациях, контролируя скорость объекта разными способами. Сейчас на скорость влияет положение конечной точки (что-то похожее на магнетизм), однако вы, например, могли бы нажать кнопку правого курсора и добавить 5 к значению xspeed. Это переместит объект сильно вправо перед тем, как его скорость будет уменьшаться до полной остановки.
Если вам интересны способы создания других типов движения, рекомендуем вам обратить внимание на уравнения замедления на сайте www.robertpenner.com. Используя эти уравнения, вы можете указывать количество кадров, которое необходимо для определенной продолжительности движения, и фильм будет замедлять свое движение от точки A до точки B. В дополнение к этому, вы можете изучить применение этих уравнений на www.gizma.com/easing. Если вам интересны эти уравнения,
До этого момента мы рассматривали различные типы замедления применительно к движению по осям x и y, однако использование этого типа движения не ограничивается лишь только этими параметрами. Можно переходить от одного значения к другому и создавать движение, основанное на изменении размеров объектов.
В данном упражнении мы реализуем упругое движение, которое только что было нами рассмотрено. Мы будем определять координаты щелчка мыши и использовать их для определения размера, который будет принимать наш фильм.
mouseDown. Сложим координаты X и Y указателя мыши, затем умножим результат на два и используем его для определения конечного размера нашего фильма. Если пользователь щелкнет на левом верхнем углу, фильм примет размер, равный нулю, а если щелчок будет в правом нижнем углу, тогда размер станет равным 1900, т.е. (550+400)*2.ball_mc.onMouseDown=function(){
this.targetScale=(this._parent._xmouse+this._parent._ymouse)*2
this.onEnterFrame=this.scaleMe
}
Итак, мы взяли это значение и записали его в переменную targetScale фильма.
scaleMe, которая будет изменять размер фильма с текущего до конечного. Это делается с помощью достижения определенной точки на осях x и y, аналогичного рассмотренному ранее. Единственная разница заключается в том, что мы будем изменять параметры _xscale и _yscale вместо _x и _y. Так как мы хотим приравнять _xscale к _yscale, мы можем проводить все вычисления с использованием _xscale и затем установить _yscale на такое же значение:this.acceleration = 12;
this. friction = 0.8;
ball_mc.scaleMe = function() {
var scaleDiff = this.targetScale-this._xscale;
this.scaleSpeed += scaleDiff/this._parent.acceleration;
this.scaleSpeed *= this._parent.friction;
this._yscale = this._xscale += this.scaleSpeed;
this.checkScale();
};
Первые три строки функции такие же, как и ранее - они вычисляют разницу между текущим размером и конечным размером, и рассчитывают, на сколько увеличивается scaleSpeed. Затем переменная скорости уменьшается посредством умножения на константу friction.
В следующей строке мы производим два действия, эквивалентные следующим строкам.
this._xscale += this.scaleSpeed; this._yscale = this._xscale;
Когда Flash обнаруживает строку кода, аналогичную той, которую мы использовали в данном примере, он начинает выполнять действия справа налево. Сначала к переменной скорости прибавляется _xscale и после этого _yscale приравнивается к _xscale.
scale.fla и запустите его - теперь объект будет принимать различные размеры, в зависимости от того, на каком месте вы щелкните мышью.checkScale. Она более или менее похожа на функцию checkDistance, с которой мы имели дело ранее, и предназначена для проверки того, что размер фильма более или менее близок к конечному размеру, и что его скорость почти равна нулю:ball_mc.checkScale = function() {
if (Math.abs(this.targetScale-this._xscale)<0.2) {
if (Math.abs(this.scaleSpeed)<0.2) {
this._xscale = this._yscale=this.targetScale;
delete this.onEnterFrame;
}
}
};
Несмотря на то, что мы использовали этот подход лишь для изменения размера объекта, он может также применяться для изменения параметров _alpha, _rotation или любого другого свойства вашего фильма.
Теперь вы уже немного разбираетесь в том, как работают рассматриваемые разнообразные методы создания сценариев движения. Мы переходим к рассмотрению различных способов управления движением. Метод, который использовался до сих пор, подразумевает использование немалого количества различных рабочих мест, а мы настроили его на работу с одним фильмом.
Было бы гораздо лучше брать любой определенный фильм и создавать myMovieClip.slideTo(100,200), чтобы фильм мог перемещаться к этой точке и останавливаться, без надобности присваивать фильму вручную управляющий элемент clipEvent или функцию для проверки достижения конечной точки, и т.д.
Далее мы будем рассматривать различные методы создания сценариев начала движения объекта. Мы не будем указывать на тот или иной определенный способ и говорить, что он является самым лучшим, а вместо этого продемонстрируем вам несколько возможностей, чтобы вы могли выбирать из них нужное для работы с вашими проектами.
ball_mc.slideTo, которая будет выполняться каждый раз при щелчке мышью пользователем.slideTo три параметра: позицию по оси x, позицию по оси y и еще одно значение, которое будет указывать скорость скольжения:_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4);
};
slideTo, которая должна будет выполнять два отдельных действия:targetX и targetY ;enterFrame фильма для обеспечения его скольжения по направлению к конечной точке.Вместо того чтобы определять эту функцию для отдельного фильма, как мы делали в предыдущих функциях ball_mc.checkDistance, мы поместим ее в . Все функции внутри доступны в равной степени всем фильмам. Все встроенные методы фильмов, такие как goToAndPlay, также присутствуют в . При вызове функции она ведет себя так, как если бы она была внутри фильма.
MovieClip.prototype.slideTo = function(x, y, speed) {
// set up targets and speed variable
this.targetX = x;
this.targetY = y;
this.speed = speed;
// create onEnterFrame function
this.onEnterFrame = function() {
// take care of motion
this._x += (this.targetX-this._x)/this.speed;
this._y += (this.targetY-this._y)/this.speed;
// check if near target
if (Math.abs(this.targetX-this._x)<0.2
Math.abs(this.targetY-this._y)< 0.2) {
this._x = this.targetX;
this._y = this.targetY;
delete this.onEnterFrame;
}
};
};
Первые три строки функции берут параметры, которые ей передаются, и сохраняют их в переменных внутри фильма. Затем присваивается функция onEnterFrame, передвигающая фильм, а также выполняется проверка достижения конечной точки.
issueCommand1.fla и запустите его. При щелчке мышью фильм будет скользить к месту щелчка.ball_mc и дать новому инстансу имя ball_mc2 с помощью Property Inspector.ball_mc2 на рабочее место и затем откройте панель Actions для изменения кода onMouseDown, согласно тексту кода, приведенному ниже:_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4);
ball_mc2.slideTo(this._xmouse+19, this._ymouse, 1);
};
issueCommand1b.fla и запустите его. Вы заметите, что второй фильм перемещается значительно правее указателя мыши и со значительно меньшей скоростью.Итак, наш фильм работает как надо, однако при более детальном рассмотрении будут выявлены некоторые проблемы при использовании данного подхода. Наиболее заметный недочет заключается в том, что любой фильм может иметь только одну функцию, присвоенную его управляющему элементу onEnterFrame: если фильм уже предположительно имел функцию, присвоенную его управляющему элементу onEnterFrame, а мы присвоили ему другую функцию, то вторая функция будет записана поверх первой. Это может случиться, если к фильму было применено плавное исчезновение, увеличение размеров или по каким-либо другим причинам.
Другая проблема состоит в том, что мы помещаем в фильм переменные, такие как targetX, targetY и speed, которые могут уже использоваться в фильме с другими целями. Они будут просто записываться поверх старых, что соответствующим образом повлияет на функциональность фильма. Несмотря на то, что мы обычно помним имена, присваиваемые объектам в нашем ActionScript, полезно использовать информативные и не лишенные смысла названия.
Теперь рассмотрим все потенциально возможные проблемы, которые могут возникнуть при создании пустого фильма внутри уже существующего. Мы будем использовать внутренний фильм в качестве объекта, в котором находятся все переменные, и он будет также являться объектом, к которому можно присвоить управляющий элемент onEnterFrame, не опасаясь выхода из строя какой-либо предыдущей функциональности.
Управляющий элемент mouseDown настраиваем так же, как и прежде:
_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4);
};
Теперь наша функция slideTo будет выглядеть почти так же, как раньше, и основным отличием является то, что прежде фильм управлял сам собой, а теперь фильм выполняет внутри себя определенные действия. Это видно из замены 'this' на 'this._parent' при доступе или изменении параметров X и Y. Наш управляющий фильм осуществляет доступ и управление параметрами своего родительского фильма, т.е. того фильма, который необходимо перемещать.
MovieClip.prototype.slideTo = function(x, y, speed) {
// create controller movieclip
var control_mc = this.createEmptyMоvieClip ("slideControl",this.depth++);
control_mc.targetX = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.onEnterFrame = function() {
// this._parent is the movieclip we're moving
this._parent._x += (this.targetX-this._parent._x) /this.speed;
this._parent._y += (this.targetY-this._parent._y)/this.speed;
if (Math.abs(this.targetX-this._parent._x)<0.2
Math.abs(this.targetY-this._parent._y) <0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.removeMovieClip();
}
};
};
Как и в отношении функции duplicate, созданной нами ранее, мы использовали ссылку, возвращаемую функцией createEmptyMovieClip для обращения к вновь созданному . Одной из возможных "ловушек" при использовании этого метода с createEmptyMovieClip является то, что пользователь может записать фильм поверх уже имеющегося внутри. Проще говоря, это то, чего нужно остерегаться при управлении фильмом; необходимо иметь некий механизм определения того, на какой глубине находятся объекты и символы. Посредством ввода переменной глубины для каждой временной линии можно эффективно реализовать данную меру предосторожности, применяя этот способ каждый раз при создании фильма. С другой стороны, можно было бы объявить глобальную переменную глубины для всех временных линий, однако все же лучше иметь одну переменную для каждой временной линии, чтобы не терять контроль над значениями. Оставляем право выбора за вами.
Вы могли бы использовать addProperty для автоматического добавления глубины каждый раз при попытке доступа, однако пришлось бы делать это для каждого фильма по отдельности, поэтому статическая переменная, увеличивающаяся на единицу с каждым разом, будет оптимальным решением. Другим возможным подходом может быть цикловое прохождение фильмов целиком в отдельном месте, и использование getDepth() в каждом из них для выяснения глубин, однако это довольно нерациональный способ, так как здесь придется делать большое количество ненужных запросов на действия.
Сохраните ваш фильм в файле issueCommand2.fla. Если вы запустите фильм на данном этапе, вы заметите, что движение будет прекращено при быстром последовательном щелчке мышью. Если выполнить команду Debug > List Variables, станет видно, что причиной этого является присутствие более чем одного инстанса slideControl в нашем фильме - все они работают с различными направлениями. Таким образом, нам нужно придумать что-то такое, с помощью чего можно будет убеждаться в присутствии в любой момент времени лишь одного инстанса этого управляющего клипа внутри нашего фильма.
Возможным решением этой проблемы является создание дубликатов slideControl каждый раз на одной и той же глубине, чтобы прежний инстанс автоматически перезаписывался новым. Однако лучше не использовать этот подход, так как он нарушает схему глубины, о которой мы только что говорили. В дополнение к этому, если мы использовали метод колебаний для обеспечения движения нашего фильма, нам совершенно не нужно будет перезаписывать любые переменные скорости внутри фильма и прерывать его движение; нам необходимо сохранение этих данных до остановки фильма.
Более разумным решением будет проверка того, имеется ли уже фильм slideControl. Если это не так, то вам нужно будет создавать его, а если он уже присутствует, то нужно будет перезаписывать переменные X и Y конечной точки и событие onEnterFrame.
Оба подхода приводят к одному и тому же результату.
MovieClip.prototype.slideTo = function(x, y, speed) {
var control_mc
if (this.slideControl) {
//if slideControl already exists
control_mc = this.slideControl;
}
else {
//if slideControl doesn't exist then create it
control_mc = this.createEmptyMovieClip
("slideControl", this.depth++);
}
control_mc.targetX = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.onEnterFrame = function() {
this._parent._x += (this.targetX-this._parent._x)/this.speed;
this._parent._y += (this.targetY-this._parent._y)/this.speed;
if (Math.abs(this.targetX-this.__parent._x)<0.2
Math.abs(this.targetY-this._parent._y)<0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.removeMovieClip();
}
};
};
Сохраните файл под именем i ssueCommand3.fla и при запуске фильма обратите внимание на то, что теперь он ведет себя нужным образом.
Несмотря на положительный результат, данный подход несостоятелен с практической точки зрения, так как мы не можем определить момент, когда фильм достигает своей конечной позиции. Движение на самом деле прекращается, но нам часто будет требоваться выполнить некоторое другое действие по окончании движения. По крайней мере, было бы неплохо знать, когда фильм находится в движении, а когда нет.
Для решения этой проблемы обратимся к функции с обратной связью. Все будет работать таким же образом, как и некоторые другие компоненты - они будут вызывать функцию при изменении значения или обновлении каких-либо данных. При прекращении движения будет вызываться указанная нами функция. Итак, прежде всего, мы создадим функцию, которая будет вызываться при окончании движения фильма. В момент, когда это потребуется, можно выводить сообщение в окне Output с указанием имени фильма, завершившего свое движение, которое мы будем использовать в качестве параметра.
function slideDone(mс) {
trace("movieClip "+mc+" has finished moving");
}
Вообще говоря, когда движение фильма будет прекращаться, будет осуществляться вызов функции slideDone в _root. При вызове функции slideTo, нам нужно будет добавить два других параметра - callBackObj и callBackFunc. callBackObj является местоположением вызываемой функции (в данном случае, _root ), а callBackFunc - именем вызываемой функции, т.е., в нашем случае, функции slideDone. Итак, сначала нужно добавить в нашу функцию два дополнительных параметра:
_root.onMouseDown = function() {
ball_mc.slideTo(this._xmouse, this._ymouse, 4, _root, "slideDone");
};
Затем нам потребуется настроить функцию slideTo для работы с этими двумя параметрами и для хранения их в фильме slideControl (посредством добавления кода):
MovieClip.prototype.slideTo =
function(x, y, speed, callbackObj, callbackFunc) {
var control_mc
if (this.slideControl) {
control_mc = this.slideControl;
}
else {
control_mc =
this.createEmptyMovieClip("slideControl", this.depth++);
}
control_mc.targetX = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.callBackObj = callBackObj;
control_mc.callBackFunc = callBackFunc;
};
Наконец, нам нужно выяснить, как вызывать _root.slideDone с использованием двух переменных callBackObj и callBackFunc. Это на самом деле очень просто и делается практически таким же образом, как осуществляется, например, доступ к значению в массиве, или динамическое создание ссылки на фильм:
this.callBackObj [this.callBackFunk] (this._parent)
Часть кода перед квадратными скобками является местоположением функции ( _root ). Содержимое в квадратных скобках осуществляет поиск значения, отвечающего данной строке (в нашем случае, slideDone ), и затем значение в скобках передается в виде параметра. Передаваемое здесь значение является обращением к фильму, который мы перемещали, т.е. this._parent, our ball_mc. Наша функция целиком выглядит примерно так:
MovieClip.prototype.slideTo =
function (x, y, speed, callbackObj, callbackFunc) {
var control_mc
if (this.slideControl) {
control_mc = this.slideControl;
}
else {
control_mc = this.createEmptyMovieClip
("slideControl", this.depth++);
}
control_mc.targetХ = x;
control_mc.targetY = y;
control_mc.speed = speed;
control_mc.callBackObj = callBackObj;
control_mc.callBackFunc = callBackFunc;
control_mc.onEnterFrame = function() {
this._parent._x += (this.targetX-this._parent._x) /this.speed;
this._parent._y += (this.targetY-this._parent._y) /this.speed;
if (Math.abs(this.targetX-this._parent._x)<0.2
Math. abs(this.targetY-this._parent._y)<0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.callBackObj[this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
Сохраните ваш фильм в файле issueCommand004.fla и при запуске фильма вы увидите, что в окне Output отобразится сообщение "movieClip_level0.mc has finished moving", т.е. сообщение о завершении движения фильма. Это будет происходить каждый раз при остановке фильма.
Вы можете изменить содержимое функции slideDone для осуществления любых нужных вам действий. Например, у вас может быть массив, содержащий данные о том, какие фильмы запущены, и вы можете использовать его для обеспечения начала выполнения следующей стадии проекта только по завершении продолжения фильма.
Функция callback позволяет нам использовать очень важную возможность обратной связи. Мы можем инициировать начало движения фильма к определенному месту и оставить этот процесс на самостоятельное завершение, так как по окончании движения от него поступит соответствующее сообщение. Нашим следующим шагом будет настройка функции, которая сможет согласоваться с любыми нужными нам параметрами, измененными в фильме, после чего фильм будет изменять все эти параметры одновременно. Мы вернемся к этой теме в одной из следующих лекций.
Сейчас настало время вновь обратиться к некоторым из уже рассмотренных подходов. То, о чем мы говорили ранее, может показаться однообразным, однако сейчас мы приведем несложный пример, использующий некоторые из изученных нами способов. В качестве примера создадим страницу Flash.
Мы планируем создать набор из нескольких фотографий так, что при щелчке на кнопке фотографии будут перемещаться под маской и двигаться таким образом, что в конечной области появится нужная фотография.


Итак, теперь у нас на рабочем месте есть инстанс в корневой временной диаграмме. Мы добавим движение, переместив фильм под маску так, чтобы нужный рисунок оказался на нужной позиции.
holder , используйте Property Inspector, чтобы задать ему имя инстанса holder и указать его значения X и Y, установив их на 0,0. После этого точка закрепления будет размещена в левом верхнем углу рабочего места. Наконец, назовите текущий слой 'pictures'.pictures и пометьте его как маску.X и Y, равные 180,89.scripts и в новом слое откройте панель Actions, чтобы добавить следующий код:holder.setMask (maska);
Посредством этого выражения фильм внутри скобок (maska) становится маской для фильма, который был вызван методом ( (набора из девяти рисунков), которые просвечиваются, а остальные будут затемнены.
background1 нарисуйте светло-серый прямоугольник 202x202 пикселя и с помощью Property Inspector задайте его значения X и Y, равные 179,88. Теперь на слое background2 нарисуйте другой серый прямоугольник более темным цветом и размером 204x244, задав для него значения X и Y, равные 178,87. Это будет границей наших фильмов.Вы можете добавить любые другие нужные вам изображения на фоновые слои.
holder , мы будем использовать функцию slide, созданную ранее. Добавьте следующий код в слой scripts над только что вставленной вами строкой кода:speed = 4;
MovieClip.prototype.slideTo =
function(x, y, speed, callbackObj, callbackFunc) {
Var mc_control
if (this.slideControl) {
mc_control = this.slideControl;
}
else {
mc_control = this.createEmptyMovieClip
("slideControl" , this. depth++) ;
}
mc_control.targetX = x;
mc_control.targetY = y;
mc_control.speed = speed;
mc_control.callBackObj = callBackObj;
mc_control.callBackFunc = callBackFunc;
mc_control. onEnterFrame = function() {
this._parent._x += (this.targetX-this._parent._x) /this.speed;
this._parent._y += (this.targetY-this._parent._y) /this.speed;
if (Math.abs(this.targetX-this._parent._x)<0.2
Math.abs(this.targetY-this._parent._y)<0.2) {
this._parent._x = this.targetX;
this._parent._y = this.targetY;
this.callBackObj[this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
function slideDone(mc) {
trace("movieClip "+mc+" has finished moving");
}
Для выполнения этого кода мы создадим несколько кнопок. Вместо того чтобы динамически создавать кнопки с использованием createEmptyMovieClip и createTextField (эти подходы мы рассмотрим позднее), мы создадим символ вручную и затем динамически применим его.
butt. Выделите фильм в Library и щелкните на белом значке меню вверху панели заголовка Library. В меню выберите параметр Linkage:, чтобы открыть окно Linkage Properties.butt. Разместите поле Dynamic Text внутри фильма и дайте ему имя инстанса tf с помощью Property Inspector. Размер текста нашего поля равен 9, шрифт - Arial, без встроенных шрифтов.scripts нашей корневой временной диаграммы (под уже имеющимся кодом).holder.setMask(maska);
for (var i = 1; i<=9; i++) {
var mc_control = _root.attachMovie("butt", "butt"+i, i);
mc_control._x = 175+i*9;
mc_control._y = 375;
mc_control.mc = this.holder["c"+i] ;
mc_control.tf.text = i;
}
Этот код циклически выполняется для каждого фильма с фотографией и добавляет кнопку. В нем используется итератор 'i' для установки текстового значения поля 'tf', поэтому в поле появятся числа от 1 до 9. В дополнение к этому, используется значение 'i' для установки конечного фильма для каждой кнопки (несмотря на то, что butt является фильмом, этот объект будет кнопкой, так как мы будем присваивать ему событие onPress ). Число 9 в конце четвертой строки кода контролирует расстояние между всеми фильмами-кнопками.
Перед присвоением события onPress нам нужно решить, какое именно действие будет выполняться при нажатии кнопки, так же как и место, в которое должен переместиться для корректного отображения каждой фотографии под маской.
Все это можно сделать, рассмотрев конкретный случай. Например, если бы у нас был фильм, расположенный в точке 300,300 в фильме , где должен был бы быть расположен , чтобы правильно отображать фотографии под маской? Если бы мы расположили на том же месте рабочего стола, что и маску, то отображалось бы все, что было бы на позиции 0,0 в . Основываясь на этом, мы можем сделать вывод, что если бы нам нужно было отображать объект на позиции 300,300 в
(maska._x-300, maska._y-300);
Теперь, если мы обратимся к координатам _x и _y любого из наших фильмов с изображениями, то получим координаты фильма внутри . Следовательно, можно сказать, что для того, чтобы каждый фильм был расположен строго под маской, нам необходимо перемещать на следующее место:
(maska._x-mc._x, maska._y-mc._y);
onPress для наших кнопок:for (var 1 = 1; i<=9; i++) {
var mc_control = _root.attachMovie("butt" , "butt"+i, i);
mc_control._x = 175+i*9;
mc_control._y = 295;
mc_control.mc = this.holder["c"+i] ;
mc_control.tf.text = i;
mc_control.onPress = function() {
var x = this._parent.maska._x-this.mc._x;
var у = this._parent.maska._y-this.mc._y;
this._parent.holder.slideTo(x, y, this._parent.speed, this._parent, "slideDone") ;
};
}
Вызов функции slideTo происходит таким же образом, как и ранее, с использованием показанного ранее вычисления для указания координат X и Y конечной точки скольжения. Hам необязательно записывать значения X и Y в переменные, поэтому мы можем убрать из кода соответствующую его часть и выполнять вычисления внутри вызова функции.
for (var i = 1; i<=9; i++) {
var mc_control = _root.attachMovie("butt", "butt"+i, i);
mc_control._x = 175+i*9;
mc_control._y = 295;
mc_control.mc = this.holder["c"+i] ;
mc_control.tf .text = i;
mc_control.onPress = function() {
this._parent.holder.slideTo(this._parent.maska._x-this.mc._x, this._parent.maska._y-this.mc._y,
this._parent.speed, this._parent, "slideDone");
};
}
Сохраните фильм в файле pictureMask.fla. При его запуске вы увидите, что при скольжении каждого рисунка на его позицию происходит регистрация происходящего в окне Output. Сейчас для нас это не имеет значения, однако позже мы разберемся, как использовать эту возможность для вызова какого-либо события, например, текстового эффекта. Это лишь однa из возможных применений этого окна. Однако этот вид управления также может быть использован для вывода некоторого пояснительного текста, так, чтобы рисунки являлись фоном интерфейса, или для запуска анимации. Необходимо заметить, что мы управляем движением, которое сами же и инициировали. Мы знаем, когда оно начинается, и можем быть оповещены о его завершении, а это означает, что оно может стать частью цепи событий.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.