Прямая кинематика (FK) — алгоритм позиционирования. Это не так сложно, как кажется, если вы связываете это с последовательностью действий с точки зрения оператора экскаватора. Начиная с ближайшего к нему узла, оператор настраивает ориентацию каждого узла в надежде, что совок достигнет предполагаемого местоположения. Часто делается несколько попыток до достижения успеха. FK — алгоритм для выполнения такой задачи. В этом уроке мы попытаемся реализовать FK и наложить его на экскаватор с двухмерного вида сбоку. Поняв этот алгоритм, можно легко адаптировать его к трехмерной среде. Наслаждайтесь!
Шаг 1: учебный процесс
В этом руководстве предполагается следующая последовательность:
- Объяснение математического фона Ф.К.
- Жесткое кодирование будет выполняться в файле в соответствии с последовательным потоком.
- Далее, объектно-ориентированный класс разработан для реализации алгоритма FK.
- Демонстрация класса FK, используемого в проекте.
- Графика применяется к демонстрации.
Шаг 2: Цепочка
Прежде всего, мы должны понять концепцию цепи. Цепочка абстрагирует иерархический порядок узлов. Существует корневой узел, самый высокий порядок цепочки, к которому присоединяются все остальные узлы. Узел, который подключается к корню, является дочерним элементом первого уровня. Узел, который подключается к дочернему элементу первого уровня, является дочерним элементом второго уровня.
Иерархические отношения упрощены ниже:
корень> узел1> узел2> узел3> узел4> узел5 …
Приведенная выше флэш-анимация показывает отношения между родителями (выше по иерархии) и дочерними (ниже по иерархии), где каждая стрелка указывает на дочерний узел.
Шаг 3: Стрелки
Каждый из этих узлов связан с соседними ограничениями. Я буду называть эти ограничения «Стрелками». Стрелка — это векторы. Они имеют длину (более известную как величина) и угол, который описывает ее ориентацию. В этом руководстве эти стрелки указывают от родительского узла к дочернему узлу. Например, узел 1 присоединяется к корню с помощью стрелки1. Стрелка1 указывает от корня на узел1.
Стрелки также следуют иерархическому порядку, потому что они пытаются объединить иерархическую цепочку узлов. Это иерархическое отношение упрощено, как показано ниже:
стрелка1> стрелка2> стрелка3> стрелка4> стрелка5 …
Так где же стрелка0? Ну, корень является самым высоким в иерархии. На нее не указывает ни одна стрелка, поэтому фактически нет стрелки0.
Шаг 4: Дельта Вращение
Регулировка вращения стрелок — самая важная вещь в алгоритме FK. Здесь мы будем вращать стрелку на угол дельта. Если мы повернем стрелку1, положение узла 1 будет обновлено. Но это не все.
В алгоритме FK кинематика перемещается от самой высокой к самой низкой иерархии. Предположим, что наша цепь имеет 6 узлов, соединенных 5 стрелками. Если дельта-вращение применяется к стрелке1, то стрелки2 к стрелке5 также необходимо повернуть на угол дельты. Я выделил стрелки, на которые повлияло изменение.
стрелка1 > стрелка2 > стрелка3 > стрелка4 > стрелка5
Если дельта вращение применяется к стрелке 3, то затрагиваются только стрелка 4 и стрелка 5.
arrow1> arrow2 > arrow3 > arrow4 > arrow5
Для каждой затронутой стрелки также будет затронута позиция связанного с ней узла. Таким образом, node4 и node5 обновят свои позиции.
В приведенном выше SWF, нажмите стрелки, чтобы увидеть, как кинематика пульсирует вниз по узлам (более темные стрелки).
Шаг 5: Начать проект
Запустите FlashDevelop и начните новый проект. Добавьте «MileStone1.as» в ваш проект.
Шаг 6: Цепочка из двух узлов: две графики
Теперь для реализации нашей первой цепочки FK. Во-первых, нам нужно добавить два спрайта на сцену в качестве переменных класса. Корневой узел — это синий круг, а первый узел — фиолетовый круг.
01
02
03
04
05
06
07
08
09
10
11
|
rootNode = new Sprite();
rootNode.graphics.beginFill(0x4499FF);
rootNode.graphics.drawCircle(0, 0, 20);
rootNode.graphics.endFill();
addChild(rootNode);
node1 = new Sprite();
node1.graphics.beginFill(0x772255);
node1.graphics.drawCircle(0, 0, 20);
node1.graphics.endFill();
addChild(node1);
|
Шаг 7: Цепочка из двух узлов: ограничение стрелки
node1
присоединяется к rootNode через vec1
, который является экземпляром класса Vector2D (пользовательский класс). Это переменная класса. Мы начинаем его и устанавливаем его величину до 60.
1
2
|
vec1 = new Vector2D(0, 0);
vec1.setMagnitude(60);
|
Шаг 8: Цепочка из двух узлов: позиционирование
Прежде чем мы начнем назначать слушателей для добавления интерактивности в нашу маленькую программу, нам нужно установить начальную позицию узлов. Сначала rootNode
, что довольно очевидно. Далее идет node1
(выделен), который зависит от rootNode
.
1
2
3
4
5
|
//setting the nodes
rootNode.x = 150;
rootNode.y = 150;
node1.x = rootNode.x + vec1.x;
node1.y = rootNode.y + vec1.y;
|
Шаг 9: цепочка из двух узлов: слушатель
Почти готово. Добавляем слушателя к событию клавиатуры. При нажатии произвольной клавиши, vec1
поворачивается и дополнительно меняет ориентацию node1
отношению к rootNode
.
1
2
3
4
5
6
7
8
9
|
stage.addEventListener(KeyboardEvent.KEY_DOWN, move);
}
private function move(e:KeyboardEvent):void
{
vec1 = vec1.rotate(Math2.radianOf(10));
node1.x = rootNode.x + vec1.x;
node1.y = rootNode.y + vec1.y;
}
|
Ниже завершена MileStone1. Просто, правда?
Нажмите на SWF, затем нажмите любую клавишу (несколько раз), чтобы увидеть эффект.
Шаг 10: Цепочка из трех узлов: графика и стрелка
Теперь, когда у нас есть базовая настройка, давайте продвинемся немного дальше. Мы добавим node2
(также фиолетовый) в существующую цепочку. node2
соединен с node1
через vec2
, который имеет величину 60
1
2
3
4
5
6
7
8
9
|
//a second child
node2 = new Sprite();
node2.graphics.beginFill(0x772255);
node2.graphics.drawCircle(0, 0, 20);
node2.graphics.endFill();
addChild(node2);
vec2 = new Vector2D(0, 0);
vec2.setMagnitude(60);
|
Шаг 11: цепочка из трех узлов: позиционирование
Мы инициализируем положение node2
относительно node1
используя vec2
в качестве ограничения, используя следующий код. Не забудьте также включить тот же фрагмент кода в прослушиватель клавиатуры (строка 64 MileStone2).
1
2
|
node2.x = node1.x + vec2.x;
node2.y = node1.y + vec2.y;
|
Вот MileStone2 завершено:
Снова используйте клавиатуру, чтобы увидеть эффект.
Шаг 12: цепь из трех узлов: э-э-э
Хм … Что-то не так с этой реализацией FK. Видите, кинематика не проходит по цепочке. Напомним Шаг 4: Дельта Вращение; кинематика должна двигаться вниз от текущего уровня иерархии до конца. Я выделил строки кода, которые делают это. Чтобы исправить логическую ошибку, просто вставьте строку 65 в MileStone2. Я поставил MileStone3 на случай, если у вас возникнут трудности.
01
02
03
04
05
06
07
08
09
10
|
private function move(e:KeyboardEvent):void
{
vec1 = vec1.rotate(Math2.radianOf(10));
node1.x = rootNode.x + vec1.x;
node1.y = rootNode.y + vec1.y;
vec2 = vec2.rotate(Math2.radianOf(10));
node2.x = node1.x + vec2.x;
node2.y = node1.y + vec2.y;
}
|
Шаг 13: цепь из трех узлов: дополнительные элементы управления
Далее, давайте добавим гибкость нашему контролю. В настоящее время мы контролируем позиционирование node1
и node2.
Позволяет управлять только позиционированием node2
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
private function move(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.PAGE_UP) {
vec1 = vec1.rotate(Math2.radianOf(10));
node1.x = rootNode.x + vec1.x;
node1.y = rootNode.y + vec1.y;
vec2 = vec2.rotate(Math2.radianOf(10));
node2.x = node1.x + vec2.x;
node2.y = node1.y + vec2.y;
}
else if (e.keyCode == Keyboard.PAGE_DOWN) {
vec2 = vec2.rotate(Math2.radianOf(10));
node2.x = node1.x + vec2.x;
node2.y = node1.y + vec2.y;
}
}
|
Используйте клавиши Page Down и Page Up для перемещения различных узлов.
Да, вращение все еще в одном направлении — против часовой стрелки. Цель выполнения шагов 5 — 13 — получить полное понимание алгоритма FK. Теперь, когда он построен, давайте перенесем это на другой уровень: проектируем класс FKChain, который позволяет легко реализовать FK.
Шаг 14: класс FKChain: переменные
FKChain был разработан, чтобы легко реализовать FK. Я разработал класс со следующими переменными и функциями. Я проведу вас через них.
переменные | Цель | Тип данных |
nodes |
Удерживать узлы цепочки ФК | Vector массив Sprites |
arrows |
Держать стрелки, соединяющие узлы | Vector массив Vector2D |
lowerLimits |
Удерживать нижнюю границу допустимого угла | Vector массив Number |
upperLimits |
Удерживать верхнюю границу допустимого угла | Vector массив Number |
selectedIndex |
Хранить данные текущего выбранного узла | Integer |
Шаг 15: FKChain Класс: структура данных
Ассоциация узлов со стрелками наряду с другими ограничениями изображена на следующей диаграмме.
Обратите внимание на связь nodes
со arrows
, lowerLimits
и upperLimits
. Угол, отмеченный красным, ограничен согласно формуле на изображении выше.
Также обратите внимание, что я упомянул на шаге 3, что arrow[0]
нет arrow[0]
. Однако, если мы хотим реализовать границы, нам понадобится arrow[0]
чтобы быть горизонтальным вектором, где угол arrow[1]
измеряется и оценивается ограничениями.
Шаг 16: Класс FKChain: Функции
Теперь, когда вы понимаете болты и гайки класса FKChain, мы переходим к определению функций FKChain.
функции | Тип ввода / данных | Цель |
FKChain() |
корневой узел / Sprite |
Инициирует переменные класса. Устанавливает корневой узел в цепочку FK вдоль с соответствующими ограничениями. Устанавливает selectedNode в корневой узел. |
appendNode() |
следующий дочерний узел / Sprite |
Добавить новый узел в цепочку вместе с соответствующими ограничениями. наборы текущий выбран. Устанавливает selectedNode для нового узла. |
removeNode() |
узел для удаления / Sprite |
Удалить входной узел из цепочки вместе с соответствующими ограничениями. |
selectedNode() |
узел для манипулирования / Sprite |
Установить текущий выбранный узел для выполнения манипуляций, таких как настройка его стрелка (величина и угол) и ограничения на угол стрелки. |
updateLimits() |
нижние, верхние границы / Number |
Устанавливает новые ограничения (верхняя и нижняя граница) на стрелки угол выбранного узла. |
alterMagnitude() |
величина / Number |
Устанавливает новую величину на стрелке выбранного узла. |
alterAngle() |
угол для регулировки / Number |
Вводит дельта-угол на стрелку выбранного узла. |
setPosition() |
(никто) | Падение кинематики от иерархии выбранного узла до конца цепь. |
Шаг 17: Класс FKChain: Свойства
Я также представил несколько геттеров для облегчения разработки. Они используются при прокрутке узлов.
Свойство | Цель |
currentNode |
Возвращает текущий выбранный узел. |
child |
Возвращает дочерний элемент выбранного узла. |
parent |
Возвращает родителя выбранного узла. |
Шаг 18: Использование FKChain
Теперь у нас есть обзор FKChain, давайте посмотрим на его применение и внутреннюю работу. В вашем проекте создайте класс TestChain
. Мы будем вызывать метод рисования для рисования всех необходимых Sprites
на нашей сцене. Всего имеется пять спрайтов и скобка для обозначения текущего выбранного узла.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
public function draw():void
{
b0 = new Sprite();
b0.graphics.beginFill(0);
b0.graphics.drawCircle(0, 0, 15);
b0.graphics.endFill();
addChild(b0);
b1 = new Sprite();
b1.graphics.beginFill(0);
b1.graphics.drawRect(-80, -10, 80, 20);
b1.graphics.endFill();
addChild(b1);
b2 = new Sprite();
b2.graphics.beginFill(0);
b2.graphics.drawRect(-40, -10, 40, 20);
b2.graphics.endFill();
addChild(b2);
b3 = new Sprite();
b3.graphics.beginFill(0);
b3.graphics.drawRect(-40, -10, 40, 20);
b3.graphics.endFill();
addChild(b3);
b4 = new Sprite();
b4.graphics.beginFill(0);
b4.graphics.drawRect(-40, -10, 40, 20);
b4.graphics.endFill();
addChild(b4);
target = new Sprite();
target.graphics.lineStyle(3, 0xFF0000);
target.graphics.moveTo( -15, -10);
target.graphics.lineTo( -15, -15);
target.graphics.lineTo( -10, -15);
target.graphics.moveTo( 15, -10);
target.graphics.lineTo( 15, -15);
target.graphics.lineTo( 10, -15);
target.graphics.moveTo( 15, 10);
target.graphics.lineTo( 15, 15);
target.graphics.lineTo( 10, 15);
target.graphics.moveTo( -15, 10);
target.graphics.lineTo( -15, 15);
target.graphics.lineTo( -10, 15);
addChild(target);
}
|
Шаг 19: Инициируйте FKChain
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
|
public function initChain():void
{
c = new FKChain(b0);
c.appendNode(b1);
c.alterMagnitude(120);
c.updateLimits(Math2.radianOf( -90), Math2.radianOf(45));
c.appendNode(b2);
c.updateLimits(Math2.radianOf( -45), Math2.radianOf(90));
c.appendNode(b3);
c.updateLimits(Math2.radianOf(0), Math2.radianOf(90));
c.appendNode(b4);
//update all node’s position
b0.x = 250;
b0.y = 300;
c.selectNode(b1);
c.setPosition();
//Place target onto root node initially
target.x = c.currentNode.x;
target.y = c.currentNode.y;
}
|
Во-первых, мы должны создать экземпляр класса FKChain, вызвав его конструктор. Укажите корневой узел в конструкторе. Дальнейшее изучение функции конструктора показывает две основные операции функции. Во-первых, это создание nodes
, arrows
, upperLimits
и lowerLimits
. Данные поступают в эти переменные после этого.
Обратите внимание, что корневой узел имеет связанную стрелку. Я явно не показывал, что arrows
всегда на один член меньше nodes
. Тем не менее, примите это сейчас, поскольку я объясню его использование в следующих разделах Другие выделенные строки являются просто заполнителями, чтобы обеспечить удобную адресацию по ограничениям. Наконец, установка текущего выбранного узла как root
для дальнейших манипуляций.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public function FKChain(node0:Sprite)
{
//instantiate variables
nodes = new Vector.<Sprite>;
arrows = new Vector.<Vector2D>;
upperLimits = new Vector.<Number>;
lowerLimits = new Vector.<Number>;
//introduce root node
nodes.push(node0);
arrows.push(new Vector2D(1, 0));
lowerLimits.push(0);
upperLimits.push(0);
selectNode(node0);
}
|
Шаг 20: добавление узлов к FKChain
После инициации мы можем начать добавлять узлы к экземпляру FKChain. Создание стрелки абстрагируется от пользователей. Я выделил начальную величину и ограничение на угол изгиба (в пределах -90 <угол <90). Эти ограничения легко настраиваются.
01
02
03
04
05
06
07
08
09
10
11
12
|
public function appendNode(node:Sprite):void
{
nodes.push(node);
var newArrow:Vector2D = new Vector2D(0, 0);
newArrow.setMagnitude(60);
arrows.push(newArrow);
lowerLimits.push(Math2.radianOf(-90));
upperLimits.push(Math2.radianOf(90));
selectNode(node);
}
|
Шаг 21: Позиционирование узлов в FKChain
1
2
3
4
5
6
7
8
9
|
//update all node’s position
b0.x = 250;
b0.y = 300;
c.selectNode(b0);
c.setPosition();
//Place target onto root node initially
target.x = c.currentNode.x;
target.y = c.currentNode.y;
|
Теперь позиция корневого узла находится в верхнем левом углу сцены. Мы переместим местоположение b1
близко к центру. Затем нам нужно переместить другие узлы в цепочке и установить скобку таргетинга на текущий выбранный узел. Давайте рассмотрим дальнейшее setPosition
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
public function setPosition():void
{
var index:int = Math.max(1, selectedIndex);
for (var i:int = index; i < nodes.length; i++)
{
//set position
nodes[i].x = arrows[i].x + nodes[i — 1].x;
nodes[i].y = arrows[i].y + nodes[i — 1].y;
//set orientation
nodes[i].rotation = Math2.degreeOf(arrows[i].getAngle());
}
}
|
Эта функция пытается переместить и переориентировать все узлы, начиная с текущего выбранного узла. Я установил ограничение на индекс (выделено), потому что логически, когда мы выполняем относительное перемещение, корневой узел не затрагивается никаким родителем. Самый высокий узел в иерархии для выполнения относительного перемещения — это b1
. Не имеет значения, пишете ли вы c.selectNode(b0)
или c.selectNode(b1)
перед c.setPosition()
хотя это повлияет на начальную позицию скобки для трейджинга.
Шаг 22: Анимация FKChain
1
2
3
4
5
6
7
|
public function TestChain()
{
this.draw();
this.initChain();
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
}
|
Теперь пришло время оживить цепочку. Мы назначим слушателя на событие клавиатуры.
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
29
|
private function keyDown(e:KeyboardEvent):void
{
//scroll through node selection
if (e.keyCode == Keyboard.SPACE)
{
if (c.currentNode == b4)
{
c.selectNode(b1);
}
else
{
c.selectNode(c.child);
}
}
else if (e.keyCode == Keyboard.UP)
{
c.alterAngle(Math2.radianOf( -1 * step));
c.setPosition();
}
else if (e.keyCode == Keyboard.DOWN)
{
c.alterAngle(Math2.radianOf(step));
c.setPosition();
}
target.x = c.currentNode.x;
target.y = c.currentNode.y;
}
|
При нажатии клавиши пробела целевая скобка будет прокручивать все узлы — кроме корневого узла, b0
. Нажатие клавиши со стрелкой вверх даст отрицательный угол дельты, -1 * step
; в то время как клавиша со стрелкой вниз даст положительный угол дельты, step
.
Вот образец TestChain.
Используйте клавишу пробела и клавиши со стрелками вверх и вниз, чтобы управлять им. Обратите внимание, как ограничения влияют на то, как далеко каждый узел может «прогнуться».
Шаг 23: Угловое ограничение в FKChain
Мы использовали alterAngle()
в слушателе. Теперь давайте рассмотрим внутреннюю работу этого.
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
|
public function alterAngle(ang:Number):void
{
var index:int = Math.max(1, selectedIndex);
//calc deviation between previous arrow and current arrow
var deviation:Number = arrows[index — 1].angleBetween(arrows[index]);
var future:Number = deviation + ang;
//ensure ang within defined limits
if (future > upperLimits[index])
{
ang = upperLimits[index] — deviation;
}
else if (future < lowerLimits[index])
{
ang = lowerLimits[index] — deviation;
}
//update relevant arrows
for (var i:int = selectedIndex; i < arrows.length; i++)
{
arrows[i] = arrows[i].rotate(ang);
}
}
|
Рассчитывается отклонение стрелки текущего узла от стрелки его родителя, это его текущий угол. Входной угол добавляется к текущему углу, чтобы сформировать будущий угол. Все хорошо, и это значение должно быть выведено. Тем не менее, мы хотим реализовать минимальную и максимальную границу, чтобы будущий угол сравнивался с этими значениями и соответственно корректировался. После этого мы выводим значение для обновления всех соответствующих стрелок.
Шаг 24: Улучшение изменчивой анимации
Вы можете заметить, что анимация прерывистая. Вы нажимаете клавишу, и она слегка подталкивает. Мы можем немного улучшить это, увеличив ускорение и замедление угловой скорости. Но это потребует некоторой перестройки обработчиков событий. Обратите внимание, что функции draw()
и initChain()
перенесены с предыдущих шагов.
1
2
3
4
5
6
7
8
9
|
public function TestChain2()
{
this.draw();
this.initChain();
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
stage.addEventListener(Event.ENTER_FRAME, animate);
}
|
Шаг 25: обработчики событий клавиатуры
При нажатии клавиш со стрелками флаги будут переключаться в true
. При отпускании этих ключей их соответствующие флаги будут переключены в false
. Эти флаги будут захвачены и оценены для дальнейших операций обработчиком события onEnterFrame
.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
private function keyDown(e:KeyboardEvent):void
{
//scroll through node selection
if (e.keyCode == Keyboard.SPACE)
{
if (c.currentNode == b4)
{
c.selectNode(b0);
}
else
{
c.selectNode(c.child);
}
}
else if (e.keyCode == Keyboard.UP)
{
upFlag = true;
}
else if (e.keyCode == Keyboard.DOWN)
{
downFlag = true;
}
else if (e.keyCode == Keyboard.LEFT)
{
leftFlag = true;
}
else if (e.keyCode == Keyboard.RIGHT)
{
rightFlag = true;
}
}
private function keyUp(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.UP)
{
upFlag = false;
}
else if (e.keyCode == Keyboard.DOWN)
{
downFlag = false;
}
else if (e.keyCode == Keyboard.LEFT)
{
leftFlag = false;
}
else if (e.keyCode == Keyboard.RIGHT)
{
rightFlag = false;
}
}
|
Шаг 26: кинематические переменные
Ниже приведены переменные, используемые для анимации кинематики:
1
2
3
4
5
|
private var angVelo:Number = 0;
private var maxVelo:Number = 5;
private var angAcc:Number = 0.2;
private var angDec:Number = 0.8;
private var step:Number = 5;
|
Шаг 27: обработчик события EnterFrame
Захватив флаги из события клавиатуры, мы можем дополнительно управлять кинематикой цепочки.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
private function animate(e:Event):void
{
//moving the whole chain
if (c.currentNode == b0) {
if (upFlag) {
b0.y -= step
}
else if (downFlag) {
b0.y += step
}
if (leftFlag) {
b0.x -= step
}
else if (rightFlag) {
b0.x += step
}
}
//adjusting angle
else {
if (upFlag) {
angVelo -= angAcc;
//setting cap on angular velocity
angVelo = Math.max(-1*maxVelo, angVelo);
}
else if (downFlag) {
angVelo += angAcc;
//setting cap on angular velocity
angVelo = Math.min(maxVelo, angVelo);
}
else {
//decelerate when keys are released
angVelo *= angDec
}
//set value to selected node's arrow.
c.alterAngle(Math2.radianOf(angVelo));
}
//refresh position
c.setPosition();
target.x = c.currentNode.x;
target.y = c.currentNode.y;
}
|
Обратите внимание, что есть немного другая обработка корневого узла. По мере выбора мы будем двигать всю цепочку вверх, вниз, влево или вправо. В то время как другие дети отбираются, мы angVelo
угловую скорость, angVelo
, вверх или вниз в пределах минимальной и максимальной скорости. Если клавиши отпущены (следовательно, все флаги установлены в false
), текущая скорость замедляется. Чтобы уточнить этот фрагмент выше, прочитайте мой пост по линейной кинематике, чтобы получить представление о приведенном выше коде.
Пример готовой работы показан ниже:
Снова используйте пробел и клавиши со стрелками вверх и вниз; на этот раз обратите внимание, насколько плавным является движение.
Шаг 28: Подготовка активов в Flash IDE
Теперь, когда основа для кодовой базы завершена, мы сосредоточимся на косметических работах над нашим проектом. Я не очень хороший художник, так что мириться с моим рисунком экскаватора.
Графика, которую я нарисовал, преобразуется в мувиклипы. Следует иметь в виду, что точка регистрации графики должна располагаться ближе к концу сегмента для оружия и совка. Я выделил точки регистрации красным кружком для всех моих активов на скриншотах ниже.
Я назвал эти MovieClip выше Root
, Arm1
, Arm2
и Scoop
соответственно
Другая деталь заключается в том, что вам нужно нажать Ctrl + L, чтобы открыть панель библиотеки и экспортировать эти ресурсы для ActionScript. Щелкните правой кнопкой мыши, выберите «Связывание» и введите данные, как показано на рисунках ниже.
Шаг 29: Экспорт активов из Flash IDE
После того, как все графические ресурсы сделаны, опубликуйте их в формате SWC для правильного импорта в FlashDevelop. Я использую Flash CS3. Нажмите Ctrl + Shift + F12, чтобы открыть настройки публикации. Выберите вкладку Flash и включите Export SWC. Затем нажмите Опубликовать.
Шаг 30: Импорт активов в FlashDevelop
Скопируйте новый файл (.swc) в папку lib вашего проекта.
Теперь, когда у вас это есть внутри вашего проекта, вам все равно нужно включить его в библиотеку. Щелкните правой кнопкой мыши свой файл .swc и выберите «Добавить в библиотеку».
Шаг 31: Проверьте активы
Разверните список, как показано на рисунке ниже, чтобы проверить имена переменных. Я использовал следующие имена Arm1
, Arm2
, Root
и Scoop
. Это имена классов, связанные с этой графикой. Мы будем использовать их позже.
Шаг 32: Замена графики активами
Наконец мы добрались до последней части этого урока. Мы заменим существующий метод draw()
на новый, drawAssets()
.
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
29
30
31
|
private function drawAssets():void
{
rootNode = new Root();
arm1 = new Arm1();
arm2 = new Arm2();
scoop = new Scoop();
addChild(arm1);
addChild(rootNode);
addChild(scoop);
addChild(arm2);
target = new Sprite();
target.graphics.lineStyle(3, 0xFF0000);
target.graphics.moveTo( -15, -10);
target.graphics.lineTo( -15, -15);
target.graphics.lineTo( -10, -15);
target.graphics.moveTo( 15, -10);
target.graphics.lineTo( 15, -15);
target.graphics.lineTo( 10, -15);
target.graphics.moveTo( 15, 10);
target.graphics.lineTo( 15, 15);
target.graphics.lineTo( 10, 15);
target.graphics.moveTo( -15, 10);
target.graphics.lineTo( -15, 15);
target.graphics.lineTo( -10, 15);
addChild(target);
}
|
Шаг 33: Настройте ограничения актива
Прямо сейчас, если вы запускаете ваше приложение, вы можете увидеть группу рисунков в верхнем левом углу или весь рычаг экскаватора и корпус, выровненный в забавной манере. Отрегулируйте ограничения величины и угловые ограничения для соединений должным образом. Я включил мой, как показано ниже.
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
|
private function initChain():void
{
c = new FKChain(rootNode);
c.appendNode(arm1);
c.alterMagnitude(260);
c.updateLimits(Math2.radianOf( -45), Math2.radianOf(45));
c.appendNode(arm2);
c.alterMagnitude(100);
c.updateLimits(Math2.radianOf( 0), Math2.radianOf(90));
c.appendNode(scoop);
c.alterMagnitude(60);
c.updateLimits(Math2.radianOf( 45), Math2.radianOf(135));
//update all node’s position
rootNode.x = 250;
rootNode.y = 300;
c.selectNode(rootNode);
c.setPosition();
//Place target onto root node initially
target.x = c.currentNode.x;
target.y = c.currentNode.y;
}
|
Шаг 34: Движение по горизонтали
В дальнейших настройках управления корневым узлом с помощью клавиатуры я устранил вертикальное движение всей цепи, чтобы экскаватор (корень) двигался только горизонтально. Фрагмент ниже помещается в обработчик Event.ENTER_FRAME
1
2
3
4
5
6
7
8
|
if (c.currentNode == rootNode) {
if (leftFlag) {
rootNode.x -= step
}
else if (rightFlag) {
rootNode.x += step
}
}
|
Шаг 35: Играй с рукой
Каждая настройка кода выполняется. Конечно, вы можете настроить его так, как хотите. А пока, прокрутите узлы, используя клавишу пробела. Для каждого узла поиграйте с клавишами направления, чтобы увидеть, как экскаватор движется вверх и вниз.
Вывод
Вот и все на этот раз. Я надеюсь, что файл классов, который я собрал, поможет облегчить вашу кинематику в ваших играх и симуляторах. Спасибо за чтение и оставьте некоторые комментарии, если вы нашли ошибку в чем-либо.