Это вторая часть этого урока. Я собираюсь показать вам, как управлять движением частиц с помощью дефлекторов.
Требуется предварительное знание основ движения и векторных полей. Я настоятельно рекомендую вам пройти первую часть этого урока, прежде чем двигаться дальше.
Окончательный результат предварительного просмотра
Посмотрите на конечный результат, к которому мы будем стремиться. Это пример эффекта пола, когда частицы отскакивают от пола.
Дефлекторы
Подобно гравитационным полям, дефлектор принимает данные о текущем движении частицы в качестве входных данных. После этого дефлектор перезаписывает движение частицы своим выходным сигналом только для того, чтобы выходные данные теперь содержали данные скорости в дополнение к данным положения. Таким образом, в двумерном пространстве выходной сигнал дефлектора представляет собой вектор 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.
Большое спасибо за чтение!