«STAR WARS REIMAGINED: african totem C3PO» © 2016, Евгений 
CGtalk.by CG Award 2016: «STAR WARS REIMAGINED: african totem C3PO», Евгений
   
Главная Форумы Галерея Теория Конкурсы Вакансии
Назад   CGtalk.by > Программирование > Программирование
Новые сообщения Зарегистрироваться

 
Дополнительно Режимы отображения Ответить
Старый 18.04.2007, 23:10   #1
black zorro
Участник
 
Сообщений: 12
Flash 8 & Физика. Введение

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

Сегодня мы попробуем смешать средства Flash 8 и несколько простых законов физики.

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

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

В самом начале пути flash это могло быть оправдано общей примитивностью языка ActionScript и нежеланием повышать требования к профессиональному уровню дизайнеров. Но когда появился ActionScript-2 и тем более flex|mxml|actionscript3. Когда в мир flash потянулись более программисты, чем дизайнеры. Программисты могут позволить себе более не довольствоваться примитивными продуктами от Macromedia|Adobe, а разработать собственные средства, взяв идеи из мира java|smalltalk|eclipse|xaml.

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

Первое - рассказать об новых инструментах и библиотеках, которые разработало сообщество (не adobe) и которые ориентируются на actionscript3|flex и будущий flash9.

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

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

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

Первый прием предполагает вызов метода hitTest от имени некоторого объекта с параметрами: координата_x, координата_y, принцип проверки.

Метод hitTest вернет вам значение “true” в случае если точка лежит внутри данного объекта.

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

Очевидно, что для ряда фигур проверка того, лежит ли в них точка, достаточно нетривиальна. Поэтому если третий параметр равен true – то выполняется точная проверка, иначе вокруг фигуры рисуется воображаемая рамка окружающего фигуру прямоугольника, и точка проверяется на попадание внутрь не фигуры, а, именно, описывающего прямоугольника (bounding box).

Для примера создайте три клипа с именами smb1, smb2, smb3 это будут фигуры соответственно прямоугольника, круга, и некоторой кривой. Дайте экземплярам клипов расположенных на слое layer1 имена smb1_obj, smb3_obj , smb3_obj Сделайте так чтобы прямоугольник и круг частично пересекались и имели общую область. Также я разместил на слое компонент checkbox с именем chk_if_shape.

Затем в первый кадр слоя layer1 введите следующий код:

Код:
 this.createTextField("status_txt", 999, 0, 0, 400, 22);
  var mouseListener:Object = new Object();
  mouseListener.onMouseMove = function():Void {
      status_txt.text = 
               " layer0: " + _level0.hitTest(_xmouse, _ymouse, true) +
               " object 1: " + _root.smb1_obj.hitTest(_xmouse, _ymouse, _root.chk_if_shape.selected) +
               " object 2: " + _root.smb2_obj.hitTest(_xmouse, _ymouse, _root.chk_if_shape.selected) +
               " object 3: " + _root.smb3_obj.hitTest(_xmouse, _ymouse, _root.chk_if_shape.selected)              
               ;
  }
  Mouse.addListener(mouseListener);
В примере выше я создаю текстовую надпись с именем status_txt. Она будет служить для вывода сообщений из созданного ниже объекта-слушателя на событие перемещение мыши. Каждый раз внутри данного обработчика я вызываю метод hitTest с координатами мыши а также, признаком того отмечен или нет checkbox, влияющий на алгоритм расчета попадания точки внутрь фигуры. При вызове hitTest от имени “_layer0” я проверяю, попала ли мышь внутрь какого-либо объекта вообще.

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

Нажмите на изображении, чтобы увидеть его в полный размер.

Название файла:	flash8_1.PNG
Просмотров:	566
Размер:	11.6 КБ
ID:	2043

Второй возможный способ использования метода hitTest это проверка того соприкасаются ли два объекта между собой. Для демонстрации я взял пример из справки flash8 только немного его дополнил так, чтобы показать не самые приятные стороны этой функции. Итак, создайте два объекта клипа: прямоугольник smb_box и круг smb_circle. Затем на слое layer1 разместите по экземпляру этих клипов с именами соответственно smb_box_obj и smb_circle_obj. Рядом с ними я разместил объект динамического текста с именем txt_status. В первый кадр слоя внесите следующий код. Вкратце, в нем я добавил поддержку перетаскивания мышью объекта круга, а для объекта прямоугольника добавил постоянную проверку, соприкасаются ли эти две фигуры между собой с выводом результатов в текстовое поле txt_status.


