Прямая кинематика (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: Играй с рукой
Каждая настройка кода выполняется. Конечно, вы можете настроить его так, как хотите. А пока, прокрутите узлы, используя клавишу пробела. Для каждого узла поиграйте с клавишами направления, чтобы увидеть, как экскаватор движется вверх и вниз.
Вывод
Вот и все на этот раз. Я надеюсь, что файл классов, который я собрал, поможет облегчить вашу кинематику в ваших играх и симуляторах. Спасибо за чтение и оставьте некоторые комментарии, если вы нашли ошибку в чем-либо.