« FOAM: физика и actionscript 3. Часть 2 | Velocity Фильтр » |
FOAM: физика и actionscript 3. Часть 2
Сегодня я завершу рассказ об FOAM. FOAM - это один из лучших физических движков доступных для flash-разработчиков. В прошлый раз я рассказал об идее легшей в основу FOAM (четкое разделение ответственности за расчеты между несколькими частями движка), об иерархии интерактивных объектов (которые мы можем добавить на сцену). Сегодня осталось только рассмотреть методику добавления к объектам, воздействующих на них сил и ограничений. Также интересен вопрос о создании renderer-ов, (пользовательских объектов позволяющих управлять тем, как “голый” физический объект можно визуализировать).В прошлой статье я рассказал об двух основных объектах-примитивах, из которых будет строиться сцена: RigidBody и Circle. Помимо них есть еще третий объект CubicBezierCurve (кривая Безье). Однако практического смысла в этом объекте нет, т.к. фактически кривая строится из нескольких линий сегментов, каждый из которых представляется в виде RigidBody (так что, правильнее было бы назвать ее не кривой, а “ломаной”). Если вы еще не сталкивались с кривыми Безье во flash, то я рекомендую посмотреть библиотечку bezier.ru/blog/. Автор FOAM пишет, что в ближайшее время он перепишет код объекта CubicBezierCurve, но (как это часто бывает для проектов на “голом” энтузиазме), скорее всего, это будет еще не скоро. Так что строить свои “физические мирки” вам придется только из кругов и полигонов. Не забывайте также, что полигоны должны быть выпуклыми, в противном случае, в ходе симуляции возникнут забавные эффекты. Например, когда я создал круг, падающий на полигон в виде фантика конфеты, то столкновение произошло гораздо раньше, чем надо (см. рис. 1).
Однако больших трудностей в построении моделей ограничение на форму полигона не имеет т.к. почти любую форму можно составить нескольких “правильных” полигонов. Для построения сложной модели гораздо важнее наличие средств задать связь между объектами. Посмотрим, что есть в копилке FOAM? Средства связи в FOAM построены как расширения генераторов силы. Фактически, если вы связываете два объекта между собой, то это значит, что когда некоторая сила воздействует на первый объект, то она посредством связи передает часть себя и воздействует на второй объект. О генераторах силы (классы поддерживающие интерфейс IForceGenerator) я говорил в прошлой статье, однако напомню, что на физические объекты в FOAM можно оказать воздействие двумя способами: либо посредством силы (у каждого объекта есть метод addForce или addForceAtPoint) либо с помощью генератора силы (addForceGenerator). Отличие этих воздействий в их продолжительности: addForce разово применяет силу к объекту (например, взрыв), в то время как addForceGenerator присоединяет к объекту силу, действующую на протяжении некоторого времени. Использование ForceGenerator является предпочтительным т.к. позволяет гибко задать алгоритм изменения силы во времени. Вы можете создать и собственный генератор силы, если стандартные вас не устраивают. Для этого нужно создать класс, реализующий интерфейс IForceGenerator и определить в нем метод:
public function generate( element:ISimulatable ) : void
В копилке FOAM есть четыре класса реализующих интерфейс IForceGenerator.
SimpleForceGenerator – самый простой генератор силы, оказывающий одинаковое по величине воздействие на объект. Величина силы задается как параметр конструктора.
Gravity – этот генератор также получает в качестве параметра конструктора значение применяемой к объекту силы, но ее воздействие на объект возрастает с увеличением массы объекта.
Friction – имитирует силу трения. При создании генератора задается величина коэффициента трения, затем сила рассчитывается по формуле “- скорость * массу * коэффициент”. Для тел (IBody) добавляется и вращательный момент.
KeyDrivenTorqueGenerator - этот генератор применяет к объекту вращательный момент в зависимости от того, какая кнопка на клавиатуре была нажата. По умолчанию, направление вращения задается как влево и вправо, но это можно изменить.
В примере вместе c библиотекой FOAM идет еще один генератор GravitationalForceGenerator. В качестве параметра конструктора для него, задается некоторый объект, играющий роль “центра притяжения”. Когда вы применяете этот генератор к произвольному объекту (один генератор может “обслуживать” любое количество объектов), то он начинает притягиваться к центру притяжения с учетом массы объектов и расстояния между ними: F = G * m1 * m2 / r^2.
Применить генератор силы можно не только к одному или нескольким объектам на сцене, но и к самому объекту FOAM. Тогда при добавлении объекта на сцену к нему автоматически будет присоединен и генератор. Что касается тех объектов, которые уже есть на сцене, то будет ли присоединен к ним генератор, зависит от значения второго параметра (по умолчанию true) метода addGlobalForceGenerator.
добавляем на сцену круг
foam.addElement (new Circle (100, 100, 25, 20));
генератор силы должен быть применен к новым объектам
foam.addGlobalForceGenerator (new SimpleForceGenerator (new Vector (1, 1)), false);
например, к вот этому кругу
foam.addElement (new Circle (200, 100, 25, 20));
var circ : Circle = new Circle (300, 100, 25, 20);
применим к объекту персональный генератор силы
circ.addForceGenerator (new SimpleForceGenerator (new Vector(0,-1)));
foam.addElement(circ);
Теперь перейдем к ограничениям (связям между объектами). Они представлены классами Spring, Bungee и RigidBodySpring, RigidBodyBungee. “Резинка” (Spring) представляет собой однонаправленную (это важно) связь между двумя телами (их центрами). Например, в следующем примере я создам два круга и связь (Spring) между ними, затем первый круг начнет перемещение по сцене вниз. Как только это произойдет, то начнет движение и второй круг.
создаем два круга
var circ1 : Circle = new Circle (100, 100, 25, 20);
foam.addElement(circ1);
var circ2 : Circle = new Circle (300, 100, 25, 20);
foam.addElement(circ2);
и связь между ними
var spring1 : Spring = new Spring (circ2, circ1);
теперь заставим первый круг перемещаться
сirc1.addForce (new Vector (0, 20));
new Spring (circ2, circ1, 0.02, 0.1);
foam.addRenderable (new Renderable (spring1));
var spring1 : RigidBodySpring = new RigidBodySpring (circ1, new Vector(10, 10), circ2, new Vector( -10, 10));
var bungee1 : Bungee = new Bungee (circ2, circ1, 0.02, 0.1);
так же как и для резинки для амортизатора есть разновидность RigidBodyBungee
var bungee1 : RigidBodyBungee = new RigidBodyBungee(circ1, new Vector(10, 10), circ2, new Vector( -10, 10));
stage.addEventListener (KeyboardEvent.KEY_DOWN,
function (keyEvt: KeyboardEvent):void{
if (keyEvt.keyCode == 81){ анализируем клавишу
if (lastMode) переключаем режим на противоположный
foam.setRenderer (new DisplayObjectFoamRenderer ());
else
foam.setRenderer( new SimpleFoamRenderer() );
lastMode = ! lastMode;
}
}
);
Для демонстрации работы с renderer-ами создадим две картинки: баскетбольный мяч и корзина, сохранив их с именами ball.png и basket.png. Теперь попробуем создать два Foam объекта круг и прямоугольник, и заменим их графическое представление с помощью этих картинок. Для этого в качестве полей главного класса приложения я объявил графические ресурсы:
[Embed(source = 'ball.png')]
private var BALL_SPRITE_CLASS : Class;
[Embed(source = 'basket.png')]
private var BASKET_SPRITE_CLASS : Class;
вам следует импортировать в библиотеку эти картинки и программно создать их экземпляры, чтобы затем подать их как параметр объекту renderable. Фактически я мог использовать в качестве графического наполнения не просто внешний файл, но и клип из flash-файла. Например, так:
[Embed(source='foamarticle.swf', symbol='BALL_MOVIECLIP)]
private var BALL_SPRITE_CLASS : Class;
[Embed(source='foamarticle.swf', symbol='BASKET_MOVIECLIP)]
private var BASKET_SPRITE_CLASS : Class;
это мяч (физическая модель)
var ball : Circle = new Circle (100, 100, 25, 20);
создаем объект графического представления
var ballSprite: DisplayObject = new BALL_SPRITE_CLASS();
масштабируем картинку так, чтобы ее размер совпал с удвоенным радиусом шара
ballSprite.width = ballSprite.height = 50;
/* добавляем в движок объект-мяч, также указываем то что объект будет обсчитываться физическим движком,
то что он будет отображен на экране и последний параметр задает информацию об клипе,
который будет визуализировать объект */
foam.addElement( ball, true, true, new DisplayObjectData( ballSprite ) );
обводка начального положения мяча
foam.addElement(new Circle (100, 100, 28, 20), false);
заставляем мяч падать вниз на корзину
ball.addForce (new Vector (0, 20));
физическая модель корзины
var basket : RigidBody = new RigidBody (100, 400, 10, ShapeUtil.createRectangle(200, 180));
создаем объект графического представления
var basketSprite: DisplayObject = new BASKET_SPRITE_CLASS();
подгоняем картинку под размер корзины
basketSprite.width = 200;
basketSprite.height = 180;
foam.addElement( basket, true, true, new DisplayObjectData( basketSprite ) );
обводка начального положения корзины
foam.addElement(new RigidBody (100, 400, 10, ShapeUtil.createRectangle(200, 180)), false);
теперь изменяем renderer на новый
foam.setRenderer (new DisplayObjectFoamRenderer ());
В ходе “доделок” Foam (а они обязательно будут) вы часто будете сталкиваться с объектом Vector. Этот объект используется почти везде, где нужно задать координаты объекта, его скорость, силу, действующую на объект. Однако помимо свойств x,y в составе Vector-а есть ряд методов реализующих “классические” операции над векторами:
Операция сложения векторов реализуется либо методом plusEquals, либо plus. В любом случае нужно передать как параметр методу ссылку на “добавляемый” вектор. Однако в первом случае результат сложения будет присвоен объекту вектору от имени которого был вызван метод plusEquals.
var v1 : Vector = new Vector (1, 2);
var v2 : Vector = new Vector (1, 2);
var v3 : Vector = v1.plus (v2);
v2.plusEquals (v3);
var body : IBody = foam.engine.getBodyUnderPoint(new Vector (100, 100));
На этом рассказ об Foam я буду считать завершенным. Я считаю главным, чтобы вы понимали, что нет идеального физического движка (когда они были, эти “серебряные пули”?) и вам в любом случае придется добавлять отсутствующую (специфическую для вас) функциональность самостоятельно. Foam, как каркас для написания собственного движка, показался мне очень неплохим решением.
Несколько видеороликов:
Пример неправильно обработки полигонов со сложной формой:
Пример Spring:
Пример Bungee:
« FOAM: физика и actionscript 3. Часть 1 | Системы управления версиями для программистов и не только. Часть 1 » |