Тема 9. Математика и физика


  1. Математические функции
  2. Одномерное движение
  3. Движение на плоскости
  4. Отскок
  5. Наклонная плоскость
  6. Маятник

1. Математические функции

Объект Math — это встроенный объект Flash, с помощью которого выполняются математические действия. Например, можно найти Эти функции являются методами объекта Math и при обращении к ним надо ссылаться на этот объект. Вот пример вычисления квадратного корня из x:
z = Math.sqrt(x);
Для получения случайных чисел можно также использовать независимую функцию random, которая вызывается с одним параметром. Например, оператор
n = random ( 10 );
записывает в переменную n случайное целое число в интервале от 0 до 9.
к началу К началу страницы

2. Одномерное движение

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

  Здесь использован переработанный пример из учебного курса фирмы Adobe, опубликованного на сайте www.intuit.ru.
  Откройте файл PRACTICE\9\pool.fla. Найдите элемент Невидимая кнопка в библиотеке и двойным щелчком мыши «загляните внутрь него».

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

Зачем нужна такая кнопка? А затем, чтобы можно было за нее схватить мышкой и потянуть кий вправо.

  Щелкните дважды по кию на сцене, чтобы открыть клип.

В этом клипе кадры  1 и 10 имеют имена init (начальное положение) и move (движение). В первом кадре слоя Программа стоит команда stop(), это значит, что клип в самом начале останавливается на 1 кадре.

Когда нужно будет оттянуть кий вправо, мы направим клип на метку move, где начинается анимация слоя Рисунок. После анимации клип снова останавливается: в кадре 30 стоит команда stop() (см. слой Программа).

  Выделите невидимую кнопку и добавьте обработчики событий:
 on (press) {
   ball._x = 360;
   ball._y = 180;
   power.text = "0";
   x0 = _root._xmouse;
   track = true;
 }
 on (dragOut) {
   stick.gotoAndPlay ("move");
 }
 on (release, releaseOutside) {
   stick.gotoAndStop ("init");
   track = false;
 }

При нажатии кнопки (событие press) координаты шарика (клипа с именем ball) устанавливаются в начальное положение, в текстовое поле power выводится нулевая сила, в переменной x0 запоминается x-координата щелчка мыши. Логическая переменная track показывает, идет ли перетаскивание — при нажатии кнопки она принимает значение true («да»).

Когда мышь выходит из активной области при нажатой кнопке мыши (событие dragOut) кий (клип stick) переходит к кадру move и начинается анимация.

Когда кнопка мышь отпущена (события release и releaseOutside) кий резко возвращается в исходное положение (на метку init). Переменной track присваивается значение false (перетаскивания больше нет).

  Выделите шарик и добавьте к нему обработчик события:
 onClipEvent (mouseMove) {
   if ( _root.track ) {
     P = _root._xmouse - _root.x0;
     _root.power.text = P;
   }
 }

Клип получает событие mouseMove даже тогда, когда мышь находится вне клипа. Если идет перетаскивание (переменная track монтажного стола _root равна true), запоминаем разницу между текущей x-координатой мыши и начальной координатой x0 (силу удара) в переменной P и выводит это значение в текстовое поле power.

Обратите внимание, что при обращении к переменным track и x0 надо указывать их адрес _root, тогда как в коде невидимой кнопки мы этого не делали. Дело в том, что кнопка является частью монтажного стола и работает с его переменными напрямую, а клип имеет свой монтажный стол и свои переменные (например, переменная P относится к клипу).

Теперь нужно, чтобы шарик двигался после удара. Для этого удобно использовать обработчик enterFrame. Сначала рассмотрим простейший вариант: с каждым вызовом шарик смещается на одинаковое расстояние, а при этом оставшаяся сила уменьшается.

  Добавьте к шарику еще один обработчик события:
 onClipEvent (enterFrame) {
   if ( ! _root.track  &&  P > 2 ) {
     _x -= 5;
     P -= 2;
   }
 }
Движение происходит только тогда, когда нет перетаскивания (переменная-флаг track равна false) и оставшаяся сила P больше 2. Два знака & рядом означают логическую операцию «И» (одновременное выполнение двух условий).

