Статьи

Введение в QuickBox2D: Часть 2

Это вторая часть серии из трех частей о QuickBox2D. Рекомендуется прочитать каждую часть этой серии по порядку. Так что, если вы не читали первую часть, вы должны сделать это сейчас. Вы можете проверить это здесь .

В этом уроке мы рассмотрим некоторые функции QuickBox2D более среднего уровня. Мы увидим, как создать многоугольные твердые тела. Мы узнаем, как точно настроить наше моделирование, используя дополнительные значения параметров, такие как реституция, linearDamping и angularDamping. Мы обсудим некоторые мощные методы Box2D, предоставляемые QuickBox2D, и поговорим о FRIM (независимое от частоты кадров движение).

Напоминание : это само собой разумеется, но не забудьте также поставить библиотеки Box2D и QuickBox2D
рядом с вашим файлом .fla или в вашем пути к классам.

Из этого туториала вы узнаете, как создать симуляцию среднего размера, разработанную с учетом конкретных аспектов QuickBox2D и Box2D. Проверьте демо и снимите немного стресса.


Откройте Flash и вставьте следующий код в действия для первого кадра:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[SWF(width = 800, height=600, frameRate=60, backgroundColor=0x000000)];
 
import com.actionsnippet.qbox.*;
 
var sim:QuickBox2D = new QuickBox2D(this);
 
/**——————————
  — insert variable definitions below this comment
  ——————————*/
 
// all the code to build rigid bodies
// is contained within this function
buildSimulation();
 
sim.start();
sim.mouseDrag();
 
function buildSimulation():void {
     
    // set some default colors and create stage walls
    sim.setDefault({fillColor:0x113366, fillAlpha:0.5, lineColor:0x3355AA});
    sim.createStageWalls();
     
/**——————————
   — insert ridgid body instantiation code below this comment
   ——————————*/
 
}

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

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

1
2
3
/**——————————
 — insert variable definitions below this comment
 ——————————*/

Для простоты мы также создали функцию buildSimulation (), чтобы обернуть все наши экземпляры твердого тела. Эта функция вызывается перед методами start () и mouseDrag () в QuickBox2D. Он содержит некоторые настройки цвета по умолчанию для наших твердых тел, а также вызов createStageWalls (). Большая часть кода, который мы рассмотрим в этом руководстве, должна быть вставлена ​​прямо под комментарием, который гласит:

1
2
3
/**——————————
  — insert ridgid body instantiation code below this comment
  ——————————*/

Я предполагаю, что вы знаете разницу между выпуклыми, вогнутыми и сложными полигонами. Если вы не знаете или не помните разницу, вы можете взглянуть на статью в Википедии на эту тему.

QuickBox2D позволяет определять полигоны двумя способами. Первый способ использует автоматическую триангуляцию многоугольника, которая позволяет просто определить координаты x и y для контура многоугольника. Нет ограничений на количество точек в вашем контуре. Вставьте следующий код в функцию buildSimulation () и протестируйте свой фильм:

1
2
3
4
5
// triangle
sim.addPoly({x:3, y:17, points:[0,0, 1,1, 0,1, 0,0]});
        
// concave poly
sim.addPoly({x:7, y:17, points:[1,0, 2,2, 1,1.2, 0, 2]});

Приведенный выше код определяет два полиса. Первая строка определяет простой прямоугольный треугольник, который позиционируется в (3,17), а следующая строка определяет вогнутый многоугольник, который позиционируется в (7,17). Мы используем параметр под названием points который представляет собой массив координат x и y. Одномерные массивы координат XY могут быть сложными, приведенный ниже рисунок должен устранить любую путаницу
может быть о коде, который мы только что добавили:

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

Вы заметите, что второй многоугольник состоит из двух треугольников. Это код триангуляции QuickBox2D на работе, он в основном разбивает ваш контур на кучу треугольников. Если вы не хотите видеть каждый треугольник, используйте параметр wireframe . Попробуйте изменить вогнутый поли-код следующим образом:

1
2
// concave poly
sim.addPoly({x:7, y:17, wireframe:false, points:[1,0, 2,2, 1,1.2, 0, 2]});

