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

Математика и физика Flash

  1. Теперь приступим к определению этой функции. Перед тем, как погружаться в математику, добавим код для обеспечения уверенности в том, что шарик находится поблизости с помощью простой проверки hitTest.
    function checkWall(wall_mc) {
        // first do simple hitTest to see if it's close
        if (wall_mc.hitTest(ball_mc)) {
        ...
        }
      }
  2. Установив shapeFlag на значение "ложь", мы используем только граничный прямоугольник стены. Если шарик не находится внутри этой области, функция просто не выполнится. В противном случае, переходим к следующему шагу.
  3. В hitTest мы сначала определяем начальные значения X и Y посредством вычитания координат wall из координат ball. После этого преобразуем угол поворота _rotation (в градусах) объекта wall в радианы и устанавливаем переменные cosAngle и sinAngle. Все это проделывалось в предыдущем файле.
    function checkWall(wall_mc) {
        // first do simple hitTest to see if it's close
        if (wall_mc.hitTest(ball_mc)) {
          // if so, determine ball's x and у position 
            in relation to the wall
          x = ball_mc._x-wall_mc._x;
          у = ball_mc._y-wall_mc._y;
          // convert wall's angle to radians and 
             compute cos and sin of it
          rad = wall_mc._rotation*Math.PI/180;
          cosAngle = Math.cos(rad);
          sinAngle = Math.sin(rad);
        }
      }
  4. Теперь я расскажу о небольшой альтернативе нашим предыдущим формулам поворота координат. (Я говорил о том, что здесь не будет ничего нового, однако это всего лишь небольшое изменение!) Приведенные мной формулы прибавляли угол к координатам. Другими словами, если бы вы прибавили 30 градусов, система координат повернулась бы в положительном направлении на 30 градусов. Однако в данном случае, предположим, что наша стена находится под углом 30 градусов, и нам нужно повернуть ее на -30 градусов, чтобы она стала горизонтальной. Конечно, можно было бы просто использовать в той же формуле значение -30 вместо +30, но так как мы заранее вычисляем значения sin и cos, этот способ является более эффективным. Формулами вычитания угла из системы координат являются следующие выражения.
    new_x = cos (angle) *x + sin (angle) *y
      new_y = cos (angle) *y - sin (angle) *x
  5. Единственное отличие заключается в замене знаков + и - в середине выражений. Чтобы вычесть угол стены из координат шарика, нужно добавить следующие строки.
    function checkWall(wall_mc) {
        // first do simple hitTest to see if it's close
        if (wall_mc.hitTest(ball_mc)) {
          // if so, determine ball's x and у position 
             in relation to the wall
          x = ball_mc._x-wall_mc._x;
          у = ball_mc._y-wall_mc._y;
          // convert wall's angle to radians and compute 
             cos and sin of it
          rad = wall_mc._rotation*Math.PI/180;
          cosAngle = Math.cos(rad);
          sinAngle = Math.sin(rad);
          // rotate coordinates to align with wall angle
          x1 = cosAngle*x+sinAngle*y;
          y1 = cosAngle*y-sinAngle*x;
        }
      }

    Теперь нам нужно также реализовать поворот скоростей. Это можно интерпретировать как поворот стрелки на рисунке.

    function checkWall(wall_mc) {
        // first do simple hitTest to see if it's close
        if (wall_mc.hitTest(ball_mc)) {
          // if so, determine ball's x and у position 
            in relation to the wall
          x = ball_mc._x-wall_mc._x;
          у = ball_mc._y-wall_mc._y;
          // convert wall's angle to radians and compute 
             cos and sin of it
          rad = wall_mc._rotation*Math.PI/180;
          cosAngle = Math.cos(rad);
          sinAngle = Math.sin(rad);
          // rotate coordinates to align with wall angle
          x1 = cosAngle*x+sinAngle*y;
          y1 = cosAngle*y-sinAngle*x;
          // rotate velocities to align with wall angle
          vx1 = cosAngle*ball_mc.velX+sinAngle*ball_mc.velY;
          vy1 = cosAngle*ball_mc.velY+sinAngle*ball_mc.velX;
        }
      }

    Заметьте, что мы не поворачиваем ни один из текущих фильмов. Это было бы пустой тратой процессорного времени, так как мы в любом случае поворачивали бы их обратно перед тем, как кто-либо их заметил. Все наши вычисления здесь носят чисто математический характер. Мы имеем воображаемый горизонтальный пол, расположенный в точке (0,0), и воображаемый шарик, расположенный на координатах x1, y1 и двигающийся с воображаемой скоростью vx1, vy1. Все, что мы сделали, это реализовали математическое представление второго, повернутого рисунка.

  6. Теперь нужно применить простой код для отскакивания шарика от горизонтального пола.
    1. Выяснить, находится ли шарик ниже пола.
    2. Если это так, переместить его на один уровень с полом.
    3. Обратить его скорость по направлению Y.

    Так как пол теоретически имеет координаты (0,0), для учета толщины шарика мы используем следующий код:

    if (y1>0-ball_mc._height/2) {
      . . .
      }

    Все так же, как было раньше.

    if (ball_mc._y>BOTTOM-ball_mc._height/2){
      . . .
      }

    Вместо этого мы просто используем координаты после поворота. Если это так - выходим из программы. В противном случае, мы настраиваем позицию шарика и обращаем его скорость по направлению Y. В терминах системы координат после поворота имеем следующее.

    function checkWall(wall_mc) {
        // first do simple hitTest to see if it's close
        if (wall_mc.hitTest(ball_mc)) {
          // if so, determine ball's x and у position 
             in relation to the wall
          x = ball_mc._x-wall_mc._x;
          у = ball_mc._y-wall_mc._y;
          // convert wall's angle to radians and compute 
             cos and sin of it
          rad = wall_mc._rotation*Math.PI/180;
          cosAngle = Math.cos(rad);
          sinAngle = Math.sin(rad);
          // rotate coordinates to align with wall angle
          x1 = cosAngle*x+sinAngle*y;
          y1 = cosAngle*y-sinAngle*x;
          // rotate velocities to align with wall angle
          vx1 = cosAngle*ball_mc.velX+sinAngle*ball_mc.velY;
          vy1 = cosAngle*ball_mc.velY+sinAngle*ball_mc.velX;
          // check if ball is hitting wall
          if (y1>0-ball_mc._height/2) {
            // do simple bounce calculation, adjusting position of
            Кball to align with the edge of wall
            y1 = 0-ball_mc._height/2;
            vy1 *= bounce;
          }
        }
      }
  7. Теперь выполняем обратный поворот посредством прибавления угла вместо вычитания, поэтому возвращаемся к исходной формуле. Это делается в последнем блоке if после vy1 *= bounce.
    x = cosAngle*x1 - sinAngle*y1;
      y = cosAngle*y1 + sinAngle*x1;
        И скорости:
      ball_mc.velX = cosAngle*vx1 - sinAngle*vy1;
      ball_mc.velY = cosAngle*vy1 + sinAngle*vx1;
  8. После этого мы обратно прибавляем x и y к значениям wall._x и wall._y для возврата в текущее положение для шарика на рабочем столе.
    ball_mc._x = x+wall_mc._x;
      ball_mc._y = y+wall_mc._y;

    Если шарик сталкивается с линией, он располагается непосредственно на ней, и его скорость станет таковой, что он отскочит от линии под реалистичным углом. Приведем полный код функции checkWall.

    function checkWall(wall_mc) {
        // first do simple hitTest to see if it's close
        if (wall_mc.hitTest(ball_mc)) {
          // if so, determine ball's x and у position in relation to the wall
          x = ball_mc._x-wall_mc._x;
          у = ball_mc._y-wall_mc._y;
          // convert wall's angle to radians and compute cos and sin of it
          rad = wall_mc._rotation*Math.PI/180;
          cosAngle = Math.cos(rad);
          sinAngle = Math.sin(rad);
          // rotate coordinates to align with wall angle
          x1 = cosAngle*x+sinAngle*y;
          y1 = cosAngle*y-sinAngle*x;
          // rotate velocities to align with wall angle
          vx1 = cosAngle*ball_mc.velX+sinAngle*ball_mc.velY;
          vy1 = cosAngle*ball_mc.velY+sinAngle*ball_mc.velX;
          // check if ball is hitting wall
          if (y1>0-ball_mc._height/2) {
            // do simple bounce calculation, adjusting position of ball
            // to align with edge of wall
            y1 = 0-ball_mc._height/2;
            vy1 *= bounce;
            // rotate coordinates back to original angle
            x = cosAngle*x1-sinAngle*y1;
            у = cosAngle*y1+sinAngle*x1;
            // rotate velocities back to original angle
            ball_mc.velX = cosAngle*vx1-sinAngle*vy1;
            ball_mc.velY = cosAngle*vy1+sinAngle*vx1;
            // adjust wall-relative position to stage position
            ball_mc._x = x+wall_mc._x;
            ball_mc._y = y+wall_mc._y;
          }
        }
      }
    Пример 8.6.

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

    Одним из моментов, с которым вы можете столкнуться - это то, что когда шарик приближается к обратной стороне стенки, он может неожиданно перескочить наверх. Это происходит потому, что мы проверяем, находится ли y1 просто под полом. Не имеет значения, на сколько именно ниже пола он находится. Мы можем обеспечить 99% уверенности в этом, добавив другое условие к выражению if. Мы знаем, что если шарик двигается вниз и проходит через стенку, самой дальней точной, которой он может достичь после стенки, является vy1, так как это наибольшее расстояние, на которое он может переместиться в одном кадре. Поэтому, если шарик находится дальше этой точки под стенкой, он должен был появиться со стороны. Нашим выражением if будет следующее.

    if (y1>0-ball_mc._height/2 && y1<vy1){
      . . .
      }

    Сейчас мы немного оптимизируем данный код. Если вы последовательно читали эту лекции, вы заметили, что сначала мы выполняем hitTest, затем вычисляем набор значений, и после этого выполняем выражение if. Однако это выражение if использует только значения y1 и vy1. Несмотря на то, что нам, в конечном счете, могут понадобиться значения x1 и vx1, не имеет смысла вычислять их до того момента, как мы определим, нужны они или нет. Поэтому мы можем переместить эти строки с вычислениями внутрь блока if. Проверьте работу функции еще раз. Вы увидите, что скорость заметно возросла. Приведем окончательный код функции.

    function checkWall(wall_mc) {
        // first do simple hitTest to see if it's close
        if (wall_mc.hitTest(ball_mc)) {
          // if so, determine ball's x and у position in relation to the wall
          x = ball_mc._x-wall_mc._x;
          у = ball_mc._y-wall_mc._y;
          // convert wall's angle to radians and compute cos and sin of it
          rad = wall_mc._rotation*Math. PI/180;
          cosAngle = Math.cos(rad);
          sinAngle = Math.sin(rad);
          // rotate у coord and velocity to align with wall angle
          y1 = cosAngle*y-sinAngle*x;
          vy1 = cosAngle*ball_mc.velY+sinAngle*ball_mc.velX;
          // check if ball is hitting wall
          if (y1>0-ball_mc._height/2 && y1<vy1) {
            // rotate x coord and velocity to align with wall angle
            x1 = cosAngle*x+sinAngle*y;
            vx1 = cosAngle*ball_mc.velX+sinAngle*ball_mc.velY;
            // do simple bounce calculation, adjusting position of ball
            // to align with edge of wall
            y1 = 0-ball_mc._height/2;
            vy1 *= bounce;
            // rotate coordinates back to original angle
            x = cosAngle*x1-sinAngle*y1;
            у = cosAngle*y1+sinAngle*x1;
            // rotate velocities back to original angle
            ball_mc.velX = cosAngle*vx1-sinAngle*vy1;
            ball_mc.velY = cosAngle*vy1+sinAngle*vx1;
            // adjust wall-relative position to stage position
            ball_mc._x = x+wall_mc._x;
            ball_mc._y = y+wall_mc._y;
          }
        }
      }
    Пример 8.7.

    Наслаждайтесь результатом работы! Перед тем, как покинуть вас, я хочу продемонстрировать вам всю мощь того, что мы только что сделали. Сделайте четыре дополнительных копии фильма wall на рабочем столе. Назовите пять стен именами wall0_mc - wall4_mc. Расположите их в различных местах и поверните на разное число градусов. Теперь перейдите к функции move и замените строку

    checkWall(wall_mc);

    следующим кодом.

    for (i=0; i<5; i++) {
      checkWall (__root ["wal""+i+"_mc"] );
      }

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

    Теперь все в ваших руках. Пользуясь приобретенными здесь навыками, вы можете создавать разнообразные Pinball-игры или игры типа "Donkey Kong".


Подведем итоги

В этой лекции мы рассмотрели очень много материала. Сделаем краткий обзор пройденного.

Теперь вы разбираетесь в основах тригонометрии.

  • Sin
  • Cos
  • Tan
  • Нахождение любой стороны или угла прямоугольного треугольника, имея минимум информации.
  • Движение фильма по окружности.
  • Нахождение расстояния между любыми двумя точками или фильмами.
  • Понятие радианов и градусов, а также преобразование их друг в друга.
  • Поворот фильма к определенной точке в определенной точке.

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

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

При построении игры с астероидами мы применили практически все знания, полученные к тому моменту, и сделали эту игру очень похожей на оригинал. Это показатель того, насколько обширными являются ваши знания на данный момент. Сейчас вы знаете достаточно много для того, чтобы создавать некоторые стоящие игры и приложения. Применяйте эти знания совместно с вашим воображением и с остальным материалом, который вы изучите в следующих лекциях (рисование API, звук, видео и т.д.), и вас уже ничто не сможет остановить на пути успешного программирования во Flash!

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

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

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

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

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

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