На каждом шаге x-координата клипа уменьшается на 5 пикселей, а сила — на 2 единицы.

Проверив клип, мы видим, что шарик движется неестественно: его скорость постоянна и он резко останавливается.

Чтобы улучшить анимацию, используем следующие идеи:

  Измените обработчик события enterFrame:
 onClipEvent (enterFrame) {
   if ( ! _root.track  &&  P > 2 ) {
     _x -= 0.5*P;
     P = 0.9*P;
   }
 }
Проверьте ролик и сохраните файл.
к началу К началу страницы

3. Движение на плоскости

Равномерное движение

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

Движение на плоскости более сложно, чем одномерное, и требует некоторых математических знаний. Пусть машину надо передвинуть на расстояние L вперед (в соответствии с ее углом поворота _rotation, который на схеме ниже обозначен греческой буквой α).

По схеме видно, что x-координата машины увеличивается на Δx, а y-координата уменьшается на Δy (учитывая, что ось Y направлена вниз). Эти величины вычисляются с помощью функция синус и косинус (это методы объекта Math).

Единственная сложность состоит в том, что для методов Math.sin() и Math.cos() необходимо задать угол в радианах, а свойство _rotation определяет угол в градусах. Для перевода из градусов в радианы надо умножить величину угла на π (константа Math.PI) и разделить на 180.

  Добавьте обработчики событий машины
 onClipEvent (load) {
   L = 5;
   function move ( d ) {
     r = _rotation*Math.PI/180;
     _x += d*Math.sin(r);
     _y -= d*Math.cos(r);
   }
 }
 onClipEvent (enterFrame){
   if (Key.isDown(Key.UP)) go(L);
   else
   if (Key.isDown(Key.DOWN)) go(-L);
   if (Key.isDown(Key.LEFT))  _rotation -= 5;
   else
   if (Key.isDown(Key.RIGHT)) _rotation += 5;
 } 

При создании клипа (событие load) в переменную L записывается величина одного шага. Кроме того, описывается функция move, которая двигает машину на заданное расстояние d. Сначала угол поворота переводится в радианы, затем координаты клипа изменяются на величины Δx и Δy, о которых говорилось выше.

В обработчике enterFrame обрабатываются четыре клавиши-стрелки: при нажатии на клавиши «вверх» и «вниз» машина двигается вперед и назад, а клавиши «влево» и «вправо» разворачивают ее в нужном направлении.

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

Неравномерное движение

Теперь попробуем сделать так, чтобы скорость можно было изменять стрелками «вверх» (увеличивать) и «вниз» (уменьшать). Если вообще не нажимать клавиши, объект постепенно замедляет движение и в конце концов останавливается.

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

  Откройте файл plane.fla из папки PRACTICE\9. Выделите клип-самолет и добавьте к нему код
 onClipEvent (load) {
   v = 0;
   function move ( d ) {
     r = _rotation*Math.PI/180;
     _x += d*Math.sin(r);
     _y -= d*Math.cos(r);
   }
 }
 onClipEvent (enterFrame){
   if (Key.isDown(Key.UP))   v += 0.2;
   else
   if (Key.isDown(Key.DOWN)) v = Math.max(0,v - 0.3);
   else                      v = Math.max(0,v - 0.1);
   move ( v );
   if (Key.isDown(Key.LEFT))  _rotation -= 5;
   else
   if (Key.isDown(Key.RIGHT)) _rotation += 5;
 }
Этот код очень поход на код предыдущего примера. Переменная v обозначает скорость самолета, вначале она равна нулю. Если нажата клавиша «вверх», скорость увеличивается на 0.2, клавиша «вниз» уменьшает ее на 0.3, если ни одна клавиша не нажата, скорость уменьшается на 0.1.

Самолет не имеет заднего хода, поэтому для того, чтобы скорость не стала отрицательной, применяется метод Math.max(), выбирающий максимальное из двух значений в скобках.

  Подсказка: можно сделать фон клипом и добавить обработчик события release. Для того, чтобы курсор над фоном был обычного вида (а не как над кнопкой), в обработчик события load добавьте строку:
 useHandCursor = false;