Если вам нужно создавать более сложные полигоны, вы, вероятно, должны создать какой-то простой редактор полигонов. Пример такого редактора есть на actionsnippet.com . Вы можете взглянуть на это в течение нескольких минут, прежде чем читать дальше. Это должно помочь вам понять, как QuickBox2D триангулирует полигоны.


Существует другой способ создания полигонов с использованием QuickBox2D, который немного сложнее. Если вы очень заинтересованы в производительности вашего моделирования, вы можете рассмотреть возможность использования этого метода по сравнению с ранее обсужденным методом. Вы также можете использовать этот метод, если вам нужно создать простой выпуклый поли, такой как трапазоид. Добавьте следующий код в функцию buildSimulation ():

1
sim.addPoly({x:13, y:17, verts:[[-1,-1, 1,-1, 2, 1, -2, 1]]});

Этот код рисует трапазоид. Проверьте свой фильм и посмотрите.

Существует несколько различий между параметром verts и параметром points . Очевидное отличие состоит в том, что массив verts является двумерным массивом. Каждый вложенный массив содержит список координат xy, этот массив можно использовать для определения до восьми вершин. Вы не можете определить вогнутый или сложный многоугольник, используя один вложенный массив. Вместо этого вы должны определить серию выпуклых многоугольников (опять же, каждый из которых имеет максимум восемь вершин). Вы должны определить каждый выпуклый многоугольник по часовой стрелке, начиная с самой верхней точки и возвращаясь назад. Это может быть сложно, но это проще, чем кажется. Добавьте следующий код в функцию buildSimulation () и протестируйте свой фильм:

1
2
3
4
sim.addPoly({x:18, y:15, verts:[[0,-1, 1,1, -1,1],
                                [1,1, 2,1.5, 1,2],
                                [1,2, 0,3, -1,3, -1,2],
                                [-1.5,2, -1,2.5, -1,3, -2,3, -2,2.5]]});

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


Реституция подобна эластичности или упругости твердого тела. По умолчанию реституция установлена ​​где-то между 0-1, но допускаются более высокие значения. Давайте посмотрим, что можно сделать с помощью реституции, скопируем следующий код в функцию buildSimulation () и протестируем ваш фильм:

01
02
03
04
05
06
07
08
09
10
// perpetually bouncing ball
    sim.addCircle({x:3, y:13, radius:1, restitution:1});
     
    // set restutution to 1.2 to create a spring board
    sim.addBox({x:24, y:15, width:3, height:0.25,
                fillColor:0xDD0000, lineAlpha:0,
                angle:0.1, restitution:1.2, density:0});
                 
    // normal circle
    sim.addCircle({x:24, y:10, radius:0.5});

Первое, что мы создаем с помощью этого кода, это круг, который отскакивает навсегда. Установив параметр restitution равным 1, он не перестанет подпрыгивать, пока остается один. Следующая вещь, которую мы создаем, это красная коробка, которая слегка повернута, имеет плотность 0 (является статической) и имеет реституцию 1.2. Восстановление 1.2 делает так, что отскочившие от него вещи получат некоторую энергию. Чтобы проверить это, мы добавляем кружок прямо над ним. Если вы внимательно посмотрите на этот круг, вы заметите, что каждый раз, когда он попадает в красную коробку, он подпрыгивает немного выше, пока, наконец, не достигнет потолка и не отскочит от красной коробки.

Реституция — это действительно первый шаг к тому, чтобы заставить ваши твердые тела действовать, как будто они сделаны из разных материалов. Восстановление 0 было бы хорошо для твердого тела, которое должно быть сделано из камня; восстановление 0,8 было бы хорошо для чего-то сделанного из очень упругой резины.


Если вы хотите предотвратить слишком быстрое перемещение или вращение твердого тела, вы можете уменьшить его линейную или угловую скорость. Добавьте этот код в функцию buildSimuation () и протестируйте свой фильм.

1
2
3
4
5
6
7
8
// green box
sim.addBox({x:3, y:5, width:1, height:1, angularDamping:5, fillColor:0x22CC00});
 
// red box
sim.addBox({x:8, y:5, width:1, height:1, fillColor:0xFF0000});
 
// small platform
sim.addBox({x:6, y:8, width:10, height:0.25, density:0});

