Коллизии клипов. Метод hitTest()

Используя метод hitTest(), можно решить две задачи, имеющие исключительную важность для практики: проверить, не пересекаются ли два клипа, и определить, входит ли точка с определенными координатами в клип. Так как задачи эти достаточно разноплановы, рассмотрим их по отдельности.

Определение пересечения двух клипов
Чтобы определить, пересекаются ли два клипа, метод hitTest() необходимо задействовать в следующей форме:

clipl.hitTest(clip2),

здесь clip1 и clip2 — клипы, проверку коллизии которых нужно провести.

В качестве результата метод hitTest() возвращает булеву величину true, если клипы имеют общие точки, и false, если они не перекрываются.

В идеале факт пересечения двух клипов нужно считать состоявшимся, если у них есть хотя бы по одной заполненной точке, которые имеют одинаковые глобальные координаты. Увы, но проверить коллизию с такой точностью можно далеко не всегда. Для этого пришлось бы последовательно перебрать значительное число точек, что недопустимо с позиции времени выполнения кода. Поэтому метод hitTest() сравнивает не положения заполненных точек двух клипов, а лишь их граничные координаты (получить которые можно, используя метод getBounds()).


Это означает, что факт коллизии будет подтвержден не тогда, когда объекты внутренней структуры двух клипов действительно перекрывают друг друга, а тогда, когда пересекаются области, ограниченные описанными вокруг них прямоугольными рамками.Определение коллизий объектов произвольной формы — эта одна из самых сложных и, увы, пока до конца не решенных задач программирования во Flash. He существует универсального способа, используя который можно было бы с точностью до одной точки установить факт перекрывания двух клипов. Имеются лишь частные случаи, в которых коллизию можно определить достаточно надежно и не используя напрямую метод hilTest().

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

Во-вторых, повысить точность определения коллизий фигур округлой формы можно, введя для них условный радиус (проще говоря, считая их кругами). При этом проверка пересечения двух клипов сведется к определению расстояния между их центрами: если оно окажется меньше суммы условных радиусов, то коллизию нужно признать состоявшейся.

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


По контуру клипа расположить маленькие невидимые клипы-датчики. При необходимости установить коллизию нужно просто проверить, что возвращает метод hitTest() для каждого из датчиков. Если окажется, что один из них пересекается с соответствующим клипом, то факт коллизии должен быть подтвержден. Эффективность описанного подхода напрямую определяется количеством клипов-датчиков. Однако даже достаточно большое их число не позволяет узнать, перекрываются ли два клипа с точностью до пикселя. Впрочем, на практике клипы-датчики используются гораздо чаще, чем поточечный перебор, так как они дают вполне удовлетворительную точность определения коллизий при небольшом объеме требуемой для этого вычислительной работы.

В-четвертых, если клипы, факт коллизии которых нужно установить, имеют средние или небольшие размеры, то можно просто сравнить их поточечно.

Определение принадлежности точки к клипу
Чтобы проверить, принадлежит ли точка с некоторыми координатами клипу, нужно использовать метод hitTest() в следующей форме:

clip.hitTest(х, у, shapeFlag),
где:

• x и у — координаты точки. Должны быть заданы в системе координат основной временной диаграммы.


Если известна позиция точки лишь исходя из локальной системы координат клипа, то преобразовать соответствующие координаты в глобальные можно, воспользовавшись методом localToGlobaI();

• shapeFlag —булева величина, задающая, будет ли считаться точка принадлежащей клипу, если она действительно заполнена (true) или если она просто входит в область, ограниченную описанной вокруг объектов клипа прямоугольной рамкой (false).

Если принадлежность точки к клипу подтверждается, то метод hitTest() возвращает true. Иначе результат — false.

В качестве примера приведем код, который делает клип протаскиваемым, если по нему был произведен щелчок:

this.onMouseDown = function():Void {
if (mov.hitTest(_root._xmouse, _root. _ymouse, true)) {
mov.startDrag(true);
}
};

Обратите внимание, как была определена позиция, занимаемая на момент щелчка курсором мыши.

Для этого мы использовали специальные свойства _xmouse и _ymouse. Так как метод hitTest() требует задания координат точки относительно системы координат основной временной диаграммы, то данные свойства должны быть задействованы как свойства _root.


Надо признать, что факт принадлежности точки к клипу метод hitTest() определяет куда более качественно, чем наличие общих точек у двух клипов. Так, он позволяет узнать, действительно ли некоторая точка соответствует объекту внутренней структуры клипа, а не только, входит ли она в область, ограниченную описанным вокруг него прямоугольником. Чаще всего рассматриваемая форма метода hitTest() используется для определения того, не располагался ли некоторый клип под указателем мыши в момент нажатия ее левой клавиши. Начиная со Flash MX справиться с подобной задачей можно и проще благодаря тому, что клипы стали листенерами «кнопочных» событий. «Отловить» событие щелчка по клипу можно, использовав обработчик onPress. Так, приведенный выше пример при этом перепишется следующим образом:

mov.onPress = function():Void {
this.startDrag(true);
};

Единственным недостатком использования «кнопочных» событий является то, что при наведении на клип курсора он будет принимать форму руки. Чтобы этого не происходило, нужно набрать следующую строчку:

mov.useHandCursor=false;

Определение коллизии клипов с использованием поточечного перебора
Как было продемонстрировано выше, метод hitTest() абсолютно неэффективен, если необходимо определить наличие перекрывания между клипами, форма которых далека от прямоугольной.


Однако без инструмента, позволяющего с точностью до нескольких пикселей регистрировать наличие коллизии между клипами сложной формы, невозможно создание качественных игр и многих эффектов. В этом подразделе мы покажем, как можно создать такой инструмент, используя несколько простых идей и базовые средства ActionScript.

Что такой клип? Для наблюдателя это всего лишь множество точек на мониторе. Что такое коллизия клипов? Это когда у двух клипов имеются точки с одинаковыми координатами. А что, если просто последовательно сравнить все точки клипов — обнаружатся имеющие сходное расположение, значит, клипы пересекаются.

Flash не является растровым редактором. Поэтому пикселей, которые можно было бы выделять из изображения клипа и сравнивать, реально не существует. Но мы можем их имитировать. Для этого нужно разбить прямоугольник, описанный вокруг объектов клипа (его граничные точки определяем при помощи метода getBounds()), на маленькие квадратики. Если точка в центре такого квадратика заполнена (это можно узнать, используя метод hitTest()), то приближенно считаем, что заполнены все его точки. Последовательно перебрав все «пиксели», можно создать массив, хранящий виртуальный образ клипа. Точность соответствия образа оригиналу будет напрямую зависеть от того, насколько малые были заданы размеры для «пикселя».

Как поточечно «просканировать» клип, мы придумали. Но как можно сравнить два массива с образами клипов на предмет соответствия элементов, если они могут иметь различные размеры и совершенно несовпадающие точки отсчета? А обойти данную проблему очень просто. Для этого «сканироваться» оба клипа должны исходя из прямоугольника границ меньшего из них (это позволит минимизировать время расчета). Если клипы не пересекаются, то не окажется ни одного «пикселя», который бы оказался заполнен относительно их обоих. Если же обнаружится хотя бы один квадратик, точка центра которого принадлежит обоим клипам, то факт коллизии можно считать доказанным. Реализуется описанная идея следующим кодом:

// Создаем метод, по стилю использования схожий с hitTest()
MovieClip.prototype.myHitTest = function(clip:MovieClip, n:Number):Boolean {
// Параметр n задает размер "пикселя". Он необязателен - по умолчанию для
// него используем значение 2
if (n == undefined) {
var n:Number = 2;
}
// Определяем координаты граничных точек клипов. Так как в дальнейшем
// придется использовать их как параметры метода hitTest(), то вычислены они
// должны быть в системе координат _root.
var coord1:Object = this.getBounds(_root);
var coord2:Object = clip.getBounds(_root);
// Вычисляем площади клипов. У которого из них она окажется меньше,
// относительно того определяем коллизию
var squarel: Number =
Math.abs((coord1.xMax-coord1.xMin)*(coord1.yMax-coord1.yMin));
var square2:Number =
Math.abs((coord2.xMax-coord2.xMin)*(coord2.yMax-coord2.yMin));
var coord:Object = square1>square2? coord2: coord1;
// Перебираем все "пиксели"
for (var i = coord.xMin+n/2; i<=coord,xMath; i +=n) {
for (var j = coord.yMin+n/2; j<=coord.yMax; j += n) {
// Если один и тот же "пиксель" занят в обоих клипах, возвращаем true
if (((clip.hitTest(i, j, true)? 1: 0) + (this.hitrest [i, j, true)?
1: 0)) ==2) {
return true;
}
}
// Если общих "пикселей" не обнаруживается, возвращаем false
return false;
};

Несложно проверить, что в случае, если один из перекрывающихся клипов имеет не очень большие размеры, созданный метод очень эффективен. Для его выполнения требуется совсем немного времени, а наличие коллизии можно доказать со сколь угодно высокой точностью.

В качестве подтверждения работоспособности метода myHitTest() автором был создан небольшой эффект. Смысл его заключается в следующем. Имеются два клипа: один хранит стилизованное изображение рельефа, второй изображает мячик. Необходимо написать код, который задал бы движение мяча по «земле» с учетом всех ее неровностей. Основываясь на созданном выше методе, решить эту задачу чрезвычайно просто.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Action Script на эту тему:
Виртуальные слои клипов
Задание формулы цвета
Имена экземпляров клипов
Импорт внешних фильмов и изображений
Клипы как носители кода

Вернуться в раздел: Action Script / 10. Клипы