Код:
 smb_box_obj.onEnterFrame = function() {
      _root.txt_status.text = this.hitTest(smb_circle_obj);
  };
  smb_circle_obj.onPress = function() {
      this.startDrag(false);
      updateAfterEvent();
  };
  smb_circle_obj.onRelease = function() {
      this.stopDrag();
  };
Результат работы виден на картинке 2.

Name:  flash8_2.PNG
Views: 939
Size:  3.9 КБ

Обратите внимание на то, что надпись txt_status отмечает значение true (фигуры пересекаются) в еще тот момент, когда этого соприкосновения еще нет. На самом деле hitTest работает так: вокруг объектов рисуется ограничивающие их прямоугольники, и проверяется, не то пересекаются ли сами фигуры, а, именно, пересекаются ли ограничивающие их прямоугольники.

Может быть, Macromedia предусмотрела еще какие то методы анализа?

Увы, но нет. Мы не можем не только точно определить столкнулись ли два объекта, но и определить их дальнейшее поведение. Фактически если вы делаете игрушку, то вы создаете ее как набор жестко предопределенных сценариев поведения. Скажем в гоночках, если машинка соприкоснулась с другой машинкой, то она получает повреждения и отскакивает в сторону.

Но является ли расчет количества повреждений и траектории отскока зависящим от того, как именно соприкоснулись эти два объекта?

Была ли их траектории касательными или же было лобовое столкновение?

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

К счастью, задачи обнаружения пересечения объектов не только сложны, но и давно изучены.
black zorro офлайн   Ответить с цитированием
Старый 18.04.2007, 23:10   #2
black zorro
Участник
 
Сообщений: 12
Flash 8 & Физика. Введение

Весь дальнейший материал ориентируется на 2d пространство. Методы работы с 3d во flash будут рассмотрены позже при условии появления откликов от читателей.

Итак, процесс обнаружения того соприкоснулись и как два объекта начинается со стадии, когда определяется какие пары объектов могут соприкоснуться (broad phase).

Так если у вас в сцене 100 объектов, то глупо перебивать все пары

100*100 – 1 = 9999

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

Для упрощенной проверки соприкосновения объектов мы создаем вокруг них bounding box (также как делает метод hitTest), а затем проверяем пересекаются ли данные прямоугольники. Начиная с Flash-8 за это отвечает класс flash.geom.Rectangle. Если у вас фигура задается в общем случае некоторым полигоном, то вам нужно найти крайние точки: максимальные и минимальные координаты среди всех вершин полигона.


Код:
 // импортируем необходимые классы
  import flash.geom.Rectangle;
  import flash.geom.Point;
  import flash.display.BitmapData;
   
  // создаем два объекта прямоугольника заданных точкой левого верхнего угла, также шириной и высотой
  var boundingBox_1 : Rectangle = new  Rectangle (10,10, 200, 200);
  var boundingBox_2 : Rectangle = new  Rectangle (70,70, 200, 200);
   
  // делаем проверки того:
  //попадает ли точка с заданными координатами внутрь прямоугольника 1
  trace ("point 1 in box = " +boundingBox_1.containsPoint(new Point (20, 30)));
  // содержится ли целиком прямоугольник 2 внутри прямоугольника 1
  trace ("box 1 contains box 2 = " +boundingBox_1.containsRectangle(boundingBox_2));
  // пересекаются ли оба прямоугольника
  trace ("box 1 intersects box 2 = " +boundingBox_1.intersects(boundingBox_2));
   
  // функция рисующая прямоугольник заданным цветом
  function paintBox (num: Number, box: Rectangle, color : Number){
   var mc:MovieClip = _root.createEmptyMovieClip("mc_" + num, this.getNextHighestDepth());
   var bmp:BitmapData = new BitmapData(box.width, box.height, false, color); 
   mc.attachBitmap(bmp, mc.getNextHighestDepth());
   mc._x = box.left;
   mc._y = box.top;
   mc._width = box.width;
   mc._height = box.height; 
  }
  // вызываем функцию рисования двух прямоугольников по очереди
  paintBox (2, boundingBox_2, 0x0000FF00);
  paintBox (1, boundingBox_1, 0x00FF0000);
  //теперь нарисуем область пересечения двух прямоугольников
  paintBox (3, boundingBox_2.intersection(boundingBox_1), 0x000000FF);