Здесь мы добавляем зеленое поле со значением angularDamping, равным 5. Это приведет к тому, что поле gren очень быстро перестанет вращаться при броске. Для сравнения мы добавили красное поле и оставили его значение angularDamping по умолчанию равным 0. Так как симуляция начинает немного загромождаться, мы добавили небольшую платформу под этими двумя полями, чтобы их было легко перемещать и наблюдать Разница между ними.

Линейное демпфирование аналогично angularDamping, но для перевода. Давайте добавим angularDamping в зеленое поле:

1
2
// green box
sim.addBox({x:3, y:5, width:1, height:1, angularDamping:5, linearDamping:5, fillColor:0x22CC00});

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

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


Важно отметить, что установка параметров angularDamping или linearDamping выше 1 сделает их чувствительными к timeStep. Это может ничего не значить для вас в данный момент, но мы обсудим это позже в этом уроке.


В Box2D трение обычно составляет 0-1. По умолчанию все QuickObjects имеют трение 0,5. Если мы уменьшим это значение, все станет очень скользким. Добавьте приведенный ниже код в функцию buildSimulation () и протестируйте свой фильм:

1
2
3
4
5
6
7
8
9
// gray box
sim.addBox({x:3, y:9, width:1, height:1, friction:1, fillColor:0xCCCCCC});
 
// light blue box
sim.addBox({x:8, y:9, width:1, height:1, friction:0, fillColor:0xCCCCFF, fillAlpha:1});
 
 // small platform
 
sim.addBox({x:6, y:12, width:10, height:0.25, density:0});

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

Возникновение трений всегда заставляет меня думать о ледяных уровнях этих старых прокруток Nintendo …


Box2D имеет нечто, называемое CCD. Это более точная, более ресурсоемкая форма обнаружения столкновений. Если вы хотите использовать этот тип столкновения на isBullet теле, вам нужно использовать параметр isBullet . Как следует из названия, вы часто используете ПЗС для пуль и других быстро движущихся твердых тел. Скопируйте приведенный ниже код в свой buildSimulation ():

01
02
03
04
05
06
07
08
09
10
11
var boxes:Array = [];
    boxes[0] = sim.addBox({x:1, y:0, width:2, height:0.25});
    boxes[1] = sim.addBox({x:1, y:2, width:2, height:0.25});
    boxes[2] = sim.addBox({x:0, y:1, width:0.25, height:2.25});
    boxes[3] = sim.addBox({x:2, y:1, width:0.25, height:2.25});
    sim.addGroup({x:12, y:6, objects:boxes});
                                 
    // if isBullet is false, circles will fall out of box
    for (var i:int = 0; i<4; i++){
        sim.addCircle({x:13, y:7, radius:0.2, isBullet:true});
    }

Здесь мы создаем пустое поле, используя групповой объект. Затем мы помещаем четыре маленьких кружка в пустое поле и устанавливаем для их свойства isBullet значение true. Это предотвращает стрельбу маленьких кругов из группового объекта пустотелого ящика. Проверьте это, бросив пустую коробку вокруг симуляции как можно быстрее. Вы заметите, что все круги остаются внутри стенок пустотелого ящика. Теперь попробуйте установить для параметра isBullet значение false и сделать то же самое, почти сразу же вы увидите, как круги разлетаются повсюду.

Стоит отметить, что ПЗС автоматически используется на статических телах.


Параметр fixedRotation очень прост для понимания. Это логическое значение, которое при значении true будет препятствовать вращению твердого тела. Это полезно для предотвращения падения персонажа в игре. Добавьте этот код в функцию buildSimulation () и протестируйте свой фильм:

1
sim.addBox({x:19, y:3, width:1, height:2, fillColor:0xFFFFFF, fixedRotation:true});

Этот код создает белое поле (оно слегка серое из-за альфа-значения по умолчанию 0,8) с fixeRotation установленным в true. Если вы попытаетесь бросить его, вы заметите, что он не вращается. Довольно просто, а?

Стоит отметить, что вы все еще можете установить angle параметра. Попробуйте это:

1
sim.addBox({x:19, y:3, width:1, height:2, fillColor:0xFFFFFF, fixedRotation:true, angle:1});

