Это вторая часть этого урока. Я собираюсь показать вам, как управлять движением частиц с помощью дефлекторов.
Требуется предварительное знание основ движения и векторных полей. Я настоятельно рекомендую вам пройти первую часть этого урока, прежде чем двигаться дальше.
Окончательный результат предварительного просмотра
Посмотрите на конечный результат, к которому мы будем стремиться. Это пример эффекта пола, когда частицы отскакивают от пола.
Дефлекторы
Подобно гравитационным полям, дефлектор принимает данные о текущем движении частицы в качестве входных данных. После этого дефлектор перезаписывает движение частицы своим выходным сигналом только для того, чтобы выходные данные теперь содержали данные скорости в дополнение к данным положения. Таким образом, в двумерном пространстве выходной сигнал дефлектора представляет собой вектор 4D; первые два компонента вектора 4D представляют x- и y-компонент вектора положения (обозначены x и y ) соответственно, а последние два компонента представляют x- и y-компонент вектора скорости (обозначены vx и вы ).
Как использовать дефлекторы
Помните класс Field
и действие Gravity
из первой части этого урока? Ну, процедура похожа. Вы создаете объекты Deflector
которые управляют движениями частиц, а затем добавляете их к действию Deflect
, точно так же, как вы добавляете объекты Field
к действию Gravity
. Теперь давайте посмотрим на быстрый пример.
Эффект пола
В этом примере мы будем использовать класс LineDeflector
для создания эффекта отскакивания частиц от пола. Дефлектор линии по существу имитирует бесконечно длинную линию в двумерном пространстве, где одна сторона является открытым пространством, а другая — сплошной; Частицам разрешено находиться только в открытом пространстве, а не в твердом. Когда частицы выходят со стороны открытого пространства, ударяясь о линию, они отскакивают назад. Свойство Particle.collisionRadius
, представляющее радиус частицы, учитывается.
Отражатель линии использует вектор нормали и точку, через которую проходит линия, в двухмерном пространстве для определения линии. Вот иллюстрация, чтобы дать вам лучшую идею.
Шаг 1: Символ круга с эффектом пола
Создайте новый документ Flash, нарисуйте круг радиусом 10, а затем преобразуйте его в символ, экспортированный для ActionScript с именем класса Circle
.
Шаг 2: Эффект пола Класс документа
Создайте файл AS для класса документа. Класс создает эмиттер и рендер. Если вам нужно освежить в памяти базовое использование Stardust, вы можете обратиться к этому руководству .
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
|
package {
import flash.display.Sprite;
import flash.events.Event;
import idv.cjcat.stardust.common.emitters.Emitter;
import idv.cjcat.stardust.common.renderers.Renderer;
import idv.cjcat.stardust.twoD.renderers.DisplayObjectRenderer;
public class FloorEffect extends Sprite {
private var emitter:Emitter;
private var renderer:Renderer;
public function FloorEffect() {
emitter = new CircleEmitter();
renderer = new DisplayObjectRenderer(this);
renderer.addEmitter(emitter);
addEventListener(Event.ENTER_FRAME, mainLoop);
}
private function mainLoop(e:Event):void {
emitter.step();
}
}
}
|
Класс эмиттера показан ниже. Он в основном выбрасывает круговые частицы, и на частицы воздействует однородное гравитационное поле, направленное вниз.
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
|
package {
import idv.cjcat.stardust.common.actions.Age;
import idv.cjcat.stardust.common.actions.DeathLife;
import idv.cjcat.stardust.common.actions.ScaleCurve;
import idv.cjcat.stardust.common.clocks.SteadyClock;
import idv.cjcat.stardust.common.initializers.Life;
import idv.cjcat.stardust.common.initializers.Scale;
import idv.cjcat.stardust.common.math.UniformRandom;
import idv.cjcat.stardust.twoD.actions.Gravity;
import idv.cjcat.stardust.twoD.actions.Move;
import idv.cjcat.stardust.twoD.emitters.Emitter2D;
import idv.cjcat.stardust.twoD.fields.Field;
import idv.cjcat.stardust.twoD.fields.UniformField;
import idv.cjcat.stardust.twoD.initializers.DisplayObjectClass;
import idv.cjcat.stardust.twoD.initializers.Position;
import idv.cjcat.stardust.twoD.initializers.Velocity;
import idv.cjcat.stardust.twoD.zones.LazySectorZone;
import idv.cjcat.stardust.twoD.zones.SinglePoint;
public class CircleEmitter extends Emitter2D {
public function CircleEmitter() {
super(new SteadyClock(1));
//initializers
addInitializer(new DisplayObjectClass(Circle));
addInitializer(new Life(new UniformRandom(60, 10)));
addInitializer(new Position(new SinglePoint(320, 100)));
addInitializer(new Velocity(new LazySectorZone(8, 4)));
addInitializer(new Scale(new UniformRandom(1, 0.4)));
addInitializer(new CollisionRadius(10));
//actions
addAction(new Age());
addAction(new DeathLife());
addAction(new Move());
addAction(new ScaleCurve(0, 10));
//gravity
var field:Field = new UniformField(0, 0.5);
var gravity:Gravity = new Gravity();
gravity.addField(field);
addAction(gravity);
}
}
}
|
Теперь вы создали эффект, когда частицы выбрасываются из центра сцены и притягиваются гравитацией. Вот как это выглядит:
Шаг 3: Эффект пола Добавьте Дефлектор
Добавьте следующий код в конструктор эмиттера. Он создает линейный дефлектор, добавляет его к действию «Отражатель», а затем добавляет действие к эмиттеру, тем самым активируя эффект дефлектора. Первые два параметра конструктора для класса LineDeflector
— это координаты точки на линии, а последние два параметра — это x- и y-компоненты нормального вектора линии. Свойство Deflector.bounce
определяет «бодрость» линии, 1 вызывает полный отскок, а 0 означает отсутствие отскока вообще.
1
2
3
4
5
6
|
//create a line deflector passing through point (320, 320) and normal (0, -1)
var deflector:Deflector = new LineDeflector(320, 320, 0, -1);
deflector.bounce = 0.6;
var deflect:Deflect = new Deflect();
deflect.addDeflector(deflector);
addAction(deflect);
|
Вы также можете нарисовать визуальное представление линии на сцене, чтобы лучше выглядеть.
Хорошо, мы закончили с этим примером. Теперь давайте посмотрим на наш окончательный результат.
Ограничительная рамка
В этом примере мы собираемся использовать дефлектор BoundingBox
для ограничения частиц внутри прямоугольной области.
Шаг 1: Ограничивающий прямоугольник Emitter Class
Класс документа остается таким же, как и в предыдущем примере, но мы собираемся изменить класс эмиттера. Это базовый класс эмиттера этого примера. По сравнению с классом эмиттера в предыдущем примере SteadClock
заменен на ImpulseClock
для мгновенного создания 20 частиц в начале, зона положения изменяется с одной точки на прямоугольную зону, которая соответствует размеру сцены, инициализатор Velocity
замедлен немного ниже, инициализатор Life
удален, потому что мы хотим, чтобы частицы постоянно оставались на сцене, действия Age
и DeathLife
, в свою очередь, не требуются и удаляются, а ScaleCurve
также удаляется. Некоторые виды импорта также добавляются и удаляются; Вы можете просто скопировать код ниже для удобства.
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
|
package {
import idv.cjcat.stardust.common.clocks.ImpulseClock;
import idv.cjcat.stardust.common.initializers.CollisionRadius;
import idv.cjcat.stardust.common.initializers.Scale;
import idv.cjcat.stardust.common.math.UniformRandom;
import idv.cjcat.stardust.twoD.actions.Deflect;
import idv.cjcat.stardust.twoD.actions.Move;
import idv.cjcat.stardust.twoD.deflectors.BoundingBox;
import idv.cjcat.stardust.twoD.deflectors.Deflector;
import idv.cjcat.stardust.twoD.emitters.Emitter2D;
import idv.cjcat.stardust.twoD.initializers.DisplayObjectClass;
import idv.cjcat.stardust.twoD.initializers.Position;
import idv.cjcat.stardust.twoD.initializers.Velocity;
import idv.cjcat.stardust.twoD.zones.LazySectorZone;
import idv.cjcat.stardust.twoD.zones.RectZone;
import idv.cjcat.stardust.twoD.zones.SinglePoint;
public class CircleEmitter extends Emitter2D {
private var impulseClock:ImpulseClock;
public function CircleEmitter() {
super(impulseClock = new ImpulseClock(20));
impulseClock.impulse();
//initializers
addInitializer(new DisplayObjectClass(Circle));
addInitializer(new Position(new RectZone(0, 0, 640, 400)));
addInitializer(new Velocity(new LazySectorZone(3, 2)));
addInitializer(new Scale(new UniformRandom(1, 0.4)));
addInitializer(new CollisionRadius(10));
//actions
addAction(new Move());
}
}
}
|
Шаг 2: Ограничительная рамка Добавьте дефлектор
Как и в предыдущем примере, мы теперь добавляем следующий код в конструктор эмиттера, чтобы использовать действие Deflect
, только на этот раз мы используем дефлектор BoundingBox
для ограничения частиц внутри прямоугольной области, которая соответствует размеру рабочей области.
1
2
3
4
5
|
//deflector
var deflector:Deflector = new BoundingBox(0, 0, 640, 400);
var deflect:Deflect = new Deflect();
deflect.addDeflector(deflector);
addAction(deflect);
|
Шаг 3: ограничивающий тестовый ролик
Вот и все. Ничего особенного не изменилось, и теперь у нас есть ограниченные частицы в ограничительной рамке. Протестируйте фильм, и вы увидите результат.
Пользовательские дефлекторы
Теперь мы собираемся создавать собственные дефлекторы самостоятельно. Давайте сначала разберемся с классом Deflector
мы собираемся расширить.
Класс Deflector
является базовым классом для всех дефлекторов. Для создания пользовательских дефлекторов вы должны расширить этот класс и переопределить метод MotionData4D
calculateMotionData4D()
, а затем вернуть объект MotionData4D
представляющий векторный вывод 4D дефлектора. Введенные вами данные, как и класс Field
, включены в объект Particle2D
переданный в метод в качестве параметра. Вы можете использовать этот объект Particle2D
для определения вашего вывода. Кстати, если этот метод возвращает null
значение, действие « Deflect
предполагает, что вы не хотите изменять движение текущей частицы, игнорируете частицу и затем переходите к обработке следующей частицы.
Например, следующий дефлектор будет вращать вектор скорости каждой частицы на один градус по часовой стрелке.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
package {
import idv.cjcat.stardust.twoD.geom.Vec2D;
import idv.cjcat.stardust.twoD.particles.Particle2D;
import idv.cjcat.stardust.twoD.deflectors.Deflector;
import idv.cjcat.stardust.twoD.geom.MotionData4D;
public class Rotator extends Deflector {
override protected function calculateMotionData4D(particle:Particle2D):MotionData4D {
var velocity:Vec2D = new Vec2D(particle.vx, particle.vy);
velocity.rotateThis(1);
return new MotionData4D(particle.x, particle.y, velocity.x, velocity.y);
}
}
}
|
Эффект дефлектора
В этом примере мы собираемся расширить класс Deflector
и создать наш собственный дефлектор, имитирующий трубку, которая по сути представляет собой два линейных дефлектора, образующих свободное пространство в форме трубки.
Шаг 1: Эффект дефлектора трубки Класс эмиттера
Скопируйте документ Flash вместе с символом Circle
и документ из первого примера. Здесь мы собираемся создать еще один класс эмиттеров, еще называемый CircleEmitter
. На этот раз излучатель испускает частицы из центра сцены, и гравитационное поле не применяется.
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
|
package {
import idv.cjcat.stardust.common.actions.Age;
import idv.cjcat.stardust.common.actions.DeathLife;
import idv.cjcat.stardust.common.actions.ScaleCurve;
import idv.cjcat.stardust.common.clocks.SteadyClock;
import idv.cjcat.stardust.common.initializers.CollisionRadius;
import idv.cjcat.stardust.common.initializers.Life;
import idv.cjcat.stardust.common.initializers.Scale;
import idv.cjcat.stardust.common.math.UniformRandom;
import idv.cjcat.stardust.twoD.actions.Deflect;
import idv.cjcat.stardust.twoD.actions.Move;
import idv.cjcat.stardust.twoD.deflectors.Deflector;
import idv.cjcat.stardust.twoD.emitters.Emitter2D;
import idv.cjcat.stardust.twoD.initializers.DisplayObjectClass;
import idv.cjcat.stardust.twoD.initializers.Position;
import idv.cjcat.stardust.twoD.initializers.Velocity;
import idv.cjcat.stardust.twoD.zones.LazySectorZone;
import idv.cjcat.stardust.twoD.zones.SinglePoint;
public class CircleEmitter extends Emitter2D {
public function CircleEmitter() {
super(new SteadyClock(1));
//initializers
addInitializer(new DisplayObjectClass(Circle));
addInitializer(new Life(new UniformRandom(60, 10)));
addInitializer(new Position(new SinglePoint(320, 200)));
addInitializer(new Velocity(new LazySectorZone(8, 4)));
addInitializer(new Scale(new UniformRandom(1, 0.4)));
addInitializer(new CollisionRadius(10));
//actions
addAction(new Age());
addAction(new DeathLife());
addAction(new Move());
addAction(new ScaleCurve(0, 10));
}
}
}
|
Шаг 2: Эффект дефлектора трубки Дефлектор трубки
Теперь мы собираемся создать наш класс дефлекторов труб. Подробности объясняются в комментариях.
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
53
54
55
56
57
58
59
60
61
|
package {
import idv.cjcat.stardust.twoD.particles.Particle2D;
import idv.cjcat.stardust.twoD.deflectors.Deflector;
import idv.cjcat.stardust.twoD.geom.MotionData4D;
public class TubeDeflector extends Deflector {
private var y1:Number;
private var y2:Number;
public function TubeDeflector(y1:Number, y2:Number) {
//y2 should be larger than y2
if (y1 > y2) {
//swap y1 and y2 if y1 is larger
var temp:Number = y1;
y1 = y2;
y2 = temp;
}
this.y1 = y1;
this.y2 = y2;
}
override protected function calculateMotionData4D(particle:Particle2D):MotionData4D {
//output components, initialized to the particle’s original motion data
var x:Number = particle.x;
var y:Number = particle.y;
var vx:Number = particle.vx;
var vy:Number = particle.vy;
//caluculate actual collsion radius
var radius:Number = particle.collisionRadius * particle.scale;
//flag for whether the deflector takes effect
var deflected:Boolean = false;
if (particle.y < (y1 + radius)) {
//particle y-coordinate is less than lower limit
//set proper new y-coordinate
y = y1 + radius;
//set flag
deflected = true;
} else if (particle.y > (y2 — radius)) {
//particle y-coordinate is greater than upper limit
//set proper new y-coordinate
y = y2 — radius;
//set flag
deflected = true;
}
if (deflected) {
return new MotionData4D(x, y, vx, vy);
} else {
//ignore the particle and not update its motion data
return null;
}
}
}
}
|
Шаг 3: Эффект дефлектора трубки Добавьте дефлектор
Вы должны знать, что мы собираемся делать дальше очень хорошо сейчас. Это верно, мы собираемся добавить дефлектор к действию Deflect
а затем добавить действие к эмиттеру. Добавьте следующий код в конструктор эмиттера.
1
2
3
4
5
|
//create a tube deflector
var deflector:Deflector = new TubeDeflector(100, 300);
var deflect:Deflect = new Deflect();
deflect.addDeflector(deflector);
addAction(deflect);
|
Шаг 4: Эффект дефлектора трубки Проверьте фильм
Теперь вы можете проверить фильм. Опять же, вы также можете нарисовать некоторое визуальное представление дефлектора на сцене для лучшего вида.
Вывод
Это конец всего урока. В первой части вы узнали о гравитационных полях. Во второй части вы узнали о концепции дефлекторов и фактическом использовании действия « Deflect
. Кроме того, вы узнали, как расширить класс Deflector
для создания пользовательских дефлекторов. Теперь вы можете выполнять расширенные манипуляции с движением частиц в Stardust.
Большое спасибо за чтение!