Пусть центры кругов находятся в точках (x1,y1) и (x2,y2), а их радиусы равны r1 и r2. Круги пересекаются, если расстояние между их центрами
меньше, чем сумма радиусов r1+r2.
if ( clip1.hitTest(clip2) ) trace ("Клипы пересекаются!");Здесь clip1 и clip2 имена двух клипов, которые задаются на панели Properties. Метод hitTest возвращает true («да») в том случае, когда прямоугольники, ограничивающие клип, пересекаются. То же самое можно было записать иначе, поменяв местами имена клипов:
if ( clip2.hitTest(clip1) ) trace ("Клипы пересекаются!");Мы изучим эту функцию на простом примере парковки автомашины (см. ниже). Желтая машина управляется клавишами-стрелками, задача припарковаться между машин в левой или в правой части стоянки.
Откройте файл PRACTICE\10\parking.fla, выделите желтую машину и добавьте к ней код двух обработчиков,
обеспечивающих движение при нажатии клавиш-стрелок:
onClipEvent (load) { v = 5; function move(d) { r = _rotation * Math.PI / 180; _x += d * Math.sin(r); _y -= d * Math.cos(r); } } onClipEvent (enterFrame) { if (Key.isDown(Key.UP)) move(v); else if (Key.isDown(Key.DOWN)) move(-v); if (Key.isDown(Key.LEFT)) _rotation -= 5; else if (Key.isDown(Key.RIGHT)) _rotation += 5; } |
Добавьте в обработчик события load строчки
_xOld = _x; _yOld = _y; _rotOld = _rotation; |
В самое начало обработчика enterFrame добавьте код:
check(); _xOld = _x; _yOld = _y; _rotOld = _rotation; |
Добавьте в конец обработчика load код функции check:
function check() { for (i = 1; i <= 4; i++) if (this.hitTest(_root["car" + i])) { _x = _xOld; _y = _yOld; _rotation = _rotOld; break; } } |
Добавим проигрывание звука при столкновении.
Дайте звуку crash.wav из библиотеки имя crash и добавьте в нужное место функции check
код
snd = new Sound(); snd.attachSound("crash"); snd.start(0, 1);Проверьте работу клипа. |
Посмотрев на исходное расположение машин, можно ожидать, что определение пересечения по границам прямоугольников будет довольно точным, поскольку все машины достаточно плотно заполняют отведенное им место. Однако при проверке клипа вы обнаружите, что столкновение происходит даже тогда, когда машины довольно далеко друг от друга.
Дело в том, что функция hitTest использует ограничивающий прямоугольник, стороны которого параллельны осям координат. При повороте машины эта область значительно расширяется, что мы и увидим далее. Для этого создадим новый клип-прямоугольник и будем изменять его положение и размеры так же, как размеры активной зоны желтой машины.
Создайте новый символ типа Movie Clip с именем Прямоугольник, нарисуйте красный прямоугольник без заливки так, чтобы его левый верхний угол находился в точке регистрации (где стоит крестик при редактировании символа). Выделите контур двойным щелчком и установите на панели Properties толщину линии 1 пиксель. |
Сначала надо подготовить клип дать ему имя.
Дайте символу Прямоугольник из библиотеки кодовое имя rectangle для использования в программе (команда
Linkage в контекстном меню). Добавьте в обработчик
события load для машины строчку
_root.attachMovie("rectangle", "rect", _root.getNextHighestDepth() ); |
Границы прямоугольника для любого клипа можно определить с помощью метода getBounds. Например, границы клипа qq на главном монтажном столе определяются так:
bounds = qq.getBounds(_root);В переменную bounds будет помещен объект, у которого есть 4 свойства: xMin, xMax, yMin и yMax, определяющие предельные значения области по осям X и Y. Разница xMax-xMin дает ширину прямоугольника, а yMax-yMin его высоту.
Добавьте этот код в конец обработчика события enterFrame для машины:
bounds = this.getBounds(_root); with (_root.rect) { _x = bounds.xMin; _y = bounds.yMin; _width = bounds.xMax - bounds.xMin; _height = bounds.yMax - bounds.yMin; }Сохраните файл и проверьте его работу. |
_root.rect._x = bounds.xMin; _root.rect._y = bounds.yMin; _root.rect._width = bounds.xMax - bounds.xMin; _root.rect._height = bounds.yMax - bounds.yMin;Использование with(...) сокращает запись, но может усложнить понимание кода.
Здесь используется еще один вариант вызова стандартной функции hitTest:
qq.hitTest ( x, y, true )Эта функция определяет, попадает ли точка с координатами x и y (это глобальные координаты, для главного монтажного стола) в область клипа с именем qq. Третий параметр, равный true, означает, что надо учитывать форму фигуры внутри клипа, то есть попадание точки в пустые области «не считается».
Если третий параметр будет равен false, функция определяет, попала ли точка в прямоугольник, ограничивающий клип.
Откройте файл PRACTICE\10\valaam.fla. Преобразуйте карту Валаамского архипелага в клип Карта (клавиша F8, тип Movie Clip). Дайте экземпляру клипа имя map. |
Создайте новый слой Точка и перетащите на него клип Точка из библиотеки.
С помощью панели Properties установите высоту и
ширину объекта 10 пикселей.
Добавьте к клипу код, позволяющий двигать точку по экрану
клавишами-стрелками
(как на предыдущем уроке):
onClipEvent (load) { v = 2; function move ( d ) { r = _rotation * Math.PI / 180; _x += d * Math.sin(r); _y -= d * Math.cos(r); } } onClipEvent (enterFrame) { if (Key.isDown(Key.UP)) _y -= v; else if (Key.isDown(Key.DOWN)) _y += v; if (Key.isDown(Key.LEFT)) _x -= v; else if (Key.isDown(Key.RIGHT)) _x += v; }Проверьте ролик. |
Добавьте в обработчик события load строчки
_xOld = _x; _yOld = _y;и функцию для процерки столкновения с картой: function check() { if ( _root.map.hitTest(this._x, this._y, true) ) { _x = _xOld; _y = _yOld; } }В самое начало обработчика enterFrame вставьте вызов функции check();Посмотрите, как двигается точка. |
Карта (рисунок map.gif в библиотеке) это файл в формате GIF, где область воды прозрачная. Это легко проверить, если сменить фон клипа с синего на какой-нибудь другой. Поэтому можно было ожидать, что функция hitTest не будут фиксировать попадание в эти прозрачные области. Но этого не случилось точка не может войти в прямоугольник, ограничивающий карту.
Перейдите двойным щелчком а режим редактирования клипа Карта. Выберите команду меню
ModifyBitmapTrace Bitmap... и преобразуйте
рисунок в векторную форму при следующих параметрах:
Перейдите к редактированию сцены и просмотрите результат. |
Теперь рисунок map.gif можно удалить из библиотеки, чтобы уменьшить размер файла. Вместо него в клипе Карта используется векторная фигура, полученная в результате трассировки. |
На рисунке в скобках для каждой точки указаны ее координаты относительно центра (смещения dx и dy). Учитывается, что на экране ось Y направлена вниз.
Для того, чтобы можно использовать цикл для перебора точек, их смещения предварительно записаны в два массива. Радиус круга r определяется как половина ширины клипа.
Добавьте в обработчик load два массива координат точек для проверки:
r = _width / 2;
dx = [0, 0.707*r, r, 0.707*r, 0, -0.707*r, -r, -0.707*r];
dy = [r, 0.707*r, 0, -0.707*r, -r, -0.707*r, 0, 0.707*r];
и измените функцию check таким образом:
function check() { for (i=0; i<8; i++) if ( _root.map.hitTest(_x+dx[i], _y+dy[i], true) ) { _x = _xOld; _y = _yOld; break; } }Сохраните фильм и проверьте его работу. |
В следующем примере мы будем использовать второй способ. Машинка управляется клавишами-стрелками, задача доехать до финиша. При касании камней слышится звук удара.
Откройте файл PRACTICE\10\stones.fla и проверьте его работу. |
По границам камней стоят значки красные точки. Это символы типа Точка из библиотеки, которые определяют возможные точки столкновения машины с камнем.
Двойным щелчком войдите в режим редактирования четвертого камня, вокруг которого нет точек. Перетащите из библиотеки на сцену клип Точка и расставьте точки по границе камня (для копирования клипа перетаскивайте его при нажатой клавише Alt). |
for ( x in объект ) { ... }Здесь вместо слова объект нужно указать абсолютный или относительный адрес объекта. Например, код
for ( x in _root ) trace( _root[x]._name );выводит в окно Output имена всех объектов главного монтажного стола (они определяются на панели Properties). Вместо x можно использовать любое другое имя переменной.
Кроме клипов, к объекту могут присоединяться и другие элементы (например, функции). Определить класс объекта можно с помощью оператора typeof.
Создайте новый слой Программа и добавьте в первый ключевой кадр код
for ( x in _root ) trace( "Объект " + _root[x] + ", класс: " + typeof _root[x] + ", имя: " + _root[x]._name );Запустите фильм. |
Объект WIN 9,0,45,0, класс: string, имя: undefined Объект _level0.finish, класс: movieclip, имя: finish Объект _level0.car, класс: movieclip, имя: car Объект _level0.stone4, класс: movieclip, имя: stone4 Объект _level0.stone3, класс: movieclip, имя: stone3 Объект _level0.stone2, класс: movieclip, имя: stone2 Объект _level0.stone1, класс: movieclip, имя: stone1Первая строчка определяет версию Flash-проигрывателя (в виде символьной строки, string). В данном случае используется версия 9.0.45.0 для Windows. Остальные строчки описывают клипы, находящиеся на главном монтажном столе (на уровне 0, отсюда _level0 здесь это то же самое, что и _root).
Номер версии Flash-проигрывателя можно получить и иначе, с помощью кода
trace(_level0.$version);
|
Выведите в окно Output информацию о внутренних объектах для камня _root.stone1. |
Теперь можно написать код, с помощью которого строится массив объектов-точек для каждого камня.
Выделите камень в левом нижнем углу (его имя stone4) и добавьте к нему код
обработчика события load:
onClipEvent (load) { points = new Array(); for (x in this) if ( typeof this[x] == "movieclip" && this[x]._name.indexOf('instance') >= 0 ) points.push(this[x]); } |
Теперь нужно написать функцию проверки столкновения: она будет возвращать true при столкновении (если хотя бы одна из точек попадает в область клипа car) и false, если столкновения нет. Для этого используется «точечная» форма метода hitTest, как и в предыдущем разделе.
Добавьте к обработчику load для клипа-камня код функции hits:
function hits ( obj ) { var p = new Object(); for ( i=0; i<points.length; i++) { p.x = points[i]._x; p.y = points[i]._y; this. localToGlobal ( p ); if ( obj.hitTest(p.x, p.y, true) ) { this.points[i].play(); return true; } } return false; } |
В то же время для функции hitTest надо задать абсолютные (глобальные) координаты на монтажном столе. Для перевода локальных координат в глобальные используется метод клипа localToGlobal. Он принимает объект p, имеющий свойства x и y. Глобальные координаты возвращаются в том же объекте.
Существует также и метод, выполняющий обратное преобразование, он называется globalToLocal. |
Если очередная точка попала в область клипа obj, начинается проигрывание клипа Точка. Если посмотреть «внутрь» этого клипа, видно, что при проигрывании (начиная с кадра 2) параметр Alpha цвета точки (непрозрачность) постепенно меняется от 0 до 100% и снова до нуля (работает анимация формы). Одновременно проигрывается звук удара.
Теперь нужно добавить реакцию на столкновение в код клипа car (желтая машина). Как и в предыдущем примере, мы будем запоминать предыдущее положение машины и, если произошло столкновение, возвращать машину в это место.
Выделите машину и добавьте в обработчик события load код
_xOld = _x; _yld = _y; _rotOld = _rotation;и функцию function check(){ if ( _root.stone4.hits(this) ) { _x = _xOld; _y = _yOld; _rotation = _rolOld; } }В самое начало обработчика enterFrame вставьте вызов этой функции и запоминание новых координат check(); _xOld = _x; _yOld = _y; _rotOld = _rotation;Проверьте клип (машина не должна заезжать на выбранный камень). |
Прототип это описание общих свойств всех экземпляров (объектов) какого-то класса. Все камни относятся к классу MovieClip, и если мы добавим функции к прототипу, их смогут использовать все объекты этого класса.
Выделите кадр 1 слоя Программа, удалите весь старый код и добавьте новый:
MovieClip.prototype.onLoad = function() { this.points = new Array(); for (x in this) { if ( typeof this[x] == "movieclip" && this[x]._name.indexOf("instance") >= 0 ) this.points.push(this[x]); } } MovieClip.prototype.check = function(obj) { var p = new Object(); for (i = 0; i < this.points.length; i++) { p.x = this.points[i]._x; p.y = this.points[i]._y; this.localToGlobal(p); if ( obj.hitTest(p.x, p.y, true)) this.points[i].play(); return true; } return false; } |
//
Кроме того, надо изменить функцию check для клипа-машины: теперь она должна проверять все 4 камня. Учитывая, что их имена stone1, stone2, stone3 и stone4, эту проверку можно сделать в цикле.
Измените функцию check для клипа-машины так:
function check() { for (i=1; i<=4; i++) if (_root["stone"+i].check(this) ) { _x = _xOld; _y = _yOld; _rotation = _rotOld; return; } }Выделив поочередно все камни, добавьте в их блок кода строчку комментария: //Проверьте ролик. |
Понятно, что начальное положение машины надо запомнить в обработчике load. Для этого введем переменные _x0, _y0 и _rot0.
Добавьте в начало обработчика load код
_x0 = _x; _y0 = _y; _rot0 = _rotation; |
Присвойте звуку cheers.wav из библиотеки кодовое имя cheers (пункт Linkage... в
контекстном меню). Добавьте в конец функции check
код для проигрывания звука при пересечении клипов
car и finish:
if ( this.hitTest(_root.finish) ) { snd = new Sound(); snd.attachSound("cheers"); snd.start(0,1); } |
Добавьте после строчки snd.start(0,1); код
snd.onSoundComplete = function() { _x = _x0; _y = _y0; _rotation = _rot0; } |
Дело в том, что функция check вызывается при каждой смене кадра, то есть 50 раз в секунду (для данного клипа). Причем каждый раз запускается новая копия звука, которая смешивается с предыдущими.
Чтобы исправить ситуацию, надо как-то запомнить, что звук уже проигрывается, и не запускать новую копию. Для этого используем новую логическую переменную soundOn. Она будет равна true, когда звук играет.
Добавьте строчку
soundOn = true;
после команды snd.start(0,1);. В начале обработчика
load и в обработчике onSoundComplete
вставьте строчки
soundOn = false;
Просмотрите результат и сохраните файл.
|
Математика и физика | Внешние файлы |
© 2007 К. Поляков