Твердые тела Box2D по умолчанию засыпают, чтобы сохранить процессор. Этот акт засыпания происходит, когда твердое тело остановилось. Иногда вы хотите предотвратить засыпание твердого тела с помощью логического значения allowSleep (хороший пример этого мы увидим позже в руководстве). Иногда хочется засыпать твердым телом. Для этих случаев вы используете параметр isSleeping. Это может быть неочевидно, но isSleeping можно использовать для создания разрушаемых объектов. Добавьте этот код в функцию buildSimulation () и протестируйте свой фильм:

01
02
03
04
05
06
07
08
09
10
11
12
/*———————————
      — Step 10: allowSleep
     ———————————*/
    // create 10 boxes in a grid formation
    for (i = 0; i<10; i++){
        sim.addBox({x:13 + (i % 2) * 0.5,
                    y:3 + int(i / 2) * 0.5,
                    width:0.5, height:0.5,
                    lineAlpha:0,
                    fillColor:0x4444FF,
                    isSleeping:true});
    }

Этот код создает 10 блоков в сетке, которые не имеют обводки и окрашены в насыщенный синий цвет. Параметр isSleeping по умолчанию имеет значение true, isSleeping ящики остаются в исходном положении до тех пор, пока что-то еще не ударит их и не разбудит. Если вы бросите другое твердое тело в сетку ящиков, вы заметите, что мы создали иллюзию хрупкого твердого тела.

Если вы не знакомы с техникой, которую мы используем для упорядочения блоков в сетке, вы можете прочитать этот учебник об упорядочивании DisplayObject в формированиях сетки на сайте learningActionScript3.com.


Параметр groupIndex может использоваться для более точного управления обработкой столкновений между твердыми телами. Это целое число, которое является либо отрицательным, либо положительным. Если два твердых тела имеют одинаковое отрицательное значение groupIndex, они не будут сталкиваться друг с другом, если они имеют одинаковое положительное значение groupIndex, они будут сталкиваться. Добавьте этот код в функцию buildSimulation ():

1
2
3
4
5
6
7
for (i = 0; i<4; i++){
      sim.addBox({x:2 + i , y:2, fillColor:0xFFFF00,
                 width:0.5, height:1.5, groupIndex:-1});
    }
     
    // small platform
    sim.addBox({x:6, y:4, width:10, height:0.25, density:0});

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

Я использую этот groupIndex совсем немного. Это важно при работе со сложным моделированием. Есть два других связанных параметра, называемых maskBits и categoryBits . Мне это еще очень нужно, но вы можете прочитать о них больше в руководстве по Box2D, если вам интересно.


Как упомянуто в части 1 этого урока. Каждый метод создания (addBox (), addPoly () и т. Д.) Возвращает объект QuickObject. Если вы хотите начать отслеживать элементы в симуляции, вам нужно хранить ссылки QuickObject в переменных. Мы собираемся начать настройку твердого тела, которое будет управляться клавишами со стрелками. Для этого нам нужно немного подготовиться. Сначала добавьте это определение переменной в ваш код (не забудьте поместить его под комментарием определения переменной вне функции buildSimulation ()).

1
2
3
4
/**——————————
  — insert variable definitions below this comment
  ——————————*/
var character:QuickObject;

Затем добавьте следующий код в функцию buildSimulation ():

1
2
3
var charPartA:QuickObject = sim.addBox({x:0, y:0, width:2, height:0.8});
var charPartB:QuickObject = sim.addBox({x:0, y:0, width:0.8, height:2});
character = sim.addGroup({objects:[charPartA, charPartB], x:8, y:3, angularDamping:0.8, linearDamping:0.8, allowSleep:false});

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

ПРЕДУПРЕЖДЕНИЕ. Эти следующие несколько шагов становятся немного сложнее, поэтому обратите пристальное внимание на каждую инструкцию. Если вы начинаете беспокоиться о том, что не можете следить за всеми изменениями в коде, после того, как сложная часть закончена, вы можете загрузить полную версию fla для сравнения.


Для некоторых типов управления ключами только использование KeyboardEvents не приведет к его сокращению. Обычно я буду использовать что-то вроде этого:

1
2
3
4
5
6
7
8
9
var key:Object = new Object();
stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyReleased);
function onKeyPressed(evt:KeyboardEvent):void {
    key[evt.keyCode] = true;
}
function onKeyReleased(evt:KeyboardEvent):void {
    key[evt.keyCode] = false;
}

