Новый nape. Testbed. И немного теории.

Новый nape:
1. Новый nape. Hello world
2. Новый nape. Обработка событий
3. Новый nape. Testbed. И немного теории. (Его вы сейчас читаете)
4. Новый nape. Создание тел, материалы, фильтрация столкновений
5. Новый nape. Соединение тел
6. Новый nape. Приложение сил к телам и raycast

Все примеры кода в Testbed: Скачать проект для FlashDevelop.

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

Cписки в nape

В nape вся физическая симуляция живет внутри экземпляра класса Space. Все тела, соединения, слушатели событий и все остальное работает только если добавлено в мир, т.е. в Space. У класса space несколько разных свойств, похожих по назначению — это списки, в которых перечислено все, что есть в мире:
  • bodies — в этом списке присутствуют все тела.
  • statics, dynamics, kinematics, liveBodies — в этих списках те же тела, что и в bodies, но только подходящие по смыслу к называнию списка, т.е. соответственно статические, динамические, кинематические (т.е. двигающиеся, но не по физике, а как вы захотите) и не спящие, активные тела.
  • constraints — это список всех соединений тел.
  • liveConstraints — это список не спящих соединений тел.
  • arbiters — это список арбитров, т.е. объектов создающихся самим движком nape, в случае если тела соприкасаются. В nape есть три типа таких арбитров: столкновений, плавания и сенсорные. Добавлять и удалять записи из этого списка нельзя, это делает сам nape.
  • callbacks — в этот список nape добавляет необработанные события. Я не рассматриваю этот метод обработки событий, т.к. использовать вызов функций при возникновении событий гораздо удобнее, а этот метод видимо просто достался в наследство от старого nape. Если им пользоваться, то после каждого шага надо проходить по этому списку обрабатывать события и очищать его.
  • listeners — здесь список всех добавленных слушателей событий.

Кроме того, например, геометрические фигуры Shape тоже живут внутри списка shapes, но это свойство класса Body, а не Space.

Все эти списки имеют похожие типы, отличающиеся только хранимыми объектами. К примеру, тела (Body) живут в BodyList, а геометрические фигуры (Shape) в ShapeList.

Добавление чего-либо в мир — это просто добавление этого чего-то в соответствующий список, а удаление — это удаление из соответствующего списка. Причем если мы удалим тело из списка dynamics, в котором хранятся все динамические тела, оно удалится и из списка bodies, в котором перечислены все тела вообще. Кроме того у тел, а также у других объектов живущих в классе Space, есть свойство space, присвоив ему значение мы автоматически добавим этот объект в соответствующий список, а присвоив этому свойству null — удалим его. У геометрических фигур (Shape) есть аналогичное свойство body. Таким образом получается, что добавление и удаление объектов можно делать так:

space.bodies.add(body); // самый очевидный способ добавить тело
body.space = space; // тоже добавит тело
space.dynamics.add(body); // если тело динамическое, то оно добавится, иначе будет ошибка

space.bodies.remove(body); // удаление тела
body.space = null; // так тело тоже удалится
space.dynamics.at(0).space = null; // так к примеру удалится самое первое динамическое тело


Перебрать все значение в списке тоже можно:

for(var i:int = 0; i < space.bodies.length; ++i)
    trace(space.bodies.at(i));

Оператор for each к сожалению не работает.

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

Testbed

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

Код простого теста:
package Tests
{
        import com.bit101.components.*;
        import nape.dynamics.*;
        import nape.geom.*;
        import nape.phys.*;
        import nape.shape.*;
        import nape.space.*;
        import nape.util.*;
        
        public class Test0 extends Test
        {
                private var bodyCount:HUISlider;
                
                private var superBody:Body;
                
                public function Test0() 
                {
                        super("Test #0");
                        
                        createWalls(Material.steel());
                        
                        superBody = new Body(BodyType.DYNAMIC, new Vec2(400, 300));
                        superBody.shapes.add(new Circle(70, null, Material.steel()));
                        superBody.align();
                        space.dynamics.add(superBody);
                        superBody.allowRotation = false;
                }
                
                override public function createUI(parent:VBox):void
                {
                        bodyCount = new HUISlider(parent);
                        bodyCount.label = "bodies";
                        bodyCount.minimum = 0;
                        bodyCount.maximum = 300;
                        bodyCount.tick = 1;
                        bodyCount.value = 50;
                        
                }
                
                override public function update():void
                {
                        while (bodyCount.value + 1 > space.dynamics.length)
                        {
                                var b:Body = new Body(BodyType.DYNAMIC, new Vec2(Math.random() * 700 + 50, Math.random() * 500 + 50));
                                var s:Shape = new Polygon(Polygon.regular(Math.random() * 80 + 20, Math.random() * 80 + 20, 
                                        Math.round(Math.random() * 4 + 3), 0), Material.rubber());
                                s.body = b;
                                b.align();
                                b.space = space;
                        }
                        
                        while (bodyCount.value + 1 < space.dynamics.length)
                        {
                                if(space.dynamics.at(0) != superBody)
                                        space.dynamics.at(0).space = null;
                                else
                                        space.dynamics.at(1).space = null;
                        }
                        
                        var collidingBodies:String = "";
                        for (var i:int = 0; i < superBody.arbiters.length; ++i)
                        {
                                var arbiter:Arbiter = superBody.arbiters.at(i);
                                if (arbiter.isCollisionArbiter())
                                {
                                        var another:Body = arbiter.body1 == superBody ? arbiter.body2 : arbiter.body1;
                                        if (another.isDynamic())
                                        {
                                                collidingBodies += another.toString();
                                        }
                                }
                        }
                        if (collidingBodies.length > 0)
                                trace(collidingBodies);
                }
        }

}


Думаю, пояснять каждую строчку не нужно. Чтобы создать тест, нужно создать класс, унаследовав его от Test. В конструкторе можно создать всякие тела, метод update вызывается каждый кадр, метод createUI служит для создания всяких кнопочек и слайдеров (я использовал библиотеку MinimalComps.)

В этом тесте создается слайдер для количества тел, и каждый шаг, если количество не соответствует, создаются или удаляются тела. Кроме того создано тело superBody, и каждый шаг проверяется с какими телами оно соприкасается.

Результат: (Найдите с помощью кнопок Next и Prev тест с названием Test 0. Также предлагаю посмотреть на приблизительный аналог крана из Mining Truck 2 Антона Карлова — тест с названием Crane, очень мне в душу эти краны запали.)
  • +23

Комментарии (11)

+1
Спасибо, вы делаете хорошее дело.
0
+1 Спс!
0
+1
в testbed счетчика fps не хватает
0
В правый верхний угол посмотри :-)
0


правый верхний там? а то я уже начал в себе сомневаться :)
0
Да, где-то там :-)
Как поправить не знаю, кроме как переделывать флешку на меньшую ширину.
+1
На широком мониторе все ок.
0
правый верхний там? а то я уже начал в себе сомневаться :)
Из-за своих размеров флешка видна полностью только при разрешении монитора 1400 и больше. Как самый простой выход, можно глянуть её отдельно по этой ссылке:
dl.dropbox.com/u/11399208/NapeTestbed.swf
+1
поправил
0
Отличный пост, спасибо.
0
молодчина, здоровский пост!!!
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.