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

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

Учет размеров объекта
  1. Откройте новый фильм. Создайте круглый фильм, который и будем использоваться (я создал круг диаметром 100 пикселей). Назовите его ball и отметьте опцию Export for ActionScript в окне Linkage Properties. Введите идентификатор связи ball.
  2. Сохраните копию шарика на рабочем месте и назовите инстанс player_mc.
  3. Теперь создадим в игре ряд препятствий-астероидов. Мы, как обычно, реализуем это в функции init с помощью цикла for. В нем мы присваиваем препятствиям случайные позиции, скорости и размеры, азатем присваиваем управляющий элемент onEnterFrame. Какой продуктивный цикл! Добавим все это в отдельный слой с именем actions.
    init();
      function init() {
        for (i=0; i<10; i++) {
          ball_mc = attachMovie("ball", "b"+i, i);
          ball_mc._x = Math.random()*550;
          ball_mc._y = Math.random()*400;
          ball_mc.velX = Math.random()*10-5;
          ball_mc.velY = Math.random()*10-5;
          ball_mc._xscale = Math.random()*40+10;
          ball_mc._yscale = Math.random()*40+10;
          ball_mc.onEnterFrame = ballMove;
        }
      }
  4. Далее определим функцию ballMove.
    function ballMove() {
        this._x += this.velX;
        this._y += this.velY;
        if (this._x>550) {
          this._x = 0;
        }
        if (this._y<0) {
          this._x = 550;
        }
        if (this._y>400) {
          this._y = 0;
        }
        if (this._y<0 {
          this._y = 400;
        }
      }

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

  5. Создадим управляющий элемент player_mc.onEnterFrame. Сначала присвоим его в конце функции init.
    player_mc.onEnterFrame = playerMove;
  6. Теперь определим саму функцию под всем текущим ActionScript. Это будет обычная функция приближения к курсору мыши с довольно сильным трением.
    function playerMove() {
        this._x += (_root._xmouse-this._x)/20;
        this._y += (_root._ymouse-this._y)/20;
      }
  7. Запустив фильм, вы увидите случайный набор медленно двигающихся кругов и большой круг, следующий за курсором мыши. Оживим эту игру, добавив в нее обнаружение коллизий. Это реализуется с помощью точно такого же кода, как и в упражнении с теоремой Пифагора, нужно лишь вставить имена инстансов. Мы добавим этот код в функцию ballMove, прямо в ее конец. Теперь каждый шарик будет проверять сам себя на столкновение с player_mc в каждом кадре.
    dx = player_mc._x-this._x;
      dy = player_mc._y-this._y;
      dist = Math, sqrt (dx*dx+dy*dy);
      if (dist<player_mc._width/2+this._width/2) {
        player_mc._visible = false;
      }

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

    player_mc. onMouseDown = function() {
        this._visible = true;
      };
  8. Запустите ваш фильм и попробуйте как можно дольше избежать столкновения с астероидами. Ниже приведен окончательный код игры. Вы можете экспериментировать с этим кодом до тех пор, пока не получите нечто более интересное.
    init();
      function init() {
        for (i=0; i<10; i++) {
          ball_mc = attachMovie("ball", "b"+i, i);
          ball_mc._x = Math.random 0*550;
          ball_mc._y = Math.random 0*400;
          ball_mc.velX = Math.random 0*10-5;
          ball_mc.velY = Math.random 0*10-5;
          ball_mc._xscale = Math.random()*40+10;
          ball_mc._yscale = Math.random()*40+10;
          ball_mc.onEnterFrame = ballMove;
          player_mc.onEnterFrame = playerMove;
          player_mc. onMouseDown = function() {
            this._visible = true;
          };
        }
      }
      function ballMove() {
        this._x += this.velX;
        this._y += this.velY;
        if (this._x>550) {
          this._x = 0;
        }
        if (this._y<0  {
          this._x = 550;
        }
        if (this._y>400) {
          this._y = 0;
        }
        if (this._y<0) {
          this._y = 400;
        }
        dx = player_mc._x-this._x;
        dy = player_mc._y-this._y;
        dist = Math.sqrt(dx*dx+dy*dy);
        if (dist<player_mc._width/2+this._width/2) {
          player_mc._visible = false;
        }
      }
      function playerMove() {
        this._x += (_root._xmouse-this._x)/20;
        this._y += (_root._ymouse-this._y)/20;
      }
    Пример 7.4.
Коллизии множества объектов

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

До сих пор мы рассматривали столкновение одного объекта с другим объектом или столкновение одного объекта с несколькими объектами. В игре, в которой мы стреляем по космическим кораблям пришельцев, корабли используют цикл for для проверки всех снарядов по очереди. Как вариант, можно было бы реализовать в снарядах проверку попадания во все корабли по очереди, однако здесь нам бы потребовалось знать количество активных в данный момент кораблей, а также их имена. Это возможно, но первый способ проще.

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

for (i=0; i<10; i++) {
    dx = this._x-_root["b"+i]._x;
    dy = this._y-_root["b"+i]._y;
    dist = Math.sqrt(dx*dx+dy*dy);
    if (dist<this._width/2+_root["b"+i]._width/2) {
      this._visible = false;
    }
  }

Этот вариант работает отлично, однако мы обеспечиваем лишь проверку одного объекта относительно одного, или одного относительно нескольких объектов. А что делать в случае, если у нас имеется набор движущихся объектов, и каждый из них должен реагировать на столкновение с любым другим объектом? Можно было бы добавить в каждый из них цикл for, как показано выше, чтобы каждый объект был обработан циклом для проверки на столкновение с любым другим объектом, однако это было бы очень неэффективно. Представьте себе следующий сценарий.

Имеется три объекта: A, B и C. A проверяет столкновение между собой и B, затем столкновение между собой и C; B проверяет столкновение между собой и A, а также между собой и C; наконец, C осуществляет проверку относительно A и B. Итак, мы имеем:

  • A:B
  • A:C
  • B:A
  • B:C
  • C:A
  • C:B

Мы получили шесть вариантов, но это вдвое больше, чем нужно. A:B - это то же самое, что и B:A, A:C идентично C:A, а B:C - то же, что и C:B.

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

Игорь Хан
Игорь Хан
След не остается
Александр Коргапольцев
Александр Коргапольцев
Вопрос по содержанию лекции №2, курс Flash MX Studio
Евгения Новоселецкая
Евгения Новоселецкая
Россия
Станислав Бакулин
Станислав Бакулин
Эстония, Нарва