Этот код создает объект (ассоциативный массив или карту), называемый key . Когда клавиша нажата в первый раз, свойство key динамически создается для key объекта, для имени свойства используется keyCode, и, поскольку мы находимся в событии KEY_DOWN, свойство устанавливается в значение true. Когда происходит событие KEY_UP, вновь созданное свойство устанавливается в значение false. Вы должны скопировать и вставить этот код полностью внизу вашего скрипта на этом этапе.

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

1
2
3
if (key[Keyboard.UP]){
  // make the character jump
}

Теперь при использовании цикла в сочетании с QuickBox2D вы можете прослушивать Event.ENTER_FRAME, если хотите, но более поздние версии QuickBox2D имеют специальный тип события, называемый QuickBox2D.STEP. Вы всегда должны использовать QuickBox2D.STEP в пользу Event.ENTER_FRAME, об этом мы немного поговорим, когда перейдем к FRIM. Внутри вашей функции buildSimulation () вы должны добавить прослушиватель для этого события:

1
sim.addEventListener(QuickBox2D.STEP, onStep);

Нам также нужно добавить функцию слушателя прямо под нашей функцией buildSimulation ():

1
2
3
4
5
6
7
8
9
sim.addEventListener(QuickBox2D.STEP, onStep);
} // end of our buildSimulation() function
 
// — copy from this point on…
// our listener function
function onStep(evt:Event):void {
   // we’ll add some key control stuff here soon
}

Хорошо, теперь мы почти готовы начать делать некоторые движения, управляемые клавишами …


Все объекты QuickObject имеют свойство body . Это свойство содержит ссылку на экземпляр Box2D b2Body. Представляя этот экземпляр b2Body, QuickBox2D дает вам доступ к множеству методов Box2D. Мы собираемся использовать GetLinearVelocity () и SetLinearVelocity (). Полный список всех методов b2Body можно посмотреть здесь .

Линейная скорость твердого тела определяется двумерным вектором, значения x и y этого вектора складываются с положением x и y данного твердого тела. Линейная скорость твердого тела изменяется со временем из-за гравитации, столкновений с другими объектами, трения и т. Д. В Box2D есть класс, специально разработанный для работы с векторами, который называется b2Vec2. Метод SetLinearVelocity () принимает b2Vec2 в качестве первого и единственного аргумента. Нам нужно импортировать b2Vec2, поэтому добавьте эту строку под оператором импорта для QuickBox2D:

1
2
3
4
5
// you already have this line
import com.actionsnippet.qbox.*
 
// add this line:
import Box2D.Common.Math.*;

b2Vec2 содержится в пакете Math. Теперь давайте посмотрим на SetLinearVelocity (). Нам нужно добавить код внутри функции слушателя onStep ():

1
2
3
4
5
6
7
// you should already have this function defintion
function onStep(evt:Event):void {
   // add this conditional
   if (key[Keyboard.RIGHT]){
      character.body.SetLinearVeloctiy(new b2Vec2(10, 0));
   }
}

Попробуйте проверить свой фильм и нажать правую клавишу. Твердое тело персонажа должно плавать справа от экрана. Давайте добавим код для левой клавиши, добавим следующий код в функцию onStep ():

1
2
3
4
// add this:
 if (key[Keyboard.LEFT]){
      character.body.SetLinearVeloctiy(new b2Vec2(-10, 0));
 }

Вы можете попробовать возиться со значениями x и y b2Vec2, чтобы понять, как он работает.

Это довольно мило, но движение довольно роботизированное. Есть несколько вещей, которые мы можем сделать, чтобы эти ключевые элементы управления работали немного лучше. Первое, что мы можем сделать, это просто организовать. Не всегда удобно и гибко постоянно создавать новые экземпляры b2Vec2, поэтому давайте добавим переменную для работы с линейной скоростью персонажа. Добавьте это определение переменной в ваш код:

1
2
3
4
5
// you already have this
var character:QuickObject;
 
// add this line
var charVel:b2Vec2 = new b2Vec2();

