Изображение анимации с точки зрения векторов интуитивно понятно, но понимание векторной математики — это боль. В этом уроке я надеюсь облегчить эту боль и обеспечить решение проблем с анимацией, используя специально написанный класс Vector2D. Мы рассмотрим некоторые фундаментальные концепции линейной кинематики в эйлеровом подходе: смещение, скорость и ускорение. Затем мы создадим простое приложение с ним.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться. Нажмите на панель Flash ниже и управляйте стрелкой, нажимая четыре клавиши со стрелками.
Шаг 1: векторное количество
Все векторные величины имеют две составляющие: величину и направление.
Шаг 2: Изменение количества вектора
Изменение векторных величин относится к одному из следующих случаев:
- Смена направления
- Изменение величины
- Изменение как величины, так и направления
Шаг 3: смещение как векторная величина
Смещение, скорость и ускорение являются векторными величинами. Их определения следующие:
- Смещение — вектор кратчайшего пути, указывающего от источника к месту назначения. Я определяю начало как точку (0, 0) и пункт назначения как местоположение частицы относительно этой точки. По сути, это относится к декартовой системе координат, реализованной Flash.
- Скорость — Скорость — это смещение с течением времени.
- Ускорение — Ускорение — это изменение скорости во времени.
Анимация ниже показывает смещение, как мы собираемся реализовать во Flash позже.
Шаг 4: Скорость как векторное число
Скорость проиллюстрирована анимацией ниже. Обратите внимание, что скорость постоянна, что означает, что в этом сценарии ускорение отсутствует. Если скорость равна нулю, смещение будет оставаться постоянным во всем.
Шаг 5: Ускорение как векторное число
Ускорение иллюстрируется анимацией ниже. Примечание: кинематика подразумевает постоянное ускорение. Если ускорение меняется со временем, оно подпадает под тему динамики . Динамика — это изучение сил, которые вызывают ускорение с течением времени. Одной из таких сил является гравитация, и я написал пост об этой анимации .
Шаг 6: Начните строить снаряд
Теперь, когда вы получили краткое представление о линейных кинематических величинах и смогли связать их с векторами, мы можем приступить к созданию нашего класса «Снаряд». Мы хотели бы, чтобы снаряд мог захватить все эти величины: смещение, скорость и ускорение — так, чтобы им можно было манипулировать на каждом кадре.
Ниже приведены данные, которые мы запишем в нашем классе снарядов:
частное смещение var: Vector2D; частный var velo: Vector2D; частный var acc: Vector2D;
Шаг 7: Инициализировать снаряд
После инициации этого класса Снаряда мы инициализируем упомянутые переменные и рисуем его графическое представление
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public function Projectile()
{
//draw graphics
this.draw();
//init all vector quantities
displace = new Vector2D(this.x, this.y);
velo = new Vector2D(0, 0);
acc = new Vector2D(0, 0);
}
protected function draw():void
{
//drawing the arrowhead
var height:Number = 30;
var width:Number = 60;
graphics.beginFill(0x0000FF);
graphics.moveTo(0, 0);
graphics.lineTo(width / -3, height / -2);
graphics.lineTo(width / 2, 0);
graphics.lineTo(width / -3, height / 2);
graphics.lineTo(0, 0);
graphics.endFill();
}
|
Шаг 8: средства доступа к векторным величинам
Ниже приведены методы доступа к нашим личным переменным — displace
, velo
, acc
— в классе «Снаряд»
публичная функция setDisp (mag: число, угол: число): пусто { displace.redefine (mag, angle); } публичная функция getDisp (): Vector2D { обратное смещение; } публичная функция setVelo (mag: число, угол: число): недействительно { velo.redefine (mag, angle); } публичная функция getVelo (): Vector2D { скорость возврата; } публичная функция setAcc (mag: число, угол: число): пусто { переопределить (mag, угол); } публичная функция getAcc (): Vector2D { возврат акк }
Шаг 9: Обновители векторных величин
После обновления каждого кадра нам нужно обновить скорость (используя ускорение) и обновить смещение (используя указанную скорость). Это может быть достигнуто с помощью следующих функций. Чтобы получить подробное объяснение о добавлении Vector, посетите этот великий пост от Daniel Sidhon .
публичная функция applyVelo (): void { this.displace = this.displace.add (velo); } публичная функция applyAcc (): void { this.velo = this.velo.add (acc); } // обновить позицию спрайта смещением. публичная функция animate (): void { this.x = this.displace.x; this.y = this.displace.y; }
Шаг 10: Обновление для Ориентации Sprite
Нам также нужно будет обновить ориентацию Sprite. Это может быть достигнуто с помощью свойства rotation
Sprite.
публичная функция orient (): void { this.rotation = Math2.degreeOf (velo.getAngle ()); }
Я также реализовал статический класс Math2
, в котором я написал функцию, позволяющую легко преобразовывать единицы измерения градусов и радиан в угол назад и вперед.
общедоступная статическая функция radianOf (deg: Number): Number { возврат град / 180 * Math.PI; } общедоступная статическая функция степеньOf (рад: число): число { обратный рад / Math.PI * 180; }
Шаг 11: Основной класс
Теперь, когда мы создали классы Projectile и Math2, мы можем начать кодировать наш основной класс. Нам также понадобится класс Vector2D, хотя подробное объяснение не включено в связи с вышеупомянутой статьей Даниэля Сидхона « Векторы « Я предполагаю, что читатели понимают класс Vector2D после прочтения. Тем не менее, если необходимы разъяснения, не стесняйтесь ответить на ваши вопросы.
Прежде всего нам нужно знать приватные переменные этого класса.
частный вар b1: снаряд; // флаги нажатия private var UP: Boolean = false; private var DOWN: Boolean = false; private var LEFT: Boolean = false; private var RIGHT: Boolean = false;
Шаг 12: Инициализация Main
После инициализации Main будет запущена функция init
. Эта функция создаст новый снаряд и установит его начальную скорость. Затем слушатели событий будут назначены.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
b1 = new Projectile();
stage.addChild(b1);
//setting initial velocity
b1.setVelo(5, Math2.radianOf(30));
//setting event listeners
b1.addEventListener(Event.ENTER_FRAME, proj_enterFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, handle_keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, handle_keyUp);
}
|
Шаг 13: слушатели событий клавиатуры
Я определил пользовательский контроль как нажатия клавиш со стрелками вверх, влево, вниз и влево. После нажатия и отпускания этих клавиш переменные-флажки Main (Шаг 11) будут установлены в true и false. Основываясь на этих флагах, Векторными величинами будут манипулировать в каждом кадре. Обратите внимание, что я разделил элементы управления на манипуляторы с горизонтальной и вертикальной осями.
приватная функция handle_keyDown (e: KeyboardEvent): void { if (e.keyCode == Keyboard.UP) UP = true; иначе if (e.keyCode == Keyboard.DOWN) DOWN = true; if (e.keyCode == Keyboard.LEFT) LEFT = true; иначе if (e.keyCode == Keyboard.RIGHT) RIGHT = true; } приватная функция handle_keyUp (e: KeyboardEvent): void { if (e.keyCode == Keyboard.UP) UP = false; иначе if (e.keyCode == Keyboard.DOWN) DOWN = false; if (e.keyCode == Keyboard.LEFT) LEFT = false; иначе if (e.keyCode == Keyboard.RIGHT) RIGHT = false; }
Шаг 14: прослушиватели событий EnterFrame
После обновления каждого кадра будет выполнен следующий код. Это долго, но не волнуйся; просто читайте дальше.
приватная функция proj_enterFrame (e: Event): void { // определить ускорение var accMag: число = 0,1; if (UP) { b1.setAcc (accMag, Math2.radianOf (-90)); b1.applyAcc (); } еще если (ВНИЗ) { b1.setAcc (accMag, Math2.radianOf (90)); b1.applyAcc (); } if (LEFT) { b1.setAcc (accMag, Math2.radianOf (180)); b1.applyAcc (); } иначе если (ПРАВО) { b1.setAcc (accMag, Math2.radianOf (0)); b1.applyAcc (); } // замедляемся при нажатии nothng для имитации трения. if (UP + DOWN + LEFT + RIGHT == 0) { var currentVeloMag: Number = b1.getVelo (). getMagnitude (); var currentVeloAng: Number = b1.getVelo (). getAngle (); if (currentVeloMag> 1) { b1.setAcc (accMag * -1, currentVeloAng); b1.applyAcc (); } } b1.applyVelo (); // ограничение спрайта границами сцены b1.getDisp (). x = Math2.implementBound (0, stage.stageWidth, b1.getDisp (). x); b1.getDisp (). y = Math2.implementBound (0, stage.stageHeight, b1.getDisp (). y); b1.animate (); b1.orient (); }
Шаг 15: Обновить движение
Обновление движения должно быть сделано в следующем порядке:
- Определите новое ускорение в соответствии с нажатием клавиши пользователя.
- Используя ускорение, обновите текущую скорость.
- Используя текущую скорость, обновите текущее смещение.
- Уточните смещение, чтобы удержать объект внутри границ.
Я выделил коды для легкой идентификации этих шагов.
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
|
private function proj_enterFrame(e:Event):void
{
//define acceleration
var accMag:Number = 0.1;
if (UP) {
b1.setAcc(accMag, Math2.radianOf(-90));
b1.applyAcc();
}
else if (DOWN) {
b1.setAcc(accMag, Math2.radianOf(90));
b1.applyAcc();
}
if (LEFT) {
b1.setAcc(accMag, Math2.radianOf(180));
b1.applyAcc();
}
else if (RIGHT) {
b1.setAcc(accMag, Math2.radianOf(0));
b1.applyAcc();
}
//decelerate as nothing is pressed to simulate friction.
if (UP + DOWN + LEFT + RIGHT == 0) {
var currentVeloMag:Number = b1.getVelo().getMagnitude();
var currentVeloAng:Number = b1.getVelo().getAngle();
if(currentVeloMag > 1){
b1.setAcc(accMag * -1, currentVeloAng);
b1.applyAcc();
}
}
b1.applyVelo();
//restricting sprite to borders of the stage
b1.getDisp().x = Math2.implementBound(0, stage.stageWidth, b1.getDisp().x);
b1.getDisp().y = Math2.implementBound(0, stage.stageHeight, b1.getDisp().y);
b1.animate();
b1.orient();
}
|
Шаг 16: замедление движения
Вы можете обнаружить, что между этими выделенными кодами есть другие функции. Кто они такие? Одним из них является применение другого вектора для замедления нашего снаряда, так как пользователь не нажимает ни на какие клавиши. Это применяется прежде, чем мы добавим скорость к нашему смещению.
// замедляемся при нажатии nothng для имитации трения. if (UP + DOWN + LEFT + RIGHT == 0) { var currentVeloMag: Number = b1.getVelo (). getMagnitude (); var currentVeloAng: Number = b1.getVelo (). getAngle (); if (currentVeloMag> 1) { b1.setAcc (accMag * -1, currentVeloAng); b1.applyAcc (); } }
Шаг 17: Оставайтесь внутри
Следующее — ограничить наш снаряд, чтобы он всегда оставался на сцене, иначе он вылетит за пределы экрана. Опять же, ImplementBound — это функция, которую я включил в статический класс Math2. Учитывая верхнюю границу, нижнюю границу и случайное значение, implementBound
вернет значение, которое находится внутри границ.
После применения этих ограничений к нашему смещению (и только после этого) мы обновляем позицию Sprite с помощью этого значения смещения.
// ограничение спрайта границами сцены b1.getDisp (). x = Math2.implementBound (0, stage.stageWidth, b1.getDisp (). x); b1.getDisp (). y = Math2.implementBound (0, stage.stageHeight, b1.getDisp (). y);
Шаг 18: Ориент Спрайт
Прежде чем оставить этот спрайт таким, какой он есть, мы должны сориентировать его так, чтобы он всегда указывал в положение, в котором он движется, с помощью функции orient
.
Шаг 19: Готовься и иди!
Теперь все готово к работе. Когда вы запустите эту часть, нажав Ctrl + Enter, вы увидите стрелку, которая постепенно замедляется, когда она направляется по диагонали вниз по экрану. Нажмите на четыре клавиши со стрелками, чтобы переместить стрелку вокруг. Не беспокойтесь о потере своей стрелы; это останется внутри вашего взгляда.
Вывод
Эта статья должна познакомить вас с использованием векторов для анимации движения. Как только вы поняли кинематику, продолжайте читать мой пост о динамике . Дайте мне знать, как это происходит. Спасибо.