к началу К началу страницы

4. Отскок

Одномерное движение

При наведении мыши на клип, расположенный ниже, мячик начинает скакать, постепенно останавливаясь. Щелчок мыши по полю возвращает его в начальное положение.

Когда мяч летит вниз (рисунок слева), сила тяжести G давит его вниз, а сила сопротивления воздуха Fc — потталкивает вверх, так как действует противоположно скорости v. При движении вверх (рисунок справа) обе силы действуют в одном направлении.

Будем считать скорость положительной, когда мяч летит вниз (его y-координата увеличивается). Тогда сила тяжести дает положительное ускорение (увеличение скорости), а сила сопротивления — отрицательное при полете вниз (когда v>0) и положительное при полете вверх (когда v<0).

Изменение скорости в программе на каждом небольшом интервале можно записать так:

v += g - mu * v * v / Math.abs(v);
Здесь g и mu — характеризуют ускорение свободного падения и сопротивление воздуха (строго говоря, mu зависит от скорости и формы объекта, но мы упрощаем дело, здесь эти тонкости не очень нужны).

Величина Math.abs(v) — это модуль скорости, поэтому отношение v/Math.abs(v) равно 1, если скорость положительна (вниз), и -1, если отрицательная. Такой прием позволяет учитывать движение в обоих направлениях.

Из-за сопротивления скорость изменяется на mu*v, то есть сила сопротивления линейно зависит от скорости.

  На больших скоростях эта зависимость становится более сложной — квадратичной и даже кубичной (от квадрата или от куба скорости).

Единственная проблема — избежать деления на нуль при нулевой скорости (в самой высокой точке). Для этого применим условный оператор:

v += g;
if ( v != 0 ) v -= mu * v * v / Math.abs(v);

В момент отскока (когда мяч достигает предельного нижнего положения) направление вектора скорости скачком меняется на противоположное, при этом теряется часть энергии, т.е. скорость уменьшается по модулю. Отскок с потерей 5% скорости в программе можно записать так:

v = - 0.95*v;
Вот и весь отскок.
  Откройте файл PRACTICE\9\ball. Выделите на сцене футбольный мяч (клип с именем ball) и добавьте к нему следующий код:
 onClipEvent (load) {
   v = 0;
   g = 9.81;
   mu = 0.02;
   function bounce() {
     v += g;
     if (v != 0) v -= mu * v * v / Math.abs(v);
     if (_y + v > 350) {
         _y = 350;
         v = -0.95 * v;
     }
     else _y += v;
   }
 }
 onClipEvent (enterFrame) {
   bounce();
 }
При загрузке клипа (событие load) задается начальная скорость v, ускорение свободного падения g и коэффициент сопротивления mu. Кроме того, описывается функция bounce, в которой изменяется скорость и координата мяча. Предельная y-координата равна 350, при достижении этого уровня происходит отскок.

Функция bounce вызывается в обработчике события enterFrame, то есть (в нашем случае) 12 раз в секунду.

  Если нужно перерисовывать мяч реже или чаще (вызывать функцию с другим интервалом), можно использовать таймер и функцию setInterval. Например, если добавить в конец обработчика load строчку
 setInterval ( bounce, 100 );
функция bounce будет вызываться с интервалом 100 мс.

Если интервал меньше, чем интервал смены кадров, могут быть проблемы с перерисовкой объекта. Чтобы улучшить анимацию, надо добавить вызов

 updateAfterEvent();
(перерисовать после события) в конец функции bounce.

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

  Самостоятельно сделайте так, чтобы тень (объект _root.shadow) при наборе высоты увеличивалась и становилась более прозрачной.

Движение на плоскости

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

Отскок от левой стенки

Область, где летают шарики, ограничена прямоугольником, левый верхний угол которого расположен в точке (10,10), а правый нижний — в точке (540,370). Рассмотрим отскок от левой стенки (рисунок справа).