Если вы ничего не передаете конструктору b2Vec2, свойства x и y по умолчанию равны 0. Затем измените функцию onStep (), чтобы она выглядела следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
function onStep(evt:Event):void {
    charVel = character.body.GetLinearVelocity();
     
    if (key[Keyboard.RIGHT]){
        charVel.x += 0.8;
        character.body.SetLinearVelocity(charVel);
    }
    if (key[Keyboard.LEFT]){
        charVel.x -= 0.8;
        character.body.SetLinearVelocity(charVel);
    }
    if (key[Keyboard.UP]){
         charVel.y = -10;
         character.body.SetLinearVelocity(charVel);
    }
    if (key[Keyboard.DOWN]){
         charVel.y = 0.8;
         character.body.SetLinearVelocity(charVel);
    }
}

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

В начале функции onStep () мы вызываем метод GetLInearVelocity (). Мы делаем это для того, чтобы не принудительно изменять линейную скорость, сбрасывая все значения. Вместо этого мы читаем линейную скорость и изменяем ее в соответствии с вводом с клавиатуры. Когда мы хотим двигаться вправо или влево, мы просто слегка изменяем свойство x (на 0,8 метра). Когда мы движемся вверх, нам нужно немного больше силы из-за силы тяжести, поэтому мы изменяем свойство y charVel на -10. Когда мы движемся вниз, гравитация работает в нашу пользу, поэтому мы можем снова использовать 0,8 метра.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function onStep(evt:Event):void {
    charVel = character.body.GetLinearVelocity();
    keyControls();
}
 
function keyControls():void{
   if (key[Keyboard.RIGHT]){
        charVel.x += 0.8;
        character.body.SetLinearVelocity(charVel);
    }
    if (key[Keyboard.LEFT]){
        charVel.x -= 0.8;
        character.body.SetLinearVelocity(charVel);
    }
    if (key[Keyboard.UP]){
         charVel.y = -10;
         character.body.SetLinearVelocity(charVel);
    }
    if (key[Keyboard.DOWN]){
         charVel.y = 0.8;
         character.body.SetLinearVelocity(charVel);
    }
}

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

Как упоминалось ранее, в классе b2Body есть целый ряд других методов. Вы можете занять несколько минут и поиграть с другими. Все они довольно интуитивно понятны — некоторые из них будут рассмотрены в третьей части этой серии уроков.


Довольно часто нужно заставлять твердое тело перепрыгивать с одного места на другое. Вы можете сделать это, используя свойства QuickObject x и y. Добавьте нижеприведенную функцию teleportCharacter () в свой код и постоянно вызывайте ее в функции onStep ():

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
teleportCharacter();
 
}// end of onStep() method
 
function teleportCharacter():void{
    // restrict character motion
    if (char.x < 4 && char.y > 12){
        char.x = 8;
        char.y = 3;
        // zero out the characters velocity
        charVel.SetZero();
        char.body.SetLinearVelocity(charVel);
    }
}

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

Это работает, постоянно проверяя свойства x и y нашего твердого тела персонажа и затем сбрасывая их, если данное условие выполнено. Мы также называем метод SetZero () b2Vec2 charVel — поскольку из названия следует, что свойства x и y charVel устанавливаются в 0, в результате чего линейная скорость персонажа устанавливается равной нулю.


На следующем этапе мы будем использовать свойства QuickObjects x, y и angle для создания простых плавающих платформ. Первое, что нам нужно сделать, это добавить некоторые определения переменных:

1
2
3
4
5
6
7
8
// you already have these…
var character:QuickObject;
var charVel:b2Vec2 = new b2Vec2();
 
// add these 3 new vars
var oscillator:QuickObject;
var oscTheta:Number = 0;
var rotator:QuickObject;

Сначала мы заполним переменную осциллятора статическим прямоугольным телом. Добавьте этот код в функцию buildSimulation ():

1
oscillator = sim.addBox({x:21, y:8, width:2.5, height:0.25, fillColor:0x009965, density:0});

Пока это довольно просто. Мы хотим качать эту платформу вверх и вниз — для этого мы будем использовать Math.sin (). Добавьте указанную ниже функцию runOscillator () в свой код и непрерывно вызывайте ее в функции onStep (). Проверь свой фильм.

1
2
3
4
5
6
7
8
runOscillator();
} // end of onStep() function
 
function runOscillator():void{
    oscillator.y = 8 + 4 * Math.cos(oscTheta);
    oscTheta += 0.01;
}