Результат работы показан на рисунке 3.

Name:  flash8_3.PNG
Views: 880
Size:  3.1 КБ

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


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

Итак, поехали.

В простейшем случае вектор это начальная точка и конечная. Их можно задать в actionscript, например, так (у меня все вектора – это объекты):


Код:
 var vector = {start: {x: 10, y: 10}, end: {x: 20, y: 40}};
Зная начальную и конечную точку вектор также можно определить как:


Код:
 var vector = {start: {x: 10, y: 10}, delta: {x: 10, y: 30}}
здесь delta – это объект хранящий внутри себя величины приращений которые нужно добавить к начальной точке для того чтобы вычислить конечную точку, очевидно, что:


Код:
 // вектор задается двумя точками - начальной и конечной
  

var vector = {start: {x: 10, y: 10}, end: {x: 20, y: 40}};
  

// вектор задается начальной точкой и величинами приращения, так чтобы попасть в 

конечную точку
  

var vector2 = {start: {x: 10, y: 10}, delta: {x: 10, y: 30}}
  

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

vector3 = {start: {x: vector.start.x, y: vector.start.y} , 
  delta : {x: vector.end.x - vector.start.x, y: vector.end.y - vector.start.y} };
  

// вектора совпали                
  trace (vector3.delta.x == vector2.delta.x); 
  trace (vector3.delta.y == vector2.delta.y);
Для каждого вектора можно рассчитать его длину. Длина считается по формуле пифагоровых штанов, например, так:


Код:
 vector3.len =  Math.sqrt(vector3.delta.x*vector3.delta.x + vector3.delta.y*vector3.delta.y);
  trace (vector3.len);
Также есть понятие нормализованного вектора или вектора направления. Это такой вектор, длина которого равна единице. Вектор направления можно получить из обычного вектора, путем деления величин приращения вектора на его длину.


Код:
 var vector4 = {start: {x: vector3.start.x, x: vector3.start.x}, ort: {x: vector3.delta.x / vector3.len,y: vector3.delta.y / vector3.len}};
  trace (vector4.ort.x + ", " + vector4.ort.y + " => "+     (vector4.ort.x*vector4.ort.x + vector4.ort.y*vector4.ort.y) );
В результате будет получено (как видите длина такого вектора действительно равна точно единице):


0.316227766, 0.948683298 => 1


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

Считая что, delta.x и delta.y – это катеты треугольника с углом 90’ то, угол между этими катетами определяется как:


Код:
 var angle_1=Math.atan2(vector3.delta.y, vector3.delta.x);
  

// не важно между чем определять углы - величинами приращения начальной точки
  var angle_2=Math.atan2(vector4.ort.y, vector4.ort.x);
  

// или составляющими нормализованного вектора
  trace (angle_1 + " == "  + angle_2);
  

// функция Math.atan2 возвращает величину угла именно в радианах, можно преобразовать ее в градусы
  var angle_d =angle_1*180/Math.PI;
  trace (angle_d);
  

// зная угол и длину вектора можно вычислить величины его приращений
  vector4.delta = {x : vector3.len * Math.cos (angle_1) , y : vector3.len * Math.sin (angle_1)};
  

// смотрите, числа снова совпали, мы прошли путь от величин приращения к углам и назад
  trace (vector4.delta.x +" == " + vector3.delta.x);
  trace (vector4.delta.y +" == " + vector3.delta.y);
Почти последнее нужное нам понятие – это нормаль к вектору.

Вектор-нормаль - это такой вектор, который строго перпендикулярен к нашему вектору, проще говоря расположен под 90 градусов. Очевидно что у каждого вектора есть целых два вектора-нормали, их называют правосторонние и левосторонние.

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