Через rBall обозначим радиус шарика. Заметим, что точка регистрации клипа находится точно в центре шарика. Отскок происходит тогда, когда x-координата (центра) шарика становится равна (или меньше) величины l=10+rBall. При этом его x-скорость (проекция вектора скорости на ось X) изменяет знак (отскок!), а y-скорость не меняется — если шарик летел вниз, он продолжает лететь вниз.

  Откройте файл PRACTICE\9\bounce.fla. Выделите шарик (клип с именем ball0) и добавьте к нему обработчики событий
 onClipEvent (load){
   v = 10;
   rBall = _width / 2;
   l = 10 + rBall;
   r = 540 - rBall;
   t = 10 + rBall;
   b = 370 - rBall;
   angle = random(360) * Math.PI / 180;
   vx =  v*Math.sin(angle);
   vy = -v*Math.cos(angle);
   function fly() {
     _x += vx;
     _y += vy;
     if ( _x < l ) {
       _x = l;
       vx = - vx;
     }
     // здесь надо дописать отскоки от остальных стенок
   }
 }
 onClipEvent (enterFrame) {
   fly();
 }
При загрузке задается скорость v, рассчитывается радиус шарика (половина его ширины) и границы допустимой области для центра. Случайно выбирается начальный угол angle и рассчитываются проекции вектора скорости на оси X и Y, обозначенные как vx и vy.

Определяется также функция fly, которая будет вызываться 12 раз в секунду при событии enterFrame. В ней координаты шарика изменяются на vx и vy, а потом идет проверка, не вышел ли он за границы прямоугольника. Если вышел — изменяется скорость.

Отскок от левой стенки написан полностью. Самостоятельно допишите код отскока от остальных стенок и проверьте ролик.

Глубина

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

Каждый из объектов на монтажном столе имеет свою глубину (depth). Глубины могут изменяться от -16384 до 1048575, элементы с большей глубиной располагаются выше элементов с меньшей глубиной и перекрывают их.

На одной глубине не могут располагаться два клипа. Если загрузить на глубину n какой-нибудь клип, то клип, находившийся ранее на этой глубине, удаляется.

Гдубина — это величина, связанная с конкретным монтажным столом. Внутри клипа тоже могут быть вложенные клипы, расположенные на разных глубинах от -16384 до 1048575, причем эти глубины никак не связаны с глубинами других монтажных столов.

Для работы с глубинами используются специальные методы объекта MovieClip. Здесь вместо mc и mc1 нужно подставить имена нужных клипов, а вместо n — целое число.

Итак, чтобы определить глубину клипа, мы используем функция getDepth() (получить глубину). Строчка
trace ( ball0.getDepth() );
выводит в окно Output значение -16380 — это глубина для первого шарика.

Создание новых клипов из программы

Для создания новых клипов и их удаления во время работы программы используются методы: Чтобы создать новый шарик, точно такой же, как и ball0, применим функция duplicateMovieClip:
ball0.duplicateMovieClip(name, depth);
Здесь name — имя нового клипа, в depth — его глубина.

Мы будем вести счетчик созданных клипов count и называть клипы ball1, ball2 и т.д, размещая их соответственно на уровнях 1, 2, и т.д.

  Добавьте в кадр 1 слоя Программа строчку
 count = 1;
Затем выделите треугольную кнопку и добавьте к ней код
 on ( release ) {
   name = "ball" + count;
   ball0.duplicateMovieClip(name, count);
   count ++;
 }
Проверьте ролик, щелкая на кнопку.
При каждом щелчке создается новый клип, в точности копирующий ball0, причем копируются не только свойства, но и все обработчики событий. При создании выполняется обработчик load.

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

Теперь сделаем, чтобы шарики «вылетали» из кнопки, а их цвет выбирался случайно.

Если войти в режим редактирования клипа-шарика отключить видимость верхнего слоя Объем, можно увидеть, что цвет шарика определяется цветом клипа с именем hand (круга на слое Фон). Именно его цвет мы и будем изменять с помощью функции setRGB.

  Добавьте в конец обработчика события release кнопки такой код:
 obj = _root[name];
 obj._x = 275;
 obj._y = 370 - obj._height/2;
 col = new Color(obj.back);
 R = random(256);
 G = random(256);
 B = random(256);
 col.setRGB(0x10000*R + 0x100*G+B);
В первой строчке для упрощения записи мы получаем ссылку на новый клип по его имени (можно было бы везде вместо obj использовать _root[name]).