Этот код осциллирует свойство y твердого тела генератора от 4 до 16 метров. Если вы не знакомы с синусом и косинусом, возможно, вы захотите немного ознакомиться с некоторыми основными тригонометриями. Суть этого примера в том, что мы можем использовать простое уравнение для изменения свойства y твердого тела.

Далее мы добавим нашу платформу ротатора. Скопируйте этот код в вашу функцию buildSimulation ():

1
rotator = sim.addBox({x:16, y:8, width:3, height:0.3, fillColor:0x009965, density:0});

Ничего особенного здесь не происходит, мы просто расположили эту платформу так, чтобы она не мешала другим частям нашего моделирования. Теперь его нужно повернуть — мы добавим функцию runRotator () для этого:

1
2
3
4
5
6
7
runRotator();
} // end of onStep() function
 
function runRotator():void{
    rotator.angle += 0.02;
}

Иди и проверь свой фильм. Потратьте несколько минут, чтобы переместить твердые тела на платформы и с них.

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

Это происходит из-за того, что тело, сидящее на качающейся платформе, заснуло и Box2D прекратил проверку на наличие столкновений с ним. Простое решение состоит в том, чтобы установить для параметра allowSleep значение false на любом твердом теле, которое может понадобиться для установки на качающейся платформе. Это хорошо работает, но не очень эффективно. Более элегантное решение было бы, если бы мы могли определить, происходило ли столкновение на качающейся платформе, и убедиться, что объект, с которым он сталкивается, не засыпает. Это возможно с помощью так называемых контактных точек. Мы расскажем о контактных точках в третьей части этого урока, но если вам интересно, вы можете посмотреть их несколько примеров здесь .


В Box2D твердые тела имеют свойство, называемое userData. Это просто нетипизированная переменная, предназначенная для использования с пользовательской информацией о данном твердом теле.

QuickBox2D заполняет свойство userData объектом DisplayObject. Итак, если вы хотите добавить MouseEvent в обложку QuickObject или если вы хотите нарисовать дополнительную графику в QuickObject, используя свойство graphics, вам нужно использовать свойство userData. Мы увидим, как сделать обе эти вещи на этом шаге. Начните с определения переменной с именем shootBox со всеми остальными переменными:

1
2
3
4
5
6
// you already have these:
var oscTheta:Number = 0;
var rotator:QuickObject;
 
// add this
var shootBox:QuickObject;

Теперь мы создадим ShoboBox и нарисуем X внутри. Добавьте следующий код в функцию buildSimulation () и протестируйте фильм:

1
2
3
4
5
6
7
8
shootBox = sim.addBox({x:24, y:17, width:1.5, height:1.5});
 
// draw an X into shootBox userData.graphics
shootBox.userData.graphics.lineStyle(0, 0xFFFFFF, 0.5);
shootBox.userData.graphics.moveTo(-20, -20);
shootBox.userData.graphics.lineTo(20, 20);
shootBox.userData.graphics.moveTo(20, -20);
shootBox.userData.graphics.lineTo(-20, 20);

Это будет работать так же, если вы используете собственные скины.

Добавление MouseEvents работает точно так же. Давайте создадим простую функцию слушателя для использования с MouseEvent, добавим эту функцию в ваш код:

1
2
3
4
5
6
function onShootBoxClick(evt:MouseEvent):void {
    var currLinearVec:b2Vec2 = shootBox.body.GetLinearVelocity();
    currLinearVec.x -= 15;
    currLinearVec.y -= 15;
    shootBox.body.SetLinearVelocity(currLinearVec);
}

Теперь мы можем использовать эту функцию слушателя вместе с shootBox, добавив этот код в функцию buildSimulation ():

1
shootBox.addEventListener(MouseEvent.CLICK, onShootBoxClick);

Когда вы тестируете свой фильм, если вы нажимаете на твердое тело shootBox, его линейная скорость сбрасывается, заставляя его стрелять вверх и влево.


Мы можем добавить один последний штрих к нашему ShoboBox, используя угловую скорость. Угловая скорость — это скорость вращения данного твердого тела. Чтобы добавить небольшое вращение в shootBox, добавьте эту строку в функцию слушателя onShootBoxClick ():

1
shootBox.body.SetAngularVelocity(-10);

Иди и проверь свой фильм.

