Опубликован: 07.11.2006 | Уровень: специалист | Доступ: платный
Лекция 8:

Обнаружение коллизий

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

Внимание! Для работы с этой лекцией необходимы учебные файлы, которые Вы можете загрузить здесь.

В "Интерфейсы AсtionScript" мы рассмотрели некоторые способы взаимодействия пользователя с объектами на экране. Теперь мы рассмотрим, как объекты могут реагировать друг на друга при обнаружении коллизий. Целая лекция, посвященная обнаружению коллизий? Да, поскольку мы расскажем о многих сопутствующих моментах, разобравшись в которых, вы будете прекрасно разбираться в коллизиях.

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

Метод hitTest

Сначала мы расскажем о простейшем из всех методов обнаружения коллизий - hitTest. Если вы читаете эту книгу, то, возможно, вам уже приходилось использовать hitTest. Так или иначе, мы пользовались этим методом в предыдущей лекции, однако существует несколько способов его использования, и сейчас мы их рассмотрим.

Есть два способа использования hitTest, причем второй имеет две возможности, итого получается три различных метода работы с hitTest. Мы рассмотрим их в порядке возрастания сложности.

Наиболее простая интерпретация hitTest, как вы помните из предыдущей лекции, такова.

movieClip1_mc.hitTest (movieClip2_mc);

Здесь просто проверяется факт коллизии двух фильмов. Не имеет совершенно никакого значения, на какой позиции находится каждый из фильмов. Если два фильма перекрывают друг друга, будет возвращено значение"истина", в противном случае - "ложь". Следовательно, hitTest используется почти исключительно в выражении if следующим образом.

if (movieClip1_mc.hitTest(movieClip2_mc)) {
  // do something
}

Граничные прямоугольники

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


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


Как можно обойти этот недостаток? Никак, если вы собираетесь использовать только данную версию hitTest. Это и есть та цена, которую вы платите за скорость и простоту использования данного метода. Хотя для многих приложений этот способ вполне годится. В небольшой игре-стрелялке, созданной нами в предыдущей лекции, все двигалось довольно быстро, и вы не заметили бы какие-либо неточности. Но представьте себе игру, например, Atari's Asteroids, где объекты могут двигаться довольно медленно. Вы собираетесь подвести ваш корабль близко к краю астероида, но тут он внезапно взрывается. "Я же его не касался!" - скажете вы. Да, как видите, эта версия hitTest не была бы хорошим решением в данной ситуации.

Вы наверняка использовали этот метод ранее, по крайней мере, в предыдущей лекции, поэтому мы не будем терять время на рассмотрение примеров с его использованием. Перейдем к следующему способу использования hitTest.

Использование shapeFlag

Приведем основной синтаксис для реализации hitTest с помощью shapeFlag.

movieClip_mc.hitTest (x, y, shapeFlag);

Я знаю, что вы сразу обратили внимание на параметр shapeFlag, но не торопитесь. Давайте прочтем выражение слева направо.

В начале мы видим movieClip_mc, который, очевидно, является именем проверяемого фильма. Заметьте, что мы в этот раз проверяем только один фильм. Итак, было ли попадание в этом фильме? Ответ является просто точкой. Эта точка определяется следующими двумя параметрами x и y. Здесь задаем вопрос, находится ли точка (x,y) внутри области фильма movieClip_mc.

Теперь переходим к параметру shapeFlag. С его помощью мы можем выбирать, нужно или не нужно использовать граничный прямоугольник фильма. Если shapeFlag установлен на значение "ложь" или 0, hitTest будет просто проверять, находится ли точка внутри фильма. Но если вы установите shapeFlag на значение "истина" или любое ненулевое численное значение, мы сможем выйти за рамки ограничений использования способа граничных прямоугольников и проверять, находится ли точка в видимой части фильма.

Да, этот способ намного эффективнее. Использование shapeFlag в качестве альтернативы методу hitTest довольно широко распространено. На самом деле я очень редко пользовался hitTest без shapeFlag.

Теперь все выглядит намного приличнее, однако прежде чем заключить, что все наши проблемы успешно решены, нужно выяснить, откуда берутся эти значения x и y. Вспомните, что с помощью первого рассмотренного нами способа фильм проверялся в сопопоставлении с другим фильмом. Мы можем имитировать это посредством использования одного фильма как фильма, выполняющего функцию, а параметры _x и _y второго фильма в качестве параметров x и y hitTest. Это будет выглядеть так.

movieClip1_mc.hitTest (movieClip2_mc._x, movieClip2_mc._y, true);

Посмотрев на следующий рисунок, вы поймете, с какой проблемой мы сейчас столкнемся.

Попадание не будет зарегистрировано, так как параметры x и y здесь ссылаются на центр movieClip2_mc, который не находится внутри нужной области попадания.


Итак, мы попали из огня да в полымя: Да, я люблю метафоры. Я хотел бы дать вам исчерпывающий совет по поводу того, как обойти эту проблему, однако так и не нашел поистине эффективного способа. Это задачка вроде поиска Св. Грааля: представить фильм с помощью shapeFlag для проверки попадания фильма. На самом деле выход существует всегда, поэтому не бейтесь головой о стену, думая, что вы упустили что-то очевидное. Я дам вам несколько советов, чтобы вам было легче.

  • Самый важный момент вполне понятен: всякие методы хороши в различных ситуациях, поэтому поэкспериментируйте со всеми тремя способами и выберите наиболее подходящий.
  • В большинстве случаев, если у вас есть большой и маленький объект, лучше всего использовать большой объект в качестве фильма, выполняющего hitTest, и маленький для предоставления параметров X и Y.
  • Если вы имеете дело с объектом неправильной формы, особенно если его размеры велики, лучше вызывать hitTest из этого объекта, так как вам нужна форма видимой области этого объекта.
  • Если у вас два более-менее прямоугольных объекта, используйте основной метод метод двух фильмов.

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

Игорь Хан
Игорь Хан

у меня аналогичная ситуация. Однако, если взять пример из приложения (ball_motion_04_click for trial.fla) то след остается. при этом заметил, что в моем проекте в поле "One item in library" виден кружок, в то время как в приложенном примере такого кружка нет.

Вопрос знатокам, что не так?

Александр Коргапольцев
Александр Коргапольцев

объект созданый мной упорно не желает оставлять след(единственное что добился, так это то что шарик резво гоняется за курсором) функция duplicateMovieClip остаётся не активной, т.е. следа от объекта не остаётся, но если я тоже самый код вбиваю в учебный файл всё работает, не могу понять где я ошибаюсь и почему в документе созданном заново, не работает код начиная от функции duplicateMovieClip? 

Тамара Ионова
Тамара Ионова
Россия, Нижний Новгород, НГПУ, 2009
Магомед Алисултанов
Магомед Алисултанов
Россия, Волгоград, лицей 2