Затем устанавливаются координаты шарика и цвет, который строится из трех случайных составляющих, каждая из которых находится в интервале 0..255.

Для удаления клипа со сцены и из памяти используют функцию

removeMovieClip ( name );
где name — имя клипа.
  Выделите клип-фон и добавьте к нему обработчик:
 on (release){
   if ( _root._ymouse > 370 ) {
     for (i=1; i<_root.count; i++)
       removeMovieClip(_root["ball"+i]);
     _root.count = 1;
   }
 }

Здесь используется переменная count главного монтажного стола _root — в цикле удаляются все клипы с номерами от до count-1. После этого в счетчик записывается 1, поскольку один клип ball0 все-же остается «на развод» (если его удалить, нечего будет копировать!).

  Самостоятельно сделайте так, чтобы надо фоном курсор имел «обычный» вид (стрелка), а не «перст указующий».

5. Наклонная плоскость

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

Векторы

Вектор — это направленный отрезок, который задается (на плоскости) двумя числами — своими координатами по осям X и Y.

Введем единичные векторы x1 и y1, которые параллельны осям координат и имеют длину, равную 1 (см. рисунок). Тогда можно записать вектор v с координатами (4,3) в виде суммы

Другими словами, координаты вектора (обозначенные через vx и vy) — это длины его проекций на оси выбранной системы координат. Координаты могут быть и отрицательными, это значит, что вектор идет в сторону, противоположную данной оси.

Сумма векторов — это вектор, координаты которого равны сумме соответствующих координат слагаемых. Для векторов v(4,3) и w(2,-1) получаем

Скалярное произведение двух векторов — это сумма произведений соответствующих координат (число). Например, скалярное произведение векторов v(4,3) и w(2,-1) равно

С помощью скалярного произведения можно, например, определить, что векторы перпендикулярны — в этом случае их скалярное произведение равно нулю. Например, векторы x1 и y1 имеют координаты x1(1,0) и y1(0,1). Легко проверить, что их скалярное произведение равно нулю.

Скалярное произведение вектора на себя равно квадрату длины этого вектора:

Следовательно,

Скалярное произведение вектора v на какой-нибудь единичный вектор z1 дает величину проекции вектора v на ось, направление которой совпадает с направлением вектора z1. Действительно, для декартовой системы имеем

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

Отскок от наклонной плоскости

Теперь рассмотрим отскок шарика от наклонной плоскости. Сначала предположим, что шарик — это материальная точка и удар упругий, то есть при ударе энергия не теряется.

Введем новые оси координат P (вдоль наклонной плоскости) и N (перпендикулярно плоскости) и разложим вектор скорости v (синяя стрелка на рисунке) на составляющие, параллельные этим осям:

После отскока составляющая скорости, параллельная оси P, не меняется, а составляющая по оси N изменяется на противоположную (вектор меняет знак). Вектора, параллельные оси N, называют нормальными (или нормалями), поскольку они перпендикулярны плоскости, от которой отскакивает шарик.

Таким образом, вектор скорости v' после отскока (зеленая стрелка на рисунке) вычисляется как:

Выразив из первой формулы vp·p1 и подставив во вторую, получаем

причем проекция vn может быть найдена как скалярное произведение:

Таким образом, чтобы определить вектор скорости после отскока, нужно знать единичный вектор n1, перпендикулярный плоскости.

Если взять две точки на плоскости и найти разность их координат dx и dy, то вектор n(-dy,dx) будет перпендикулярен прямой, проходящей через эти точки. Однако этот вектор — не единичный, его длина равна (по теореме Пифагора)

Поэтому для того, чтобы получить вектор единичной длины n1, обе координаты вектора n нужно разделить на его длину.

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

Если удар неупругий, то нормальная составляющая скорости (то есть составляющая, параллельная оси N) уменьшается по модулю, так что

где μкоэффициент упругости, число в интервале от 0 до 1. При отскоке резинового мячика от стальной стенки можно принимать μ=0,99, а при отскоке яблока от войлока — μ≈0,2.

В этом случае скорость после отскока равна

Шарик в кольце