Угловая скорость -10 приведет к тому, что ShoboBox будет вращаться против часовой стрелки со скоростью 10 радиан в секунду. Положительное значение заставит стрелку вращаться по часовой стрелке.


FRIM (независимое от частоты кадров движение) используется для того, чтобы поддерживать постоянную скорость моделирования Box2D независимо от частоты кадров, на которой работает ваш SWF. Это хорошо, потому что частота кадров будет варьироваться от браузера к браузеру и от компьютера к компьютеру.

Перейдите к первой строке своего кода и попробуйте изменить частоту кадров на 20, 120, 5 и т. Д. … каждый раз тестируя SWF, чтобы увидеть, как он выглядит.

1
[SWF(width = 800, height=600, frameRate=20, backgroundColor=0x000000)];

При изменении значения frameRate вы заметите, что скорость движения твердых тел остается относительно постоянной. Разница в том, что для представления одной и той же анимации используется разное количество кадров. Это действительно важно в играх — если ваша игра анимируется на старом компьютере очень медленно, играть в нее будет значительно проще.

Мы можем переключать FRIM, изменяя аргументы, которые мы передаем конструктору QuickBox2D. Вы должны быть на вершине своего скрипта, идти вперед и изменить строку, где мы создаем QuickBox2D, чтобы это выглядело так:

1
var sim:QuickBox2D = new QuickBox2D(this, {frim:false});

Протестируйте фильм несколько раз, каждый раз изменяя значение frameRate, и обратите внимание, что более низкая частота кадров теперь приводит к замедлению симуляции, а более высокая частота кадров ускоряет симуляцию. Когда вы закончите тестирование, установите FRIM обратно в true и установите frameRate обратно в 60.

Примечание. Второй аргумент конструктора QuickBox2D — это объект параметров, связанных со всем моделированием.

Ранее в руководстве мы использовали событие QuickBox2D.STEP вместо enterFrame. Это может быть очевидно, но QuickBox2D.STEP запускается независимо от frameRate. По умолчанию QuickBox2D пытается обновить симуляцию с частотой 60 Гц (или 60 раз в секунду), поэтому, если SWF-файл работает только со скоростью 30 кадр / с, событие QuickbBox2D.STEP будет запускаться примерно по 2 таймера на кадр. Вы можете изменить частоту QuickBox2D.STEP, изменив параметр timeStep следующим образом:

1
var sim:QuickBox2D = new QuickBox2D(this, {frim:true, timeStep:1/30.0});

Выше приведено понижение timeStep до 30 Гц, в результате чего QuickBox2D.STEP будет вызываться каждый второй кадр со скоростью 60 кадров в секунду или один раз за кадр со скоростью 30 кадров в секунду. Вы можете попробовать это, если хотите, но я обнаружил, что 60 Гц и частота кадров 60 кадров в секунду работают лучше всего большую часть времени.


Мы можем изменить гравитацию в симуляции, добавив дополнительные параметры в аргумент параметров QuickBox2D. Попробуй это:

1
var sim:QuickBox2D = new QuickBox2D(this, {frim:true, gravityY: -20});

Когда вы проверите свой фильм, вы заметите, что гравитация теперь перевернута. По умолчанию сила тяжести Y равна 20, а установка -20 заставляет все падать. Вы также можете изменить gravityX (это хорошо для создания эффектов ветра):

1
var sim:QuickBox2D = new QuickBox2D(this, {frim:true, gravityY:-20, gravityX:10});

Это довольно забавно выглядит:

Но если вы подумаете об этом на мгновение, было бы очень круто иметь переменную гравитацию и ветер в игре. Попробуйте добавить этот фрагмент в свой код, просто добавьте его прямо под кодом экземпляра QuickBox2D:

1
2
3
4
stage.addEventListener(MouseEvent.CLICK, onClick);
function onClick(evt:*){
    sim.gravity.x += 3;
}

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


Это завершает вторую часть этой серии уроков. Третья и последняя часть этого урока будет охватывать некоторые передовые методы. Мы посмотрим, как QuickBox2D обрабатывает все различные типы соединений Box2D (их много). Мы также рассмотрим контактные точки и особые типы столкновений.

Я надеюсь, вам понравился этот урок. Если вы создадите что-то интересное из того, что вы узнали из этого урока, не стесняйтесь размещать ссылку на него в комментариях: D