Код:
 // не забывайте, что в flash ось OY направлена сверху вниз
  vector4.left = {delta: {x: - vector4.delta.y, y: vector4.delta.x}};
  vector4.right = {delta: {x: vector4.delta.y, y: - vector4.delta.x}};
Теперь, усложним ситуацию и введем второй вектор (в оригинальной задаче столкновения точки и стены ведь были два вектора, так что избежать еще одного математического ликбеза не удастся).


Первая операция – сложение двух векторов, это очень простой случай. Например, если вы кидаете камень под углом 45 градусов, то на него действует сила вашего броска – это вектор №1, а также сила гравитации – это вектор №2. Реальная траектория полета будет чем-то промежуточным между этими двумя направлениями. Для получения результирующего вектора просто сложите соответствующие компоненты этих двух векторов, например, так:


Код:
 var vector5  ={delta: {x: vector2.delta.x + vector3.delta.x, y: vector2.delta.y + vector3.delta.y}};
Интересным является определение того, как два вектора ориентированы друг относительно друга, или какой между ними угол. Угол между двумя векторами определяется через их скалярное произведение.


Код:
  // создаем еще один вектор
  var vector6 = {ort: {x: Math.SQRT1_2, y: Math.SQRT1_2}, delta: {x: 10, y: 10}};
  

// вычислим длины участвующих в операции векторов
  vector6.len =  Math.sqrt(vector6.delta.x*vector6.delta.x + vector6.delta.y*vector6.delta.y);
  vector4.len =  Math.sqrt(vector4.delta.x*vector4.delta.x + vector4.delta.y*vector4.delta.y);
  

//  и вычисляем угол между этими двумя векторами
  var cos_alpha_1 = (vector4.delta.x*vector6.delta.x + vector4.delta.y*vector6.delta.y) / (vector4.len * vector6.len);
  

// если вычислять длину между нормализованными векторами, то не обязательно делить их на произведение длин
  var cos_alpha_2 = (vector4.ort.x*vector6.ort.x + vector4.ort.y*vector6.ort.y);
  trace (cos_alpha_1 + " == " + cos_alpha_2);
Теперь самое важное, что происходит при проецировании вектора vector4 на направление вектора vector6. Если нарисовать это на бумаге то станет видно, что направление проекции совпадет с направлением вектора vector6. Будьте внимательны, когда проецируете, линия проекции должна быть перпендикулярна не оси OX, а именно самому вектору vector6.


Код:
 var scalar_m = (vector4.delta.x*vector6.ort.x + vector4.delta.y*vector6.ort.y);
  var vector_4_on_6 = {delta: {x: scalar_m * vector6.ort.x , y: scalar_m * vector6.ort.y }};
  trace (vector_4_on_6.delta.x +" , " + vector_4_on_6.delta.y);
Что же сегодня мы неплохо поработали, фактически для решения задачи об отскоке точки от стены нам осталось только найти способ определить, где именно вектор точки пересечется с вектором стены. Затем мы сможем собрать все части головоломки вместе, ввести гравитацию, трение, потери энергии при отскоке и заставить все это работать вместе. При условии положительных откликов я также расскажу о моделировании поведения более сложных объектов.

Также возможно ввести методы моделирования на основе verlet-ов, позволяющие запрограммировать практически что угодно при незначительных затратах процессорных ресурсов.

© 2007, Николай Жишкевич
black zorro офлайн   Ответить с цитированием
 
Ответить


Дополнительно
Режимы отображения

Правила создания сообщений
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is Вкл
[IMG] Вкл
HTML Выкл

Быстрый переход

Polygon.by. Учебно-практический центр компьютерной графики.

Онлайн журнал о магии компьютерной графики и цифрового искусства «Magic CG»
Пакет инструментов для обеспечения качества 3D-контента для игр
Цвет — это что?
» Январь 2018
В П В C Ч П С
31 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 123

Текущее время 19:21 (GMT +4)


Powered by vBadvanced CMPS v3.0 RC1
Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Хостинг и облачные решения — Active Technologies

Все права защищены © 2006 — 2014, CGtalk.by