Мы построим пример, в котором шарик будет летать внутри кольца (чтобы посмотреть на будущий результат, наведите мышку на рисунок).

  Откройте файл PRACTICE\9\skew.fla. Поместите клип Шар из библиотеки на сцену в любое место внутри кольца.
  Выделите шар, и добавьте к нему код, который «двигает» объект по экрану:
 onClipEvent(load){
   v = 5;
   rBall = _width / 2;
   angle = random(360) * Math.PI / 180;
   vx =  v*Math.sin(angle);
   vy = -v*Math.cos(angle);
   function fly() {
      _x += vx;
      _y += vy;
   }
   setInterval ( fly, 20 );
 }
Проверьте работу клипа.
Этот код практически совпадает с предыдущим примером, направление движения шарика выбирается случайным образом. Однако шарик пока не отталкивается от стенок кольца.

Раньше мы рассматривали отскок точки, а шарик имеет ненулевые размеры. Тем не менее, отскок шарика от поверхности можно рассмотреть как отскок его центра от некоторой окружности, радиус которой меньше, чем внутренний радиус кольца, на величину радиуса шарика.

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

Вычислим радиус окружности, которая ограничивает движение центра шарика (на рисунке он обозначен через R). Если открыть клип Шар в режиме редактирования, легко проверить, что внешний радиус кольца равен 150, а его внутренний радиус — 95% от внешнего (см. свойство Inner radius), то есть 142,5. Кроме того, их этого числа нужно вычесть радиус шарика, который в программе определяется автоматически и записывается в переменную rBall.

  Добавьте перед функцией fly строчки
 xc = 200;
 yc = 200;
 R = 142.5 - rBall;
Здесь через xc и yc обозначаются координаты центра кольца.

Теперь остается написать код, который моделирует упругое отталкивание шарика от стенок. Мы расположим его внутри функции fly, он срабатывает тогда, когда расстояние между центром кольца и центром клипа-шарика становится меньше R.

  Добавьте в конец функции fly код
 var dx = _x - xc;
 var dy = _y - yc;
 var dist = Math.sqrt(dx*dx + dy*dy);
 if ( dist >= R ) {
   n1x = dx / dist;
   n1y = dy / dist;
   vn = vx*n1x + vy*n1y;
   vx -= 2*vn*n1x;
   vy -= 2*vn*n1y;
   updateAfterEvent();
 }
Сохраните файл и посмотрите, как работает клип.
Рассмотрим код более подробно. Сначала определяются смещения шарика относительно центра по осям X и Y, а также расстояние до центра:
var dx = _x - xc;
var dy = _y - yc;
var dist = Math.sqrt(dx*dx + dy*dy);
Если это расстояние меньше или равно R, произошло столкновение. Вектор нормали (перпендикулярный к плоскости отскока) всегда направлен к центру кольца и имеет координаты (dx,dy). Если разделить их на dist, получается единичный вектор того же направления с координатами
n1x = dx / dist;
n1y = dy / dist;
Далее вычисляем скалярное произведение вектора скорости и вектора n1, которое равно проекции скорости на нормаль:
vn = vx*n1x + vy*n1y;
и изменяем компоненты вектора скорости в соответствии с формулой предыдущего пункта
vx -= 2*vn*n1x;
vy -= 2*vn*n1y;
Коэффициент 2 говорит о том, что соударение абсолютно упругое.

Вызов функции updateAfterEvent заставляет программу сразу же перерисовать шарик в новом положении.

Если внимательно присмотреться, шарик в момент соударения «заходит» на кольцо. Действительно, это следует из условия dist>=R. Чтобы исправить этот недочет, нужно «вернуть» шарик назад так, чтобы его центр находился точно на окружности. Попробуйте разобраться в следующем коде самостоятельно.

  Добавьте в начало тела условного оператора (внутри фигурных скобок) код
 var k = (dist - R) /  R;
 _x -= dx*k;
 _y -= dy*k;
к началу К началу страницы

6. Маятник

Если навести мышку на рисунок ниже, маятник начинает колебаться, и часы тикают ровно 1 раз в секунду. Мышкой можно «схватить» маятник за диск и перетащить, задав новое начальное отклонение.

