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

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

Математическое обнаружение коллизий

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

Что мы на самом деле ищем при проверке на коллизию? Коллизия - это столкновение или соприкосновение двух объектов.

Пусть у нас есть объект в определенном месте. Если другой объект находится в том же самом месте, тогда между ними происходит коллизия. Предположим, имеется стена, расположение которой _x=500. Если координата _x другого объекта больше 500, то он должен быть в состоянии столкновения со стеной, так? Да, поскольку мы решили, что у стены бесконечная высота, так как мы не проверяем значение _y. Самое замечательное заключается в том, что нам даже не нужно создавать стену. Мы просто говорим, что она там есть, посредством указания значений ее позиции. Самый простой способ - установить константу в начале фильма, к примеру right=500 ;. Давайте рассмотрим этот случай и попробуем с ним поработать.

Живой шарик

  1. Откройте новый фильм. Находясь в нем, установите левую стенку, реализуя ее в функции init. Переименуйте слой по умолчанию actions и введите следующий код.
    init();
     function init () Х
      right = 500;
      left = 50;
     }
  2. Теперь нужно реализовать некоторую коллизию с участием стенок. Начнем с простого шарика. Нарисуйте шарик, преобразуйте его в фильм с именем ball и дайте его инстансу имя ball_mc.
  3. Далее нужно обеспечить движение шарика. До этого мы использовали термин speed. Здесь мы поступим несколько более точно с технической точки зрения и используем velocity. Velocity - это скорость в определенном направлении. Мы ограничим возможные направления правой и левой сторонами, поэтому нашей скоростью Velocity будет либо +speed, либо -speed. Мы назовем ее velX, в виде сокращения от "Velocity по оси x". Добавим нужный код в init.
    init ();
     function init() {
      right = 500;
      left = 50;
      ball_mc. velX = 5;
     }
  4. Для функции реализации движения шарика используем управляющий элемент onEnterFrame. Присвоим функцию управления в секции init и после этого создадим функцию move. Добавьте следующий код в имеющуюся программу.
    init ();
      function init()  {
      right = 500;
      left = 50;
      ball_mc.velX = 5;
      ball_mc.onEnterFrame = move;
     }
     function move() {
      this._x += this.velX;
     }

    Здесь берется скорость velocity и прибавляется к позиции _x. Так как velX = 5, эта функция прибавляет 5 к позиции _x шарика в каждом кадре. Запустив этот фильм, вы увидите, что шарик переместится вправо и исчезнет. Итак, сейчас мы займемся обнаружением столкновений с использованием стенки.

  5. Вспомните нашу прежнюю проверке коллизий с точкой: контур всегда больше той точки, в которой он находится. При проверке удара объекта о стенку нужно учитывать толщину объекта. Предположим, что шарик имеет диаметр 20 пикселей. Это означает, что край шарика находится в 10 пикселях от центра (если шарик имеет центральную точку регистрации). Мы можем принимать это в расчет при проверке позиции шарика относительно стенки. Код будет выглядеть примерно так.
    if (this._x>right-(this._width/2)) {
      // then we have a hit
     }

    Мы добавим этот код в функцию move сразу после того, как настроим позицию шарика.

    Берем параметр _width фильма ball и делим его на два. Это даст расстояние от центра круга до его края (т.е. радиус). Имейте в виду, что это будет работать только в случае с объектами, у которых имеется центральная точка регистрации, и которые отцентрированы по оси x.


  6. Ударившись о стенку, шарик отскакивает. В нашем случае он двигается строго перпендикулярно к поверхности, поэтому после удара просто будет двигаться в обратном направлении. Другими словами, если до столкновения его скорость равна +5, после столкновения она будет равна -5. Вместо реализации скорости -5 мы просто будем умножать любую скорость на -1. Давайте добавим эту функциональность в наш код в функции move.
    function move() {
      this._x += this.velX;
      if (this._x>right-(this._width/2)) {
       this.velX *= -1;
      }
     }
  7. Теперь можном немного изменить этот код, чтобы создать аналогичное выражение if для проверки столкновения между шариком и левой стенкой. Добавьте следующий новый код в функцию move.
    if (this._x<left+(this._width/2)) {
      this.velX *= -1;
      }
  8. Вот весь код, который мы имеем на данный момент.
    init();
      function init() {
      right = 500;
      left = 50;
      ball_mc.velX = 5;
      ball_mc.onEnterFrame = move;
      }
      function move() {
      this._x += this.velX;
      if (this._x>right-(this._width/2)) {
        this.velX *= -1;
      }
      if (this._x<left+(this._width/2)) {
        this.velX *= -1;
      }
      }
  9. Вы можете нарисовать линии на позициях 50 и 500 по оси x, чтобы видеть, что именно происходит. Помните, что эти линии являются лишь графическими элементами, которые не взаимодействуют с кодом и действием в фильме. Проверка коллизий - процесс сугубо математический.

  10. Запустив фильм, вы увидите, что шарик скачет туда и обратно между двумя линиями. Если у вас достаточно хорошее зрение, вы заметите нечто довольно неприятное. Шарик на самом деле немного проникает в стену, прежде чем отскочить от нее.

    Если вы не видите этого, уменьшите либо частоту кадров фильма, либо значение скорости. Когда вы бросаете мяч в направлении стены, он ведь не проникает в нее, а просто отскакивает! (Имеется в виду обычный человек, бросающий в стену обычный теннисный мячик, а не Арнольд Шварценеггер, кидающий железное ядро). Сейчас мы постараемся решить эту проблему.

  11. Мы видим, что при ударе о стенку шарик немного проникает внутрь стены. Это показано на рисунке выше. Вручную переместим шарик так, чтобы он лишь касался края стены. Внесите следующие изменения в функцию move (функция init остается прежней)
    function move() {
        this._x += this.velX;
        if (this._x>right-(this._width/2)) {
          this.velX *= -1;
          this, x = right-(this, width/2);
        }
        if (this._x<left+(this._width/2)) {
          this.velX *= -1;
          this._x = left+(this._width/2);
        }
      }
  12. Если вы запустите фильм сейчас, шарик немного войдет в стенку, будет зарегистрировано столкновение, и шарик переместится к краю стенки. Но, так как Flash не обновляет экран, пока не выполнится весь код, мы на самом деле никогда не увидим, как шарик проникнет в стенку, а будем наблюдать лишь касание. Итак, одна из "странных" особенностей функционирования Flash была использована на наше же благо!
  13. Теперь я совершу "квантовый скачок" и добавлю в фильм совершенно новое измерение. Я продублирую строки, содержащие _x или velX с использованием _y и velY, а также добавлю новые переменные с именами top и bottom. Также _width будет заменено на _height. Нарисуйте еще две линии вверху и внизу рабочего места на позициях (x,50) и (x, 350) для отображения стенок. Следующий код не представляет проблем.
    init();
      function init() {
        right = 500;
        left = 50;
        top = 50;
        bottom = 350;
        ball_mc.velX = 5;
        ball_mc.velY = 5;
        ball_mc.onEnterFrame = move;
      }
      function move() {
        this._x += this.velX;
        this._y += this.velY;
        if (this._x>right-(this._width/2)) {
          this.velX *= -1;
          this._x = right-(this._width/2);
        }
        if (this._x<left+(this._width/2)) {
          this.velX *= -1;
          this._x = left+(this._width/2);
        }
        if (this._y>bottorn-(this._height/2)) {
          this.velY *= -1;
          this._y = bottom-(this._height/2);
        }
        if (this._y<top+(this._height/2)) {
          this.velY *= -1;
          this._y = top+(this._height/2);
        }
      }
    Пример 7.1.
  14. Запустите фильм. Теперь все это напоминает старый добрый пинг-понг, в который я играл еще ребенком. Ну, раз уж на то пошло, давайте доведем дело до конца.
Игорь Хан
Игорь Хан

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

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

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

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

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