Введение в
события и управляющие элементы
Внимание!
Для работы с этой лекцией необходимы учебные
файлы, которые Вы можете загрузить
здесь.
Одним из
наиболее важных изменений во Flash MX является новая
модель поддержки событий. Она полностью изменяет
представление о способе кодирования анимации и
взаимодействия с клавиатурой и мышью. Вы уже кое-что
узнали об этом из нашей книги, рассмотрим теперь эту
тему подробнее.
Мы
рассмотрим несколько важных событий, которые имеют
отношение к анимации и интерактивности и
представляют хорошую модель для структуры фильма,
основанной на коде. Мы также рассмотрим некоторые
связанные с этим возможности, такие как setInterval
и watches.
Основные
определения
Событие -
это что-то, что происходит в фильме во время его
выполнения. Определенные объекты в фильме могут
воспринимать эти события и реагировать на них. Чаще
всего применяются события, которые происходят при
возникновении нового кадра в фильме, движении мыши,
нажатии кнопки мыши или клавиши.
Управляющий элемент события (или управляющий метод
события) - это функция, которая выполняется при
возникновении определенного события.
Приемник -
это объект, который может воспринимать и
обрабатывать определенные типы событий с управляющим
элементом событий.
Рассмотрим в качестве примера
onEnterFrame. Сначала создадим фильм - ничего
экстраординарного, просто небольшую фигуру. Назовем
его mc1_mc. Сначала мы
должны убедиться, что фильм по умолчанию является
приемником события enterFrame.
Каждый раз при переходе Flash к новому кадру
происходит событие enterFrame,
и наш mc1_mc должным
образом информируется об этом факте. В данный момент
это не имеет большого значения, так как мы не
определили управляющий элемент события для его
обработки, но он предупрежден о событии и готов
приступить к действиям, если мы дадим
соответствующую команду.
Теперь
определим функцию для управляющего элемента. Введите
этот код в кадр 1 фильма.
function move() {
this._x++;
}
Сейчас
это просто функция с именем move. Она берет
какой-либо объект, вызывает эту функцию и перемещает
его на один пиксель вправо. Функция довольно проста.
Теперь присвоим эту функцию управляющему элементу
onEnterFrame фильма
mc1_mc.
mc1_mc.onEnterFrame =
move;
Обратите внимание на ключевое слово
this в коде выше.
Когда вы используете параметр или переменную саму по
себе (без пути), подразумевается, что он (она)
принадлежит текущей временной диаграмме. Если
опустить выражение this
в этом блоке кода, _x
примет значение _root._x,
и наша функция переместит не
mc1_mc, а весь фильм целиком!
При
указании выражения this
в функции идет ссылка на всякий объект, вызывающий
эту функцию. В данном случае фильм
mc1_mc вызывает
функцию move, поэтому this._x
переходит к mc1_mc._х,
что нам и требуется.
При
выполнении этого кода mc1_mc
обнаруживает событие
enterFrame в начале каждого нового кадра,
которое вызывает его метод
onEnterFrame (теперь - функция
move () ). В
результате фильм медленно скользит поперек рабочего
места.
Об этом
уже говорилось в данной книге, но теперь мы
рассмотрим все более подробно. При определении
переменной, например,
name="Keith", Flash отводит место в памяти
вашего компьютера и помещает туда значение "Keith".
Если вы ранее присваивали значение, к примеру,
name="Todd", то оно
будет заменено в памяти новым значением. Аналогичный
процесс происходит при определении функции.
Когда
мы пишем
function move() {
this._x++;
}
Flash
отводит место в памяти для хранения всех команд,
содержащихся в функции. Если вы дополните команду
move() двумя скобками,
Flash перейдет в это местоположение в памяти и
выполнит все выражения, которые там будут обнаружены.
Хотя это и очевидно, но насколько известно, при
поддержке событий достаточно важно то обстоятельство,
что имя функции без скобок тоже несет в себе
определенный смысл.
Имя
функции само по себе может восприниматься как
переменная, которая содержит адрес функции в памяти.
Вы можете себе представить почтовое отделение, на
стене которого подвешены небольшие металлические
почтовые ящики. Каждый из них имеет свой номер,
который говорит о положении ящика, и некоторое
содержимое, т.е. почту. Имя функции можно сравнить
со стопкой бумаги с номером ящика на ней. Этот номер
означает, где находится ваша функция. С технической
точки зрения это значит, что переменная move
содержит ссылку на функцию. Вы можете присвоить
переменной значение и затем передать это значение
другой переменной, и точно так же вы можете
передавать ссылки функции. Давайте рассмотрим
следующий код:
function hello() {
trace("hi there");
}
Мы
создали функцию в памяти. Переменная
hello содержит ссылку
на эту функцию. Любая переменная, содержащая ссылку
на функцию, может использоваться для выполнения этой
функции посредством добавления пары скобок.
hello ();
Теперь
мы можем передать эту ссылку другой переменной.
greeting = hello;
Здесь
приветствие в переменной дает ссылку на функцию. Обе
переменные теперь содержат ссылку на одну и ту же
функцию. Теперь вы можете выполнить функцию
следующим образом.
greeting();
Это
действует даже для встроенных функций и специальных
объектов во Flash. Например, у нас есть объект
Math, содержащий метод
random(). Это просто
функция в памяти. Переменная
random в объекте Math
содержит ссылку на эту функцию. Мы можем взять эту
ссылку и сохранить ее в любом нужном месте.
myRand = Math.random;
Теперь
myRand содержит ссылку
на функцию, которая будет возвращать случайное число.
Вы можете вызвать ее так.
myRand();
Заметьте, что это выражение отличается от следующего.
myRand = Math.random();
В
данном случае после имени функции поставлены скобки,
поэтому функция и выполняется. Затем она будет
возвращать случайное число и передавать его
myRand. Это не совсем
то, что нужно, но вы будете часто допускать эту
ошибку. Даже я до сих пор это делаю!
Итак,
возвращаемся к нашему коду. Теперь у вас должно быть
более ясное представление о том, что здесь
происходит.
function move() {
this._x++;
}
mc1_mc.onEnterFrame = move;
Мы
создаем функцию в памяти, move представляет ссылку
на данную функцию и передает эту ссылку в
mc1_mc.onEnterFrame.
Внимание! Многие допускают ошибку, набирая следующий
код.
mc1_mc.onEnterFrame =
move();
Теперь
вы должны понимать, почему эта структура не будет
работать, и что на самом деле происходит.
Вы
также можете прибегнуть к сокращенному методу
определения управляющих элементов событий.
mc1_mc.onEnterFrame =
function() {
this._x++;
};
Это
можно интерпретировать как создание функции без
имени.
function______________ ()
{
this._x++;
}
Ссылка
на функцию не передается переменной, а выражение
выше само по себе при запуске дает в результате
ссылку на функцию. Поэтому мы можем взять результат
определения функции и присвоить его прямо
управляющему элементу события.
mc1_mc.onEnterFrame=function______________________
() {
this._x++;
};
Безусловно, мы привели длинный пробел в строке для
полноты представления. На самом деле нужно писать
так, как показано в первом примере выше. Этот метод
полезен, когда вы знаете, что вы будете присваивать
функцию только один раз и только одному управляющему
элементу.
Также
обратите внимание на точку с запятой в конце строки.
Весь этот блок кода является одним выражением
присвоения, и, следовательно, должен заканчиваться
точкой с запятой, хотя правилами синтаксиса она не
требуется. После определения функций самих по себе
точку с запятой можно не ставить.
Удаление
управляющих элементов событий
Одним
из значимых моментов, связанных с управляющими
элементами событий, является то, что они создаются
не "раз и навсегда". Вы можете присваивать их когда
угодно, присваивать другую функцию в любой момент
времени или удалять управляющий элемент. Предположим,
вам нужно выполнить функцию только один раз при
возникновении определенного условия. Это можно
сделать так:
function move() {
if (this._x<500) {
this._x++;
}
}
mc1_mc .onEnter Frame = move;
Это как
раз тот момент, на котором мы "зависали" во Flash 5
при работе с событиями фильма. На самом деле это не
самый эффективный метод. В данном примере функция
move все еще проверяет значение
this._x и сравнивает
его со значением 500, один раз в каждом кадре. Если
мы знаем, что функция полностью завершила свою
работу, мы можем просто ее удалить. Существуют два
способа выполнения этого действия: либо удалить
управляющий элемент, либо присвоить ему
неопределенное значение или null. Принято просто
удалять его. В результате получим следующее.
function move() (
if (this._x<500 {
this._x++;
} else {
delete this.onEnterFrame;
}
}
mc1_mc.onEnterFrame = move;
Здесь
необходимо сказать, что при удалении управляющего
элемента таким способом вы на самом деле не удаляете
функцию, содержащуюся в памяти. Вы просто удаляете
ссылку на эту функцию, хранимую в
onEnterFrame. В данном
случае у вас все еще будет ссылка на функцию в
переменной move. Она
может быть заново присвоена в любое время.
Flash
ведет протоколирование функций, а также количества
ссылок, указывающих на каждую функцию. Если вы
удалите последнюю или единственную ссылку на функцию,
хранимую в памяти, Flash оповестит операционную
систему о том, что это место в памяти больше не
требуется, и что оно освободилось для использования
в каких-либо других целях.
Изменение
управляющих элементов событий
Приведем другой пример полезного использования
управляющих элементов событий. В последнем примере
нам нужно было, чтобы фильм выполнялся до тех пор,
пока не реализуется определенное условие. Однако
точно так же мы могли бы назначить ему другое
действие в другое время. Нужно просто присвоить
другую функцию, посредством которой будут
выполняться какие-либо другие действия. В следующем
коде у нас есть две функции:
moveRight и moveLeft.
Когда фильм перемещается слишком далеко вправо или
влево, его управляющий элемент
onEnterFrame
переключается на противоположную функцию. Мы также
немного увеличили скорость движения, поэтому шарик
не будет "ползти" по экрану.
function moveRight()
{
this._x+=5;
if (this._x>500) {
this.onEnterFrame = moveLeft;
}
}
function moveLeft() {
this._x-5;
if (this._x<50) {
this.onEnterFrame = moveRight;
}
}
mc1_mc.onEnterFrame = moveRight;
Я вовсе
не утверждаю, что это наилучший способ движения
объекта вперед и назад, однако так можно динамически
изменять управляющие элементы событий в любой момент
во время выполнения фильма.
Передача параметров
управляющим элементам событий
Если вы
когда-либо ранее использовали функции в любом языке
программирования, вы, вероятно, задавали себе вопрос:
"Что насчет параметров?". Обычно при определении
функции можно указывать число параметров внутри
скобок. Этим можно воспользоваться для отправки
данных функции при ее выполнении. Функция может
использовать эти данные в своих выражениях.
function
saySomething(message) {
trace (message);
}
saySomething("I'd like to buy an argument.");
Как это
работает с управляющими элементами события? Отвечаем:
никак. Сначала я расскажу об ошибке, которую
допускают многие программисты, чтобы вы запомнили ее
и не допускали сами. Вы определяете функцию, которая
содержит один или два параметра, и затем пытаетесь
присвоить эту функцию управляющему элементу события.
function
showCoords(x, y) {
trace("horiz: "+x+" vert: "+y);
}
mc1_mc.onEnterFrame = showCoords(this._x, this._y);
Все
корректно, однако помните, что при использовании
имени функции со скобками функция выполняется, а не
присваивает ссылку. Но можно обойти это
обстоятельство. Используя метод прямого присвоения,
создайте безымянную функцию и сделайте так, чтобы
эта функция вызывала вашу функцию с параметрами.
function
showCoords(x, y) {
trace("horiz: "+x+" vert: "+y);
}
mc1_mc.onEnterFrame = function() {
showCoords(this._x, this._y);
};
В
данном примере выполняется безымянная функция,
присвоенная onEnterFrame.
Она запускает функцию
showCoords(), передавая ей параметры.
Программирование
на основе событий
Теперь,
когда вам вполне понятно, что
onEnterFrame является полезным изобретением,
перейдем к другим управляющим элементам. Здесь мы
можем погрузиться в мир программирования на основе
событий.
Помните, что все, что находится в функции
управляющего элемента
onEnterFrame, будет выполняться много раз в
секунду. Приведем пример того, как это используется
в некоторых случаях.
mc1_mc.onEnterFrame
= function() {
if (buttonStatus == "pressed") {
// do some stuff here...
}
};
В
некотором месте фильма имеется кнопка, нажатие
которой устанавливает переменную
buttonStatus на
значение "pressed", т.е. "нажато". Здесь управляющий
элемент события постоянно проверяет состояние этой
переменной, ожидая его изменения. Напоминает дальнюю
поездку с детьми в автомобиле, когда они засыпают
вас вопросами "Мы уже приехали? Мы уже приехали? Мы
уже приехали?".
Этот
процесс называется опросом. Несмотря на то, что в
каждом кадре мы выполняем только одно выражение if,
реализация такого подхода в фильме весьма влияет на
производительность. Я работал с несколькими сложными
анимационными проектами, выполнявшимися корректно
сами по себе. Но затем я поместил их в интерфейс,
где было два блока кода обработки событий, который
проверял статус некоторой переменной. Неожиданно моя
анимация замедлилась и "подвисла". Я просмотрел
нарушающие работу циклы, проверявшие определенное
условие, и перепрограммировал их, после чего все
заработало корректно.
Программирование на основе событий реализуется
различными способами. Вместо постоянной проверки
того, случилось ли что-то или еще нет,
устанавливается управляющий элемент события, после
чего можно "откинуться на спинку стула и
расслабиться". Программа не выполняет никаких
действий и занимает минимальное количество
процессорного времени. Когда происходит нужное
событие, она возвращается в активное состояние и
приступает к выполнению действий.
Все
остальные управляющие элементы событий, о которых мы
узнаем, будут работать таким же образом. Всегда
полезно искать различные способы того, как могут
быть использованы другие управляющие элементы
событий вместо постоянной проверки с
onEnterFrame.
Другие события
Ранее
мы упоминали о том, что фильм является приемником
события enterFrame.
Ему известно, когда фильм сменил кадр и, если
определен соответствующий метод поддержки события,
он обработает это событие посредством выполнения
функции. Итак, фильмы также являются приемниками для
другого типа событий, называемых событиями мыши. В
них входят события mouseMove,
mouseDown и
mouseUp. Хотя это
очевидно, событие mouseDown
происходит при каждом нажатии главной кнопки мыши, а
событие mouseUp происходит, когда вы отпускаете
кнопку мыши. Им соответствуют методы поддержки
onMouseMove,
onMouseDown и
onMouseUp.
Не
озадачивайте себя различием между, скажем,
mouseMove и
onMouseMove. mouseMove
- это событие. Это то, что непосредственно
происходит. onMouseMove
- это управляющий элемент поддержки события. Это то,
что нужно сделать при возникновении данного события.
Это можно интерпретировать следующим образом: "Когда
произойдет событие mouseMove,
нужно сделать следующее:". Более того, вам никогда
не придется вводить имена событий, например,
mouseMove,
mouseDown,
enterFrame и т.д. Эти
термины существуют лишь для упоминания о событиях в
описании или рассказе о них. Они не являются
ключевыми словами программирования AсtionScript.
Мы
можем создавать и присваивать функции этим
управляющим элементам точно так же, как мы это
делали в случае с
onEnterFrame. Когда произойдет событие,
функция будет вызвана.
Здесь
мы присвоим две функции управляющим элементам
onMouseDown и
onMouseUp фильма.
mc1_mc.onMouseDown
= function() {
this._xscale = 200;
this._yscale = 200;
};
mc1_mc.onMouseUp = function() {
this._xscale = 100;
this._yscale = 100;
};
Помните всемогущий "this"?! Несмотря на то, что мы
присваиваем функцию непосредственно управляющему
элементу, нам все равно нужно указывать this для
ссылки на объект, вызывающий эту функцию.
При
нажатии кнопки мыши управляющий элемент onMouseDown
фильма mc1_mc вызывает и выполняет присвоенную ему
функцию. Эта функция устанавливает размеры
mc1_mc по
x и
y на значение 200%.
Когда
вы отпустите кнопку, вызовется функция, присвоенная
onMouseUp, установив
размер обратно на 100. Вам также необходимо иметь в
виду, что это не похоже на случай с кнопкой, когда
вам нужно было находиться поверх фильма для
регистрации щелчка. Все фильмы на рабочем столе
будут принимать все события мыши, независимо от
того, где они произошли.
Теперь
проверим управляющий элемент
onMouseMove. Это действие выполняется каждый
раз при движении мыши. Здесь мы применим длинный
метод, чтобы вы умели пользоваться и тем, и другим.
function move() {
this._x = _root._xmouse;
this._y = _root._ymouse;
}
mc1_mc.onMouseMove = move;
Добавив строку
Mouse.hide();
вы
узнаете, как создать свой собственный курсор для
фильмов Flash.
Управляющий
элемент события кнопки
Во
Flash 5, чтобы сделать фильм "перетаскиваемым",
нужно было выполнить достаточно сложную процедуру по
созданию невидимой кнопки внутри фильма, и присвоить
ей действия, влияющие на фильм, в котором она
находилась. Во Flash MX фильмы сами по себе могут
выступать в роли кнопок. Приведем несколько новых
управляющих элементов событий, которые применимы как
к кнопкам, так и к фильмам.
- onRollOver
- onRollOut
- onPress
- onRelease
-
onReleaseOutside
Они
работают таким же образом, как и управляющие
элементы поддержки событий, с которыми мы уже
ознакомились. Поддерживаемые ими события должны быть
довольно простыми. Давайте посмотрим, насколько
легко создавать перетаскиваемый фильм.
Так
как фильм теперь может обрабатывать нажатия и
отпускания кнопки, все, что нам нужно, это
установить функцию на каждый управляющий элемент для
начала и окончания перетаскивания. Это просто.
mc1_mc.onPress =
function() {
this.startDrag();
};
mc1_mc.onRelease = function() {
this.stopDrag();
};
mc1_mc.onReleaseOutside = function() {
this.stopDrag();
};
Я
всегда также обеспечиваю поддержку события
onReleaseOutside. Это
предотвращает "зацепление" фильма за указатель мыши,
если вы случайно перетащили слишком быстро и
отпустили кнопку в момент, когда указатель не
находился в фильме. Так как функции этих двух
событий в точности идентичны, можно использовать
другой сокращенный подход.
mc1_mc.onPress =
function() {
this.startDrag();
};
mc1_mc.onRelease = mc1.onReleaseOutside = function() {
this.stopDrag();
};
Оба
управляющих элемента присвоены одной функции.
При
запуске этого файла обратите внимание на то, что
указатель мыши, находясь в верхней части фильма,
превращается в "руку", как если бы там находилась
кнопка.
Перетаскивание и
бросание объектов
Можно
создать расширенный метод "перетащить и вставить" -
"перетащить и бросить". Это позволит пользователю
перетаскивать шарик в определенном направлении,
причем после отпускания кнопки шарик будет
продолжать двигаться в заданном направлении. На
самом деле, он будет перемещаться с той же скоростью,
с которой его перетаскивали. Вы удивитесь, когда
узнаете, насколько просто это реализуется, однако
перед этим необходимо объяснить некоторые моменты.
- Мы можем
начать с ввода только что созданного кода для
перетаскивания и вставки.
- Необходимо
знать, перетаскиваем ли мы объект в данный
момент. Это можно сделать с помощью переменной
типа Boolean,
которую мы назовем dragging. Мы будем
устанавливать значение переменной dragging на
"истина" при перетаскивании фильма, и на "ложь",
когда кнопка мыши не нажата. Для лучшего
контроля мы сделаем dragging параметром самого
клипа.
mc1_mc.onPress
= function() {
this.startDrag();
this.dragging = true;
};
mc1_mc.onRelease = mc1.onReleaseOutside = function () {
this.stopDrag();
this.dragging = false;
};
- Нам нужен
некоторый код для перемещения объекта в то
время, когда он не перетаскивается. Это
реализуется в функции управляющего элемента
события onEnterFrame.
Сначала мы просто будем проверять, является ли
объект перетаскиваемым. Если это не так, объект
будет перемещен.
mc1_mc.onEnterFrame = function() {
if (!this.dragging) {
this._x += this.xSpeed;
this._y += this.ySpeed;
}
};
Разумеется, xSpeed
и ySpeed еще не
определены и будут равны нулю, поэтому мы не
будем наблюдать никакого движения. Настроив их
вручную, мы увидим, что фильм перемещается до
щелчка на нем, после которого мы можем его
перетаскивать. Отпустив кнопку, мы увидим, что
фильм продолжит движение с той же скоростью и в
том же направлении, в котором он перемещался
изначально.
mc1_mc.xSpeed =
1;
mc1_mc.ySpeed = 1;
- Теперь нужно
определить, что происходит при перетаскивании.
Это выполняется в блоке
else после начального блока
if. В этом случае
мы можем проигнорировать имеющиеся переменные
скорости и перезаписать их. Мы знаем, что
xSpeed является
числом пикселей, на которое фильм переместится
горизонтально в одном кадре. Чтобы "бросить"
объект, мы просто направляем в обратную сторону
процесс перетаскивания. Определяем, насколько
далеко мы перетащили объект по горизонтали с
момента последнего кадра и присваиваем это
значение переменной
xSpeed.
mc1_mc.onPress
= function() {
this.startDrag();
this.dragging = true;
};
mc1_mc.onRelease = mc1_mc.onReleaseOutside = function () {
this.stopDrag();
this.dragging = false;
};
mc1_mc.onEnterFrame = function() {
if (!this.dragging) {
this._x += this.xspeed;
this._y += this.yspeed;
} else {
this.xSpeed = this._x-this.oldx;
this.ySpeed = this._y-this.oldy;
this.oldx = this._x;
this.oldy = this._y;
}
};
Рассмотрим код в обратном порядке. В конце
функции мы присваиваем текущее значение
_x переменной с
именем oldx. В
следующий раз мы выполняем функцию через долю
секунды. Мы незначительно переместили объект,
поэтому его значение _x
теперь несколько отличается от прежнего. Если
вычесть старое значение из нового, мы узнаем
расстояние, на которое переместился объект.
Присваиваем это значение переменной
xSpeed. Когда мы,
наконец, отпускаем кнопку мыши, возобновляется
выполнение кода для перемещения самого объекта с
использованием значения
xSpeed. Для значений
_y и
ySpeed строки кода
будут идентичны рассмотренным.
- Может
случиться, что объект, перемещаясь слишком
быстро, выйдет за пределы экрана и вовсе
исчезнет. Этот недостаток можно устранить,
применяя перенос экрана и трение. Сначала
используем перенос. Данный способ заключается в
том, что объект, уходя за пределы экрана, будет
снова появляться на противоположной стороне.
Такой прием использовался при создании многих
видеоигр. Что бы это сделать, нужно проверить
позицию и изменить ее, если она выходит за
пределы допустимого. Это будет происходить сразу
после обновления позиции объекта.
if
(this._x>Stage.width) {
this._x = 0;
} else if (this._x<0) {
this._x = 550;
}
if (this._y>Stage.height) {
this._y = 0;
} else if (this._y<0) {
this._y = 400;
}
- Добавление
трения постепенно, с каждым кадром, уменьшает
скорость объекта. Самым простым способом здесь
является умножение переменных скоростей на
величину вроде 0,98 в каждом кадре. Это можно
применить прямо перед добавлением скорости по
направлениям.
this.xspeed *=
.98;
this.yspeed *= .98;
Итак, независимо от того, с какой скоростью вы "бросили"
объект, он всегда будет замедляться так, что вы
снова сможете его захватить мышью. В следующей
лекции мы рассмотрим дополнение к этому подходу.
Окончательный код находится в файле
throwcursor.fla в
папке Chapter 6 на компакт-диске.
Поддержка событий
клавиатуры
Я
хотел бы рассказать еще лишь о двух событиях. Они
относятся к взаимодействию с клавиатурой. После их
изучения мы создадим фильм, который будет
использовать весь рассмотренный материал. Но сначала
хочу ознакомить вас с новой концепцией,
заключающейся в применении приемников событий.
Приемники событий
До сих
пор мы работали с фильмами, активностью кадров и
мышью. Все, что мы делали, это создавали функцию и
присваивали ее управляющим элементам фильмов для
событий, которые нужно было обрабатывать. Все
становится немного сложнее, когда начинают
использоваться события клавиатуры. Существуют два
события для поддержки клавиш:
- onKeyDown -
происходит при нажатии клавиши.
- onKeyUp -
происходит, когда клавиша отпускается.
Вероятно, вы уже об этом знаете, и даже наполовину
написали вашу функцию. Продолжим работу и посмотрим,
что произойдет.
mc1_mc.onKeyDown =
function() {
this._xscale = 200;
this._yscale = 200;
};
mc1_mc.onKeyUp = function() {
this._xscale = 100;
this._yscale = 100;
};
Этот
код идентичен тому коду, который вы писали для
onMouseDown и
onMouseUp. Так почему
же ничего не происходит при его выполнении и нажатии
клавиши? Потому, что mc1_mc
не является приемником для событий клавиш.
Ранее
мы приводили определение приемника. Теперь нам нужно
узнать о нем немного больше. Как мы говорили ранее,
приемник - это просто любой объект, который может
принимать уведомление об определенных типах событий
и выполнять какие-либо действия. Наши фильмы уже
могут обрабатывать такие события, как движение мыши,
нажатие и отпускание кнопки мыши, потому что фильм,
по умолчанию, является приемником для событий мыши.
Когда происходит событие мыши (движение или нажатие
кнопки), все фильмы уведомляются об этом и реагируют
соответствующим образом, если у них есть функция
поддержки событий для этого события. Они не обязаны
обрабатывать события, однако они могут это делать.
Но
фильмы не являются приемниками событий клавиш.
Независимо от того, нажмете ли вы клавиши слегка или
с силой ударите кулаком по клавиатуре,
mc1_mc никак не
отреагирует на эти события. Даже если вы присвоили
ему функцию поддержки обработки событий
onKeyDown или
onKeyUp, он никогда не
получит уведомление, и функция никогда не будет
выполнена.
Нужно
сделать наш фильм приемником для событий клавиш. Это
делается с помощью команды
Key.addListener (object). Каждый фильм Flash
имеет специальный объект с именем
Key. У этого объекта
есть параметры и методы (функции), которые
протоколируют нажатия клавиш, какие именно клавиши
были нажаты последними, какие клавиши нажаты в
данный момент, какие специальные клавиши применены и
т.д. Выражение
Key.addListener (mc1_mc) сообщает объекту
Key, что
mc1_mc будет
приемником для событий клавиш. Теперь каждый раз при
нажатии или отпускании клавиши
mc1 будет уведомляться.
Это похоже на присутствие в списке рассылки:
mc1 получает сообщение
и узнает, когда нужно выполнять функции.
mc1_mc.onKeyDown =
function() {
this._xscale = 200;
this._yscale = 200;
};
mc1_mc.onKeyUp = function() {
this._xscaie = 100;
this._yscaie = 100;
};
Key.addListener(mc1_mc);
Теперь
все должно работать так, как и предполагалось.
Важно
знать, что addListener
применим не только к фильмам. Любой объект может
быть приемником практически для любого типа события.
Это означает, что вы можете создать свой собственный
объект, который сможет принимать и обрабатывать
события мыши, клавиш, а также другие события.
Например, рассмотрим этот код.
myObj = new
Object();
myObj.onMouseMove = function() {
mc1_mc._x = _root._xmouse;
mc1_mc._y = _root._ymouse;
};
Mouse.addListener(myObj);
Видно,
что mc1_mc здесь
довольно пассивен. Он просто уведомляется о том, что
нужно делать, посредством
myObj, который обрабатывает события мыши
move. myObj может
принимать и поддерживать эти события, так как мы
сделали его приемником для всех событий мыши с
помощью команды
Mouse.addListener ().
Создание простой
игры
Существуют также другие приемники, например, для
текстовых полей, компонентов и даже для рабочего
места. Однако мы изучили все, что нам нужно для
достижения некоторых эффектных результатов при
работе с мышью и клавиатурой, поэтому немного
поэкспериментируем и создадим небольшую игру.
Эта
игра не станет бестселлером. Мы не будем терять
время на создание графики или специальных эффектов.
Вместо этого мы изучим основные принципы
использования управляющих элементов поддержки
событий для управления, анимации и взаимодействия с
графикой.
- В нашей игре
будет немного стрельбы, поэтому, прежде всего,
нужно создать прицел. Мы используем для этого
вертикальную и горизонтальную линии. Вы в
качестве прицела можете нарисовать то, что вам
больше нравится. Создайте прицел в фильме с
именем cursor и назовите инстанс именем
cursor_mc.
Всегда полезно держать код на отдельном слое,
поэтому создайте еще один слой и назовите его,
например, actions.
Щелкните на кадре 1 слоя
actions и откройте панель Aсtions. Теперь
вы увидите то, что сэкономит вам многие часы. В
правом верхнем углу панели Aсtions есть
небольшой значок, похожий на кнопку. Щелкните на
нем. Это кнопка Pin Current Script. После ее
нажатия панель Aсtions будет прикреплена к
сценарию, над которым вы работаете в данный
момент. Вы можете выбрать другие кадры, объекты,
кнопки, перейти в фильме на много уровней глубже,
но сценарий, с которым вы работаете, останется в
вашем окне в любом случае.
Ключевым элементом в успешном программировании
является централизация кода. Это означает, что
нужно располагать код в одном месте, вместо того,
чтобы вводить его в разных частях фильма. Весь
наш код будет находиться в кадре 1 слоя
Aсtions, который
будет прикреплен к панели
Aсtions, что позволит нам работать со
всеми графическими элементами фильма без потери
привязки к коду.
- Теперь
введите следующий код.
Mouse.hide();
cursor_mc.onEnterFrame = function() {
this._x += (_root._xmouse-this._x)/5;
this._y += (_root._ymouse-this._y)/5;
};
Сначала мы скрываем указатель мыши, поэтому все,
что мы теперь увидим - это
cursor_mc. Далее
мы присваиваем курсору функцию
onEnterFrame для
выполнения. Эта функция является простым
уравнением сокращения. В нем вычисляется разница
между указателем мыши (т.е. его позиция,
несмотря на то, что в данный момент он невидим)
и текущим положением
cursor_mc, после чего результат делится
на пять, и полученное значение прибавляется к
позиции курсора. Это обеспечивает плавное
движение курсора. (Это также несколько осложнит
прицеливание и, следовательно, сделает игру
интереснее!)
- Теперь,
безусловно, нам нужны боеприпасы. Я просто
нарисовал квадратик размером 4х4 пикселя и
поместил его в фильм с именем
bullet. Убедитесь,
что отмечена опция Export for AсtionScript, и
оставьте такое же имя
bullet для идентификатора экспорта. Если
после этого пуля все еще будет отображаться на
экране, удалите ее. Мы будем добавлять пули из
Library по мере необходимости.
- Мы будем
использовать клавиатуру для инициализации,
поэтому нам потребуется обработка событий
клавиатуры. Создадим триггер для фиксирования
этих событий. Затем мы превратим этот объект в
приемник для событий клавиатуры. Вот
соответствующий код.
trigger = new
Object ();
Key.addListener (trigger);
Здесь мы наблюдаем еще одно преимущество новой
модели событий. Во Flash 5 для обработки событий
клавиатуры нужно было использовать фильм или
кнопку. Распространенным решением в данном
случае было добавление пустого фильма на рабочее
место и ввод в него некоторого кода
onClipEvent (keyDown).
Это нарушает принцип централизации кода, и, что
еще хуже, пустой фильм отображается в виде
маленькой белой точки на рабочем месте. Если
поместить еще несколько таких объектов с
графикой на рабочее место, станет очень трудно
искать нужные части кода.
- Теперь мы
можем присвоить функцию.
trigger.onKeyDown =
shoot;
Имейте в виду, что можно присваивать функцию
перед ее непосредственным определением, до тех
пор, пока она определена в одном и том же кадре.
Flash автоматически прочтет функции в первую
очередь, независимо от их расположения в кадре,
и затем они будут доступны для любых выражений,
которые их вызывают. Как правило, проще всего
сначала назвать и присвоить функции для
обеспечения логической структуры программы, а
потом уже создавать функции с определенным кодом
- ведь мы обычно сначала рисуем квадрат, а потом
уже его закрашиваем.
- Теперь можно
приступить к определению нашей функции.
function
shoot() {
currBullet_mc = _root.attachMovie("bullet", "bullet"+i, i);
currBullec_mc._x = 275;
currBullet_mc._y = 400;
currBullet_mc.onEnterFrame = bulletMove;
i++;
if (i>9) {
i = 0;
}
}
Эта функция сначала присваивает инстанс
bullet к
_root. Она дает
инстансу имя "bullet"+i. Мы добавляем строку в
начале файла, приравнивающую
i к нулю. Таким
образом, наша первая пуля получит имя
bullet0. В конце
функции i увеличивается, поэтому при следующем
выстреле именем пули будет
bullet1. Мы также
используем i для
установки новой глубины фильма. Необходимо
увеличивать глубины "метких" пуль, так как два
фильма не могут существовать на одной и той же
глубине. Они также не могут иметь одинаковых
имен, если они находятся на одной и той же
временной диаграмме. После этого мы добавляем
небольшое выражение if.
Оно сбрасывает i
на ноль, если его значение превышает 9, поэтому
на экране никогда не будет одновременно больше
десяти пуль.
Далее мы устанавливаем два параметра. Если бы мы
знали полное имя фильма, мы могли бы
использовать обозначение точками и написать
_root.bullet0._x=275.
Но так как i будет каждый раз меняться, нам
нужно найти способ ссылки на это значение,
независимо от его величины. В дополнение к
обозначению точками, мы можем использовать
обозначение массивами, добавляя строку в скобках.
Строка является именем адресуемого объекта.
Преимуществом этого метода является то, что
строка может быть создана динамически.
Следовательно, мы можем написать
_root ["bullet"+i]
для ссылки на фильм, именем которого является
"bullet"+i. Однако
существует еще более удачный способ. Он
заключается в том, что при использовании
действия attachMovie
будет возвращаться ссылка на только что
примененный фильм. Она будет записываться во
временную переменную
currBullet_mc (для текущей пули). Это
позволит нам настраивать параметры без
надобности ввода всех скобок и кавычек. Этот
способ также несколько быстрее. Мы устанавливаем
для объекта _x и
_y на такие
значения, чтобы расположить его внизу посередине
экрана.
- Теперь нужно
применить к объекту некоторое движение. Мы
сделаем это с помощью управляющего элемента
onEnterFrame,
присвоенного фильму, следующим образом.
currBullet_mc.onEnterFrame = bulletMove;
- Мы будем
присваивать функцию и затем определять ее.
Приведем функцию
bulletMove.
function
bulletMove() {
if (!this.shot) {
this.xSpeed = (_root.cursor_mc._x-this._x)/20;
this.ySpeed = (_root.cursor_mc._y-this._y)/20;
this.shot = true;
}
this._x += this.xSpeed;
this._y += this.ySpeed;
}
Она проверяет переменную с именем
this.shot с
использованием оператора
"НЕ" - !. Это можно интерпретировать
следующим образом: "если НЕ выстрел (если
выстрел нулевой или не существует), то выполнить
следующее:". Так как мы еще не определили
shot, будет
возвращаться значение true и блок кода будет
выполняться. Будет вычисляться расстояние от
пули до курсора, после чего оно будет делиться
на 20, а результат будет использоваться для
установки значений xSpeed
и ySpeed. Далее
this.shot
устанавливается на значение "истина". Это
предотвратит повторное выполнение данного блока
кода. Затем значения
xSpeed и ySpeed
просто добавляются к значениям
_x и
_y пули.
- К данной
функции нужно сделать еще одно дополнение,
обеспечивающее удаления пули в случае, если она
переместилась слишком далеко, что позволит
избежать ненужных бесконечных вычислений позиции
пули. Реализуется это так.
if (this._y<0)
}
removeMovieClip(this);
}
Это действие будет выполняться сразу после
установки параметра _y
пули. Приведем код, который мы имеем на данный
момент.
Mouse.hide();
cursor_mc.onEnterFrame = function() {
this._x += _root._xmouse-this._x)/5;
this._y += _root._ymouse-this._y)/5;
};
trigger = new Ob]ect();
Key.addListener(trigger);
trigger.onKeyDown = shoot;
i = 0;
function shoot() {
currBullet_mc = _root.attachMovie("bullet", "bullet"+i, i);
currBullet_mc._x = 275;
currBullet_mc._y = 400;
currBullet_mc.onEnterFrame = bulletMove;
i++;
if (i>9) {
i = 0;
}
}
function bulletMove() {
if (!this.shot) {
this.xSpeed = (_root.cursor_mc._x-this._x)/20;
this.ySpeed = (_root.cursor_mc._y-this._y)/20;
this.shot = true;
}
this._x += this.xSpeed;
this._y += this.ySpeed;
if (this._y<0) {
removeMovieClip (this);
}
}
Пример
6.1.
До
сих пор все, что было известно при нажатии
клавиши - что какая-то клавиша была нажата, но
неизвестно, какая именно. Например, у нас на
экране могли бы находиться три отдельные пушки
вместо одной. Мы уже говорили, что объект
Key имеет
параметры для хранения обозначения нажатой в
данный момент клавиши, а также последней нажатой
клавиши. Мы будем использовать второй параметр.
У нас будет три пушки, управляемые клавишами z,
x и с, что немного напоминает старую добрую игру
70-х годов "Missile Command". Мы можем получать
данные двумя способами: в виде значения ASCII,
или в виде кода клавиши. Значения ASCII
привязаны к отдельным символам, а коды клавиш
привязаны к отдельным клавишам клавиатуры.
Например, заглавная буква "A" имеет код ASCII,
равный 65, и код клавиши, также равный 65. В
коде ASCII строчная буква "a" имеет значение 97.
Но так как она является физически той же самой
клавишей на клавиатуре, код клавиши по-прежнему
равен 65. Имейте в виду, что в разных странах
разные раскладки клавиатуры, и, следовательно,
разные коды клавиш.
- Мы можем
использовать широко распространенную таблицу
кодов ASCII для выяснения значений
z,
x и
c. Они равны,
соответственно, 122, 120 и 99. Введем их в
качестве констант в начале файла.
FIRELEFT = 122;
FIREMID = 120;
FIRERIGHT = 99;
- Теперь мы
используем выражение
switch для проверки значения нажатой
клавиши. Это новая возможность во Flash MX,
которая имеется в большинстве других языков
программирования. Проверяемое нами значение
указывается в скобках после ключевого слова
switch. После
этого мы используем последовательность выражений
типа "case", после которых указывается возможное
значение с точкой с запятой. Если проверяемое
значение соответствует данной величине,
последующие выражения будут выполнены. В
противном случае, будет осуществлен переход к
следующему выражению case
для проверки.
Обратите внимание на слово
break после
каждого блока кода. Как только
switch найдет
удовлетворяющее значение, последует попытка
выполнить весь последующий код, даже тот,
который находится в другой секции
case.
Использование break
заставляет Flash выйти из выражения
switch и
продолжить выполнение кода с данного места.
Также обратите внимание на секцию по умолчанию.
Она не обязательна, однако позволяет установить
для выполнения некоторые действия, если не будет
найдено удовлетворяющих значений. Приведем нашу
новую функцию shoot.
function
shoot() {
keyPressed = Key.getAscii() ;
switch(keyPressed){
case FIRELEFT:
startX = 50;
break;
case FIREMID:
startx = 275;
break;
case FIRERIGHT:
startX = 500;
break;
default:
startX = 0;
}
if(startX){
currBullet_mc = _root.attachMovie("bullet",
"bullet"+i, i);
currBullet_mc._x = startX;
currBullet_mc._y = 400;
currBullet_mc.onEnterFrarne = bulletMove;
i++;
if (i>9) {
i = 0;
}
}
}
Здесь мы присваиваем значение ASCII нажатой
клавиши переменной keyPressed. Затем мы
используем switch для проверки ее на
предопределенные значения
FIRELEFT, FIREMID,
FIRERIGHT. Если
получаем совпадение,
startX присваивается численное значение.
Если совпадение не найдено (в случае, если
нажата другая клавиша),
startX присваивается значение 0.
Если startX
присвоено некоторое ненулевое значение, следует
выстрел, как и ранее.
- Теперь у нас
есть три функции и несколько дополнительных
команд. Код стал несколько беспорядочным. Все
эти дополнительные команды выполняются только
один раз в начале фильма для его инициализации.
Поэтому мы соберем их вместе и поместим в одну
функцию с именем init.
function init()
{
Mouse.hide();
trigger = new Object();
Key.addListener(trigger);
trigger.onKeyDown = shoot;
i = 0;
FIRELEFT = 122;
FIREMID = 120;
FIRERIGHT = 99;
}
Теперь наш кадр состоит из четырех функций.
init()
cursor.onEnterFrame = function()
shoot()
bulletMove()
- Если нам
требуется найти определенный кусок кода, можно
вывести логическим путем, в каком месте он
находится. Но помните, что функция
init не будет
выполняться сама по себе. Поэтому нам нужно
добавить одну строку вверху кода.
init();
- Теперь нужно
создать цель для стрельбы! Я нарисовал
космический корабль и преобразовал его в фильм с
именем ship. Не
забудьте указать, что нужно выполнить экспорт
для AсtionScript, тоже с именем
ship, и удалить
все инстансы с рабочего места для инициализации.
Сейчас мы создадим функцию, которая будет
рисовать корабль.
function
createShip() {
currShip_mc = attachMovie("ship", "ship"+shipCounter,
shipCounter);
currShip_mc._x = 0;
currShip_mc._y = Math.random 0*300;
currShip_mc.speed = Math.random 0*10+5;
currShip_mc.onEnterFrame = shipMove;
shipCounter++;
}
Весь этот код вам уже знаком. Мы присвоили фильм,
установили его позиции _x
и _y, создали
переменную с именем speed, присвоили функцию
onEnterFrame и
увеличили переменную, используемую для имени
фильма и установки его уровня.
- Однако теперь
мы столкнемся с определенной проблемой. Сейчас
мы располагаем наш первый корабль на глубине 0 и
увеличиваем значение с нуля. Мы также
располагаем первую пулю на глубине 0 и так же
увеличиваем значение с нуля. В конечном счете
возникнет конфликт. Либо пуля попытается занять
глубину, на которой уже расположен корабль, либо
наоборот. Но так как известно, что глубина пули
всегда будет в пределах 0-9, мы можем решить эту
проблему, начав отсчет уровней корабля с 10. Это
можно сделать, добавив следующую строку кода в
функцию init.
shipCounter=10;
- Теперь, если
вы внимательно следили за происходящим, вы
сможете предсказать следующий шаг. Мы присвоили
функцию shipMove
нашему управляющему элементу корабля
onEnterFrame.
Сейчас нам нужно определить эту функцию. Мы уже
установили для нее случайную скорость. Нам нужно
просто использовать эту скорость для контроля
позиции корабля.
function
shipMove() {
this._x += this.speed;
}
- У нас есть
функция для создания корабля. Как и когда нужно
вызывать эту функцию? Нам придется делать это не
один раз, иначе у нас будет только один корабль.
Не следует делать это в функции
onEnterFrame, так
как через несколько секунд кораблей станет
слишком много. Нам нужно, чтобы каждый корабль
появлялся, скажем, каждые две секунды. Так как
это на самом деле не делается с помощью
обработки событий, мы реализуем это в качестве
бонуса (так как нам это нужно для
функционирования игры!).
Flash MX содержит очень полезную команду
setInterval. С ее
помощью можно указывать функции, что необходимо
выждать определенный промежуток времени перед
выполнением, и затем выполняться каждый раз по
прошествии этого интервала. Так как это
понадобится нам в начале фильма, введем команду
в функцию init.
setInterval (createShip,
2000);
Аргументами здесь являются выполняемая функция и
интервал ожидания. Обратите внимание, что здесь
мы опустили скобки в имени функции. Мы просто
сделали так, чтобы
setInterval содержал ссылку на саму
функцию. Интервал указывается в миллисекундах,
поэтому 2000 будет соответствовать интервалу в
две секунды. Когда мы выполним функцию
init,
setInterval
сообщит createShip
о необходимости ожидания в течение двух секунд,
после чего функция выполнится. С этого момента
функция будет выполняться каждые две секунды.
Несмотря на то, что мы не будем использовать
здесь эту функцию, я скажу несколько слов о
сопровождающем объекте для
setInterval -
clearInterval. Существует множество
обстоятельств, когда нужно, чтобы функция
выполнялась через интервал времени, но потом
прекращала свое выполнение.
clearInterval
(intervalID) используется для прекращения
выполнения этой функции. Как узнать ID
интервала? Он возвращается при выполнении
setInterval.
Следовательно, нужно ввести примерно следующий
код:
intervalID =
setInterval (createShip, 2000);
Теперь предположим, что нам нужно создать только
100 кораблей. Мы можем следить за числом
кораблей с помощью нашей переменной
shipCounter. С
учетом того, что
shipCounter начинается со значения 10, по
достижении значения 110 у нас будет 100
кораблей. Исходя из этого, нужно добавить в
функцию createShip
следующую функцию:
if (shipCounter
= = 110) {
clearInterval (shipIntervalID);
}:
Это предотвратит повторное выполнение
createShip. На
этом этапе можно установить новый интервал, в
котором createShip
будет выполняться каждые 1,5 вместо 2 секунд,
что ускорит действие игры. Оставляю эти
дополнительные настройки на ваше усмотрение!
Приведем весь созданный нами код.
init ();
function init() {
Mouse.hide();
trigger = new Object ();
Key.addListener(trigger);
trigger.onKeyDown = shoot;
i = 0;
FIRELEFT = 122;
FIREMID = 120;
FIRERIGHT = 99;
shipCounter = 10;
setInterval(createShip, 2000);
}
cursor_mc. onEnterFrame = function()
this._x += (_root._xmouse-this._x)/5;
this._y += (_root._ymouse-this._y)/5;
};
function shoot() {
keyPressed = Key.getAscii();
switch(keyPressed){
case FIRELEFT:
startX = 50;
break;
case FIREMID:
startX = 275;
break;
case FIRERIGHT:
startX = 500;
break;
default:
startX = 0;
}
if(startX){
currBullet_mc = _root.attachMovie ("bullet", "bullet"+i,
i);
currBullet_mc._x = startX;
currBullet_mc._y = 400;
currBullet_mc.onEnterFrame = bulletMove;
i++;
if (i>9) {
i = 0;
}
}
}
function bulletMove() {
if (!this.shot) {
this.xSpeed = (_root.cursor_mc._x-this._x)/20;
this.ySpeed = (_root.cursor_mc._y-this._y)/20;
this.shot = true;
}
this._x += this.xSpeed;
this._y += this.ySpeed;
if (this._y<0) {
removeMovieClip(this);
}
}
function createShip() {
currShip_mc = attachMovie("ship", "ship"+shipCounter,
shipCounter);
currShip_mc._x = 0;
currShip_mc._y = Math.Random()*300;
currShip_mc.speed = Math.random()*10+5;
currShip_mc.onEnterFrame = shipMove;
shipCounter++;
}
function shipMove() {
this._x += this.speed;
}
Пример
6.2.
Советую сейчас просмотреть каждую строку кода и
убедиться в понимании каждой команды и символа,
их назначения, а также их расположения в коде.
- Небольшим
недостатком нашей игры является то, что при
попадании пули в корабль ничего не происходит.
Поэтому мы добавим проверку попадания для
законченности игры. В следующей лекции мы уделим
много внимания обнаружению коллизий, поэтому мы
остановимся здесь лишь на общей функции
hitTest, которая
выглядит так.
movieclip1.hitTest
(movieclip2)
Нам необходимо постоянно проверять попадания,
поэтому лучше добавить эту функцию в блок кода
onEnterFrame. Так
как мы уже имеем управляющий элемент
onEnterFrame,
определенный для каждого корабля, это место как
нельзя лучше подходит для проверки на попадание.
Одновременно на рабочем месте будут
присутствовать до десяти пуль, и нам нужно
проверять корабль на попадание каждой пули. Это
легко осуществить с помощью цикла
for, динамически
создавая имя каждой пули. Если
hitTest возвратит
попадание, пуля будет просто удалена, а также
будет удален сам корабль. Функция
shipMove
становится такой.
function
shipMove() {
this._x += this.speed;
for (test=0; test<10; test++) {
if (this.hitTest(_root["bullet"+test])) {
removeMovieClip(_root["bullet"+test]);
removeMovieClip(this);
}
}
}
- Добавим
удаление всех кораблей, которые вышли за пределы
экрана. Используем произвольное большое число
для проверки того, что корабль вышел за пределы
экрана. Если это так, корабль будет удален.
function
shipMove() {
this._x += this.speed;
if(this._x > 700){
removeMovieClip(this);
}
for (test=0; test<10; test++) {
if (this.hitTest(_root["bullet"+test])) {
removeMovieClip(_root["bullet"+test]);
removeMovieClip(this);
}
}
}
Надеемся, что весь рассмотренный материал вам
понятен (и что ваша игра работает корректно!).
Это не самая захватывающая игра в мире, но она
дает вам возможность попрактиковаться в
программировании поддержки событий и несложных
функций. Вы также получили представление об
основной модели программирования. Играйте в вашу
игру и добавляйте в нее все новые и новые
возможности. Вы сможете добавить в нее столько
функций, сколько захотите, поддерживать любые
события, а также делать любую функцию настолько
простой или сложной, насколько это требуется.
Полный код игры вы можете найти в файле
shootemup.fla в
папке Chapter 6.
Наблюдатели
Последнее, о чем я хотел бы рассказать в этой лекции,
это совершенно новая возможность во Flash MX -
наблюдатели. Здесь под этим термином не
подразумевается человек, который наблюдает за
чем-либо. Наблюдатель следит за переменной или
параметром, и вызывает функцию при изменении
переменной. Хотя это и не является поддержкой
событий, данный процесс аналогичен ей в том, что
происходит ожидание какого-либо события, и затем
выполняется определенная функция.
Наблюдатели полезны в компонентах, таких как опции с
галочками. Опция может быть либо помечена, либо нет,
что интерпретируется как истинное или ложное
значение. Вам не нужно, чтобы опция производила
какие-либо действия до того, как она будет отмечена
и ее состояние будет изменено. Поэтому добавляется
наблюдатель переменной, содержащей это состояние.
Когда оно изменяется, функция вызывается и выполняет
нужные действия.
Вернемся к нашему эксперименту с перетаскиванием и
бросанием и проведем его заново с применением
наблюдателя. Мы использовали
onPress и onRelease/onReleaseOutside
для начала и окончания перетаскивания фильма. В то
же время мы устанавливали переменную с именем
dragging на значение "истина" или "ложь". Мы будем
отслеживать именно эту переменную. При ее изменении
мы будем переключать управляющий элемент
onEnterFrame фильма на
другую функцию.
Для
начала изучите синтаксис функции наблюдателя.
object.watch ("property",
function);
-
object - это объект,
фильм или временная диаграмма, где расположена
переменная.
-
"property" - это
параметр или переменная сама по себе. Обратите
внимание, что это строковое значение, а не
просто имя переменной.
-
function является
обращением к функции, которая должна выполняться.
watch будет
передавать функции следующие параметры.
- id
- имя параметра.
-
oldval - значение
параметра перед изменением.
-
newval - новое,
текущее значение.
Теперь
все становится несколько интереснее. Ранее мы
говорили, что нельзя применять параметры напрямую к
функции управляющего элемента событий. Этот факт
проявляется, когда управляющий элемент события
вызывается Flash, так как ему не передаются никакие
параметры.
В
случае с watch,
функции автоматически передаются три параметра. В
выражении watch
по-прежнему достаточно указать ссылку без скобок. Но
при определении самой функции необходимо указать
параметры, которые будут ей передаваться.
Вот
наше выражение watch.
mc1_mc.watch ("dragging",
dragHandler);
А вот
как нужно начинать определение нашей функции
dragHandler.
function
dragHandler(id, oldval, newval){
}
Сейчас
нам не требуется использовать все эти параметры, но
нам нужно определить нашу функцию с их
использованием. В большинстве случаев вы будете
работать только с параметром
newval. Кроме всего прочего, новое значение
параметра - это именно то, что нам интересно. Это
как раз относится к нашему примеру. Мы знаем, что
наша функция всегда вызывается на основе переменной
dragging, поэтому нам
уже известно, каким будет значение id. oldval также
в данном случае не играет никакой роли.
Также
необходимо учесть, что, как и в случае с функцией
поддержки события, мы используем данную функцию для
ссылки на данный объект. В функции
dragHandler будет
ссылка на mc1_mc.
Известо, что переменная
dragging может иметь значение "истина" или "ложь",
поэтому значение newval будет также иметь такой
формат. Мы будем выполнять одно действие, если
значением является "истина", и другое действие - в
противном случае.
function
dragHandler(id, oldval, newval) {
if (newval) {
this.onEnterFrame = drag;
} else {
this .onEnterFrame = move;
}
return newval;
}
Обратите внимание на то, что мы просто написали if
(newval):
Помните, что выражения if
всегда превращают содержимое в значение "истина" или
"ложь". Так как newval
уже имеет формат истина/ложь, нам не нужно выполнять
никаких дополнительных действий, например, сравнение,
как в этом случае: if (newval
= = true). Мы можем использовать данное
выражение прямо в таком виде, в каком оно есть.
Другим важным моментом, который нужно иметь в виду,
является то, что в конце действий в функции
наблюдения нужно добавлять строку
return newval. Хотя
это и не упоминается в документации, было выяснено,
что если опустить эту строку, переменная, переданная
функции наблюдения становится неопределенной! В
нашем случае это означает, что переменная
dragging, вместо того,
чтобы иметь значение "истина" или "ложь", будет
undefined. Возврат
значения newval решает
эту проблему.
Теперь
вместо одной большой функции для
onEnterFrame,
проверяющей состояние переменной
dragging много раз в
секунду, мы разбили наши действия на две различные
функции. Значение dragging
проверяется только тогда, когда изменяется, поэтому
здесь мы выиграли в производительности.
Теперь
осталось только определить наши новые функции
drag и
move. Это просто две
части нашей исходной функции
onEnterFrame. Приведем конечный код нашего
нового дополнения.
mc1_mc.onPress =
function() {
this.startDrag();
this.dragqing = true;
};
mc1_mc.onRelease = mc1.onReleaseOutside = function() {
this.stopDrag();
this.dragging = false;
};
mc1_mc.watch("dragging", dragHandler);
function dragHandler(id, oldval, newval) {
if (newval) {
this.onEnterFrame = drag;
} else {
this.onEnterFrame = move;
}
return newval;
}
function move() {
this.xspeed *= .98;
this.yspeed *= .98;
this._x += this.xspeed;
this._y += this.yspeed;
if (this._x>550) {
this._x = 0;
} else if (this._x<0) {
this._x = 550;
}
if (this._y>400) {
this._y = 0;
} else if (this._y<0) {
this._y = 400;
}
}
function drag() {
this.xspeed = this._x-this.oldx;
this.yspeed = this._y-this.oldy;
this.oldx = this._x;
this.oldy = this._y;
}
Пример
6.3.
Запустите этот код. Вы увидите, что мы создали
заново нашу программу по перетаскиванию и бросанию
так, что она теперь полностью основывается на
событиях. Вы можете найти законченную программу в
файле throwcursor_watches.fla
в папке Chapter 6 на компакт-диске.
Сейчас,
перед экспериментированием и реализацией ваших идей,
вы должны знать, что у вас нет возможности
устанавливать наблюдатели на большинство встроенных
параметров фильмов, таких как
_x и _y.
Внутренние методы, используемые для доступа к этим
параметрам, делают их несовместимыми с функцией
watch. Это более
детально описывается во Flash MX Reference по строке
поиска watch.
И
последнее. Аналогично сокращенному способу
присвоения функций поддержки событий, мы можем
использовать небольшой "ярлык" при написании функций
watch. Мы просто
определяем безымянную функцию и включаем ее прямо в
выражение watch.
Вместо
использования ссылки на функцию в выражении, мы
вставляем прямо в него определение функции (без
имени). Для большей ясности, в коде ниже выделена
соответствующая строка:
mc1_mc.watch("dragging",
function (id, oldval, newval){
if (newval) {
this.onEnterFrame=drag;
} else {
this.onEnterFrame=move;
}
}
А
здесь все написано так, как вы обычно будете видеть.
mc1_mc.watch ("dragging",
function (id, oldval, newval) {
Помните, что определение функции без имени
возвращает ссылку на эту функцию. Это удовлетворяет
требованиям второго аргумента
watch. Не важно, каким способом вы
воспользуетесь. Делайте так, как вам удобнее и
понятнее.
|