Сложность заключается в том, чтобы добитьcя анимации, похожей на работу реального маятника:

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

Функция α = cos(t), где t — время, описывает бесконечную волну с амплитудой 1 и периодом . Это значит, что за время единиц маятник возвращается обратно в ту же точку, а за время π делает половину цикла, то есть переходит из одного крайнего положения в другое.

Нам нужно изменить амплитуду и период так, чтобы амплитуда была равна некоторому заданному значению A, а полупериод равен 1 секунде или, если измерять время в кадрах, 25 кадров. Можно показать, что при частоте 25 кадров в секунду этим условиям удовлетворяет функция

α = A·cos (π·t / 25),

где α — угол поворота, а t — время в кадрах.

Для вычисления функции cos будем использовать метод Math.cos, а вместо числа π=3,1415926... — константу Math.PI из того же модуля Math.

  Откройте файл PRACTICE\9\pendulum.fla, выделите на сцене маятник (клип pend) и добавьте обработчики событий
 onClipEvent (load) {
   max = 20;
   _rotation = max;
   t = 0;
 }
 onClipEvent (enterFrame) {
   _rotation = max*Math.cos(t*Math.PI/25);
   t ++;
 }
Проверьте ролик.

При создании клипа устанавливается размах колебаний (20 градусов), маятник выводится в крайнее положение и обнуляется счетчик кадров t («время»).

Теперь добавим звук («тиканье»), который должен проигрываться каждую секунду, то есть при значениях t, равных 25, 50 и т.д. Заметим, что все эти числа делятся нацело (без остатка) на 25, поэтому получаем

if ( t % 25 == 0 ) проиграть звук;
Напомним, что оператор % вычисляет остаток от деления первого числа на второе.
  Присвойте звуку tick.wav из библиотеки имя tick (правая кнопка мыши, Linkage). Добавьте в конец обработчика enterFrame код
 if (t % 25 == 0) {
   snd = new Sound();
   snd.attachSound("tick");
   snd.start(0,1);
 }
Вот, собственно, и все.

Добавим еще одну возможность — маятник можно будет «схватить» мышкой и перетащить в новое крайнее положение.

Введем логическую переменную track, которая будет равна true («да»), если идет перетаскивание маятника. При создании клипа она должна быть равна false, при нажатии кнопки мыши на маятнике — true, а при отпускании мыши — снова false.

  Добавьте строчку
 track = false;
в обработчик load клипа. Добавьте два новых обработчика событий мыши:
 on (press) {
   track = true;
 }
 on (release, releaseOutside) {
   track = false;
   max = _rotation;
   t = 0;
 }
Обратите внимание, что при отпускании мыши (все равно, на маятнике или вне его), в переменной max запоминается новый максимальный угол поворота, а счетчик времени сбрасывается в ноль.

Остается самое сложное — сделать слежение маятника за мышкой в режиме перетаскивания. Для определения угла будем использовать координаты клипа _x и _y (точка регистрации совпадает с центром вращения) и текущие координаты мыши _xmouse и _ymouse на главном монтажном столе _root.

Желаемый угол поворота α определяется значениями dx и dy, их отношение dx/dy определяет тангенс угла α. Для вычисления самого угла будем использовать функцию Math.atan2.

  В самое начало обработчика enterFrame добавьте код
 if (track) {
   dx = _x - _root._xmouse;
   dy = _root._ymouse - _y;
   alpha = Math.atan2(dx, dy);
   _rotation = alpha*180/Math.PI;
   return;
   }
и проверьте работу клипа.
Этот блок срабатывает только тогда, когда идет перетаскивание (значение track равно true). Величины dx и dy вычисляются так, как на рисунке. Функция Math.atan2 возвращает значение угла в радианах, для записи в свойство _rotation клипа угол переводится в градусы. В последней строчке происходит выход из функции по команде return (возврат), так как при перетаскивании остальной код обработчика (перемещение маятника) выполнять не нужно.
к началу К началу страницы


Оглавление
 Объекты среды Flash Назад В начало Вперед Столкновение


© 2007  К. Поляков


Сайт создан в системе uCoz