Статьи

Стреляй по звездам с помощью двигателя Stardust Particle Engine

В этом уроке я познакомлю вас с двигателем частиц Stardust . Сначала я покажу вам, как настроить Stardust, а затем расскажу об основных обязанностях класса Stardust и о том, как они сотрудничают, чтобы заставить Stardust работать в целом.

Далее мы рассмотрим общий рабочий процесс Stardust и приступим к созданию эффекта частиц со звездами, вылетающими из курсора мыши; звезды будут постепенно замедляться, увеличиваться после рождения и уменьшаться при смерти.

Наконец, я продемонстрирую гибкость Stardust, создав несколько вариантов из уже законченного примера, включая использование анимационных роликов в качестве частиц, временную шкалу моделирования частиц и съемку экранных объектов различных классов из одного эмиттера.

Это руководство предназначено для людей, которые уже знакомы с объектно-ориентированным программированием (ООП) ActionScript 3.0, поэтому я предполагаю, что вы уже хорошо знаете, что означают классы, объекты, наследование и интерфейс. Нет проблем с ООП? Тогда пошли стрелять в некоторые звезды!




Как следует из названия, Stardust используется для создания эффектов частиц. Если вы опытный ActionScripter, вы, возможно, много раз создавали эффекты частиц с нуля и говорили: «Я совершенно классен в создании эффектов частиц с нуля, так зачем мне вообще нужен движок частиц?» Ну, Stardust здесь, чтобы помочь вам сосредоточиться больше на дизайне реального поведения частиц, чем беспокоиться об утомительных базовых вещах низкого уровня, таких как управление памятью. Вместо того чтобы писать код для обработки данных частиц, инициализации и утилизации ресурсов в Stardust, вы пропускаете эти скучные процедуры и просто решаете, как ваши частицы будут себя вести.

Структура классов Stardust была вдохновлена FLiNT Particle System , еще одним движком частиц ActionScript 3.0. Таким образом, они имеют некоторые сходные основные черты.

  • 2D и 3D эффекты частиц — Stardust можно использовать для создания эффектов 2D и 3D частиц. Он имеет свой собственный встроенный 3D-движок, и его также можно использовать для работы в сочетании с другими 3D-движками третьих сторон , включая ZedBox , Papervision3D и ND3D .
  • Высокая расширяемость — Stardust предоставляет большой набор поведений частиц и рендеров в вашем распоряжении. Если ни один из них не соответствует вашим потребностям, вы всегда можете расширить базовые классы и создать свое собственное поведение частиц; Кроме того, вы можете создать свой собственный рендер для работы с другим 3D движком, который не поддерживается Stardust.

В дополнение к этим основным функциям, Stardust также предоставляет несколько продвинутых функций для опытных пользователей.

  • Настраиваемая шкала времени симуляции — шкала времени, используемая для симуляции частиц, может динамически корректироваться во время выполнения. Например, если вы измените шкалу времени на половину оригинала, эффект частицы будет вдвое быстрее обычной скорости; и если вы настроите шкалу времени в два раза по сравнению с оригиналом, эффект частицы будет симулироваться в два раза быстрее, чем обычно. Эта функция может пригодиться, когда вы создаете игру с эффектами замедленного движения: эффект частиц может замедляться, чтобы соответствовать скорости вашего игрового движка, синхронизируясь с вашей игровой анимацией и графикой.
  • Сериализация XML — вы можете преобразовать свою систему частиц в файл в формате XML, который можно сохранить на жестком диске, позже загрузить во время выполнения и интерпретировать для восстановления вашей оригинальной системы частиц. Это очень полезно, когда вы работаете с большим проектом. Скажем, вы просто хотите немного увеличить размер ваших частиц, поэтому вы настраиваете параметры в исходном коде и перекомпилируете все ваше Flash-приложение, что может занять минуту или даже больше пяти минут, если ваш проект очень большой. Стоит ли оно того? Абсолютно нет. Это пустая трата времени. Используя функцию сериализации XML для сохранения вашей системы частиц во внешних XML-файлах, вы отделяете параметры от исходного кода для основного приложения. Поэтому вам нужно просто открыть файл XML, изменить значения параметров, сохранить его и снова открыть основное приложение. Это оно! Перекомпиляция не требуется вообще. Я бы сказал, что это идеальный способ работы с большими проектами.

Когда дело доходит до эффектов частиц, очень важно эффективно обрабатывать массивные данные частиц. Stardust интенсивно использует пулы объектов и связанные списки для повышения производительности:

  • Пулы объектов — используемые объекты хранятся в пуле; позже, если требуется объект того же типа, Stardust не создает его экземпляр немедленно, а проверяет, есть ли какой-либо объект, ранее сохраненный в пуле объектов. Если да, то Stardust просто вынимает этот объект и использует его, вместо того, чтобы создавать совершенно новый объект. Обычно эффекты частиц включают в себя множество объектов, которые потребляют процессор. Используя пулы объектов, Stardust значительно снижает накладные расходы на создание экземпляров.
  • Связанные списки — очень легко и заманчиво хранить данные частиц в массиве; однако в случае, когда частицы создаются и удаляются очень часто, происходит много сращивания массива для удаления мертвых частиц. Сращивание массивов — это процесс, потребляющий процессор, особенно для длинных массивов. Для связанного списка, независимо от его длины, всегда требуется одинаковое короткое время для склеивания мертвых частиц. Начиная с версии 1.1, Stardust начал использовать связанные списки для хранения данных частиц.

Прежде чем мы перейдем к реальному кодированию, нам нужно взять копию Stardust Particle Engine. Он выпущен по лицензии MIT, что означает, что он абсолютно бесплатный, независимо от того, хотите ли вы использовать его в коммерческом или некоммерческом проекте.

Вот домашняя страница проекта Stardust: http://code.google.com/p/stardust-particle-engine/

Вы можете скачать Stardust здесь: http://code.google.com/p/stardust-particle-engine/downloads/list

На момент написания последней версии, которую можно загрузить из списка загрузок, является 1.1.132 Beta. Вы всегда можете получить последнюю версию из репозитория SVN (однако, она может быть нестабильной).

На домашней странице проекта вы также можете найти дополнительные аксессуары, такие как документация по API и копию руководства в формате PDF . На YouTube есть даже видеоуроки.

Здесь я кратко расскажу об основных классах Stardust и их обязанностях.

Этот класс является суперклассом всех базовых классов, который определяет свойства и методы, особенно для сериализации XML.

Вообще говоря, эффекты частиц — это все, что касается контроля количества объектов со схожим, но рандомизированным внешним видом и поведением. Класс Random предназначен для генерации случайных чисел, которые можно использовать в Stardust для рандомизации свойств частиц. Например, класс UniformRandom является подклассом класса Random, и его имя говорит обо всем: случайное число, сгенерированное объектом UniformRandom, распределено равномерно, и я буду использовать этот класс, в частности, для всего урока.

Бывают случаи, когда одномерного случайного числа недостаточно. Иногда нам нужны двумерные случайные числа, которые по сути являются парами случайных чисел, для таких свойств, как положение и скорость. Класс Zone предназначен для генерации двумерных пар случайных чисел. Этот класс моделирует пару случайных чисел как случайную точку в 2D-зоне. Например, CircleZone генерирует пары случайных чисел (x, y) из случайных точек в круговой области. Классы Random и Zone в основном используются классом Initializer, который будет рассмотрен позже. Класс Zone3D является трехмерным аналогом этого класса для эффектов трехмерных частиц.

Класс Emitter — это то, где все низкоуровневые вещи инкапсулированы. Излучатель инициализирует вновь созданные частицы перед их добавлением в моделирование, обновляет свойства частиц в каждой итерации основного цикла и удаляет мертвые частицы из моделирования. Метод Emitter.step () — это то, что вы хотите вызывать несколько раз, чтобы поддерживать и запускать Stardust.

Класс Clock определяет скорость создания новой частицы для излучателей. Один объект Emitter содержит ровно одну ссылку на объект Clock. В начале каждого вызова метода Emitter.step () излучатель спрашивает объект часов, сколько новых частиц он должен создать. Возьмем, к примеру, класс SteadyClock, он говорит эмитентам создавать новые частицы с постоянной скоростью.

Этот класс предназначен для инициализации вновь созданных частиц. Объект Инициализатор должен быть добавлен в эмиттер для его работы. По сути, один подкласс Initializer инициализирует только одно свойство частицы. Например, класс инициализатора Mass инициализирует массу новых частиц. Некоторые инициализаторы принимают объект Random в качестве параметра конструктора для инициализации частиц со случайными значениями. Следующий код создает инициализатор жизни, который инициализирует жизни частиц до значений с центром в 50 с вариацией 10, а именно в диапазоне от 40 до 60.

1
new Life(new UniformRandom(50, 10));

Объекты действия обновляют свойства частиц на каждой итерации основного цикла (метод Emiter.step ()). Например, класс действия Move обновляет положения частиц в соответствии со скоростью. Объект Action должен быть добавлен в эмиттер, чтобы он работал.

Теперь, когда вы знаете, как основные классы взаимодействуют друг с другом, давайте рассмотрим общий рабочий процесс для Stardust.

Вы начинаете с создания эмиттера. Используйте класс Emitter2D для эффектов 2D-частиц и класс Emitter3D для 3D-эффектов.

1
var emitter:Emitter = new Emitter2D();

Чтобы указать скорость создания частиц, нам нужны часы. Это можно установить с помощью свойства Emitter.clock или путем передачи часов в качестве первого параметра конструктору эмиттера.

1
2
3
4
5
//property approach
emitter.clock = new SteadyClock(1);
 
//constructor approach
var emitter:Emitter = new Emitter2D(new SteadyClock(1));

Добавьте инициализаторы к эмиттеру с помощью метода Emitter.addInitializer ().

1
2
emitter.addInitializer(new Life(new UniformRandom(50, 10)));
emitter.addInitializer(new Scale(new UniformRandom(1, 0.2)));

Добавьте действия к эмиттеру с помощью метода Emitter.addAction ().

1
2
emitter.addAction(new Move());
emitter.addAction(new Spin());

Создайте средство визуализации и добавьте эмиттер к нему через метод Renderer.addEmitter ().

1
2
var renderer:Renderer = new DisplayObjectRenderer(container);
renderer.addEmitter(emitter);

Наконец, неоднократно вызывайте метод Emitter.step (), чтобы продолжить моделирование частиц. Вы можете использовать событие enter-frame или таймер для этого. В одном вызове метода Emitter.step () часы определяют, сколько новых частиц должно быть создано, эти новые частицы инициализируются инициализаторами, все частицы обновляются действиями, мертвые частицы удаляются, и, наконец, рендеринг рендеринга эффект частиц.

1
2
3
4
5
6
7
8
//enter-frame event approach
addEventListener(Event.ENTER_FRAME, mainLoop);
 
//timer approach
timer.addEventListener(TimerEvent.TIMER, mainLoop);
function mainLoop(e:Event):void {
    emitter.step();
}

Хорошо. Вот и все это для начинающих Stardust. Теперь пришло время открыть Flash IDE и испачкать руки.

Создайте новый документ Flash с размером 640X400, частотой кадров 60 кадров в секунду и темным фоном. Здесь я сделал темно-синий градиентный фон. Кстати, Stardust хорошо работает как с Flash Player 9, так и с 10, так что это нормально, независимо от того, используете ли вы Flash CS3 или CS4. В этом уроке я буду использовать Flash CS3.

Мы создаем эффект частиц со звездами, поэтому нам нужно нарисовать звезду и преобразовать ее в символ, конечно же, экспортированный для ActionScript. Этот символ будет использован позже для визуализации эффекта частиц. Назовите символ и экспортируемый класс «Звезда».

Создайте новый класс документа и назовите его StarParticles.

01
02
03
04
05
06
07
08
09
10
package {
    import flash.display.Sprite;
     
    public class StarParticles extends Sprite {
         
        public function StarParticles() {
             
        }
    }
}

Как уже упоминалось в общем рабочем процессе, первым шагом является создание эмиттера. И следующим шагом является добавление инициализаторов и действий для эмиттера. Хотя это можно сделать в конструкторе класса документа, я настоятельно рекомендую сделать это в отдельном подклассе Emitter. Всегда лучше отделить дизайн поведения частиц от основной программы; благодаря этому код становится намного чище и его легче модифицировать в будущем, не путая с основной программой.

Мы собираемся создать эффект двумерной частицы, поэтому Emitter2D — это класс эмиттера, который мы собираемся расширить. Расширьте класс Emitter2D и назовите его StarEmitter, так как мы собираемся заставить его снимать звезды позже. Конструктор Emitter принимает параметр Clock, поэтому мы объявим параметр конструктора для передачи ссылки на объект Clock в конструктор суперкласса.

01
02
03
04
05
06
07
08
09
10
11
package {
    import idv.cjcat.stardust.twoD.emitters.Emitter2D;
     
    public class StarEmitter extends Emitter2D {
         
        public function StarEmitter(clock:Clock) {
            //pass on the clock object to the superclass’s constructor
            super(clock);
        }
    }
}

Лучший подход к созданию подкласса эмиттера состоит в том, чтобы объявить параметры частиц как статические константы, сгруппированные в одном месте. Так что, если вы хотите настроить параметры, вы всегда будете знать, где найти объявления. Значение этих констант будет объяснено позже, когда они будут использованы.

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
//average lifespan
private static const LIFE_AVG:Number = 30;
 
//lifespan variation
private static const LIFE_VAR:Number = 10;
 
//average scale
private static const SCALE_AVG:Number = 1;
 
//scale variation
private static const SCALE_VAR:Number = 0.4;
 
//scale growing time
private static const GROWING_TIME:Number = 5;
 
//scale shrinking time
private static const SHRINKING_TIME:Number = 10;
 
//average speed
private static const SPEED_AVG:Number = 10;
 
//speed variation
private static const SPEED_VAR:Number = 8;
 
//average omega (angular velocity)
private static const OMEGA_AVG:Number = 0;
 
//omega variation
private static const OMEGA_VAR:Number = 5;
 
//damping coefficient
private static const DAMPING:Number = 0.1;

Какие инициализаторы нам нужны для создания эффекта частиц? Давайте посмотрим на список ниже:

  • DisplayObjectClass — этот инициализатор назначает указанный экранный объект каждой частице, который будет использоваться DisplayObjectRenderer для визуализации эффектов частиц. Конструктор принимает ссылку класса на класс экранного объекта, который мы хотим создать; для этого урока этот класс будет тем классом Star (символ), который мы создали на шаге 2.
  • Жизнь — этот инициализатор присваивает каждой частице случайное значение жизни. Позже мы добавим действия к излучателю, чтобы истощить это жизненное значение во времени и пометить частицу как мертвую, если ее жизненное значение достигнет нуля. Случайный объект передается конструктору, который будет использоваться этим инициализатором для генерации случайного значения для жизни частиц. В большинстве случаев класс UniformRandom удобен и достаточен; первый параметр конструктора UniformRandom — это центральное (или среднее) значение сгенерированных случайных чисел, а второй — радиус (или вариация). Например, объект UniformRandom с центром 20 и вариацией 5 генерирует случайные числа в диапазоне [15, 25]. Здесь мы используем константу LIFE_AVG для значения центра и LIFE_VAR для радиуса.
  • Масштаб — Подобно инициализатору Life, инициализатор Scale инициализирует масштаб частицы случайным значением, определяемым объектом Random, передаваемым конструктору инициализатора. Здесь мы используем константу SCALE_AVG для значения центра и SCALE_VAR для радиуса.
  • Положение — этот инициализатор назначает частице случайную позицию. В отличие от инициализаторов Life и Scale, которым требуются только 1D случайные числа, инициализатору Position требуются 2D генераторы пар случайных чисел. Как описано в разделе «Обязанности класса Stardust», класс Zone предназначен именно для этой цели. Объект Zone, переданный в конструктор инициализатора, используется для генерации двумерных пар случайных чисел, которые будут назначены частицам как векторы положения. В этом уроке мы собираемся заставить звезды стрелять из одной точки, расположенной у курсора мыши, поэтому мы будем использовать класс SinglePoint, который является подклассом Zone. Чтобы динамически корректировать координаты этого объекта SinglePoint из класса документа, нам нужно предоставить ссылку на этот точечный объект через открытое свойство. Для этого и используется свойство «point».
  • Скорость — Аналогично инициализатору положения, инициализатору скорости необходим объект Zone для создания двумерных пар случайных значений для инициализации скоростей частиц. 2D-вектор, сгенерированный объектом Zone, который является координатой случайной точки в зоне, присваивается частицам в качестве их скоростей. Здесь мы используем класс LazySectorZone, который представляет область сектора. Сектор — это часть круга, ограниченная двумя радиусами и двумя углами. Для LazySectorZone два угла по умолчанию равны 0 и 360, представляя полный угол вокруг круга. Первый параметр конструктора класса LazySectorZone является средним из двух радиусов, а второй — вариацией радиусов. В этом случае среднее из двух радиусов представляет среднюю скорость, а изменение радиусов представляет изменение скорости. Здесь мы используем константу SPEED_AVG для первого параметра и SPEED_VAR для второго.
  • Вращение — инициализатор вращения инициализирует угол поворота частицы в случайное значение. И как некоторые из вышеупомянутых инициализаторов, конструктор принимает объект Random для генерации случайного значения. Поскольку нам бы хотелось иметь частицы с углами от 0 до 360 градусов, мы будем использовать 0 в качестве центра и 180 в качестве радиуса объекта UniformRandom.
  • Омега — Омега, как и в большинстве учебников по физике, означает угловую скорость. С учетом сказанного цель этого инициализатора ясна: он инициализирует угловую скорость частицы случайным значением, а константа OMEGA_AVG используется в качестве центра, а OMEGA_VAR — в качестве радиуса объекта UniformRandom.

И вот код:

1
2
3
4
5
6
7
8
9
point = new SinglePoint();
 
addInitializer(new DisplayObjectClass(Star));
addInitializer(new Life(new UniformRandom(LIFE_AVG, LIFE_VAR)));
addInitializer(new Scale(new UniformRandom(SCALE_AVG, SCALE_VAR)));
addInitializer(new Position(point));
addInitializer(new Velocity(new LazySectorZone(SPEED_AVG, SPEED_VAR)));
addInitializer(new Rotation(new UniformRandom(0, 180)));
addInitializer(new Omega(new UniformRandom(OMEGA_AVG, OMEGA_VAR)));

Хорошо, мы закончили с инициализаторами. Теперь пришло время добавить действия для эмитента. Ниже приведен список необходимых нам действий:

  • Age — действие Age уменьшает жизненную ценность частицы на 1 на каждом шаге эмиттера.
  • DeathLife — Когда значение жизни частицы достигает нуля, это действие помечает частицу как мертвую, изменяя ее свойство isDead с false на true. В конце шага эмиттера мертвые частицы удаляются.
  • Перемещение — в значительной степени, как следует из его названия, действие Переместить обновляет положения частиц в соответствии с их скоростями.
  • Spin — аналогично действию Move, действие Spin обновляет угол поворота частицы в соответствии с значением омега частицы (угловой скоростью).
  • Демпфирование — это действие умножает скорость частицы на коэффициент в диапазоне [0, 1], имитируя эффекты демпфирования и постепенно замедляя частицу. Один фактор означает полное отсутствие демпфирования: частицы движутся свободно, как если бы не было демпфирующего эффекта; нулевой коэффициент означает полное затухание: все частицы не могут двигаться немного. Этот коэффициент определяется «коэффициентом демпфирования» по следующей формуле: «коэффициент = 1 — (коэффициент демпфирования)». Параметр, передаваемый конструктору, является коэффициентом демпфирования; здесь мы просто хотим немного затухающего эффекта, поэтому мы используем значение 0,1 для коэффициента.
  • ScaleCurve — действие ScaleCurve изменяет масштаб частицы в соответствии с ее жизненным значением. Он растет от начальной шкалы до нормальной шкалы после рождения и постепенно умирает до конечной шкалы. Конечно, частица также может иметь начальное или конечное значение масштаба, которое больше нормального масштаба; это зависит только от личного выбора. Во многих случаях мы хотели бы, чтобы частицы имели начальное и конечное значение масштаба, равное нулю, что является значением по умолчанию. Первый параметр в конструкторе обозначает время роста частицы, а второй — время затухания; поэтому мы передаем константы GROWING_TIME и SHRINKING_TIME в качестве первого и второго параметра соответственно. Время роста равно 5, что означает, что частица растет от нулевой шкалы до нормальной шкалы в течение первых 5 единиц жизни; и время сжатия равно 15, что означает, что частица сжимается до нулевого масштаба в последние 15 единиц срока службы. Обратите внимание, что по умолчанию переход является линейным, но можно использовать любую функцию замедления, в частности уравнения замедления, созданные Робертом Пеннером. Есть еще одно подобное действие, называемое AlphaCurve , которое аналогичным образом работает с альфа-значениями.

Вот и все. Наш излучатель готов. Вот код этого эмиттера в полном объеме, включая необходимые операторы импорта.

1
package { import idv.cjcat.stardust.common.actions.Age;

Теперь пришло время вернуться к классу документа и закончить его. Давайте посмотрим на оставшиеся задачи.

  • Создайте экземпляр StarEmitter — мы собираемся создать экземпляр класса StarEmitter, который мы только что закончили.
  • Назначьте объект Clock для эмиттера. Мы хотим иметь постоянную скорость излучения частиц, поэтому мы будем использовать класс SteadyClock. Параметр, передаваемый конструктору часов, — это скорость излучения или, другими словами, количество новых частиц, созданных на каждом шаге эмиттера; дробный коэффициент 0,5 означает, что на каждом шаге эмиттера существует 50% -ная вероятность создания новой частицы и 50% -ная вероятность отсутствия частицы.
  • Создание рендерера. Чтобы визуализировать эффект частиц, нам понадобится рендерер. DisplayObjectRenderer следует использовать вместе с инициализатором DisplayObjectClass: инициализатор назначает экранный объект каждой частице, а средство визуализации добавляет эти экранные объекты в список отображения контейнера, постоянно обновляя их. Также не забудьте добавить эмиттер к рендереру.
  • Повторно вызывайте основной цикл — этот последний шаг поддерживает работу Stardust. Здесь мы будем использовать событие enter-frame.
  • Ниже приведен полный код класса документа, включая необходимые операторы импорта.

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
package {
    import flash.display.Sprite;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.geom.Rectangle;
    import idv.cjcat.stardust.common.clocks.SteadyClock;
    import idv.cjcat.stardust.common.renderers.Renderer;
    import idv.cjcat.stardust.twoD.renderers.DisplayObjectRenderer;
     
    public class StarParticles extends Sprite {
         
        private var emitter:StarEmitter;
         
        public function StarParticles() {
            //instantiate the StarEmitter
            emitter = new StarEmitter(new SteadyClock(0.5));
             
            //the container sprite
            var container:Sprite = new Sprite();
             
            //the renderer that renders the particle effect
            var renderer:Renderer = new DisplayObjectRenderer(container);
            renderer.addEmitter(emitter);
             
            //add the container to the display list, above the background
            addChildAt(container, 1);
             
            //make use of the enter-frame event
            addEventListener(Event.ENTER_FRAME, mainLoop);
        }
         
        private function mainLoop(e:Event):void {
            //update the SinglePoint position to the mouse position
            emitter.point.x = mouseX;
            emitter.point.y = mouseY;
             
            //call the main loop
            emitter.step();
        }
    }
}

Наконец-то мы закончили! Теперь давайте посмотрим на результат. Нажмите CTRL + ENTER во Flash, чтобы проверить фильм, и вы увидите результат.


Мы еще не закончили! Давайте сделаем еще несколько вариантов. Первый использует анимационные клипы для наших частиц.

Этот первый вариант довольно прост и не требует дополнительного кодирования. Это так же просто, как создать базовую анимацию на временной шкале. Отредактируйте символ звезды в Flash IDE, создайте еще один ключевой кадр и измените цвет звезды в этом кадре на красный. Это по существу заставляет звезды мигать между желтым и красным. Возможно, вы захотите вставить еще несколько пустых кадров между ними, поскольку частота кадров 60 кадров в секунду слишком высока для мигания двух кадров.

Теперь протестируйте фильм и проверьте результат. Эффект мигающей звезды выглядит мультяшно; это может быть использовано для классических головокружительных эффектов, которые обычно можно увидеть в мультфильмах.


Как я упоминал ранее, одной из функций Stardust является «регулируемая шкала времени моделирования», что означает, что шкалу времени, используемую Stardust для моделирования частиц, можно динамически регулировать. Все делается путем изменения свойства Emitter.stepTimeInterval, которое по умолчанию равно 1. Следующий фрагмент кода изменяет это значение на 2, в результате чего частицы движутся в два раза быстрее, а излучатель создает новые частицы с двойной скоростью.

1
emitter.stepTimeInterval = 2;

В этом варианте мы создадим ползунок на сцене и используем его для динамической настройки шкалы времени моделирования.

Перетащите компонент Slider из панели компонентов на сцену. Назовите это «слайдер».

Нам бы хотелось, чтобы ползунок скользил между 0,5 и 2, а это значит, что мы хотим, чтобы наше моделирование частиц было как минимум вдвое быстрее обычного и максимально вдвое быстрее. Кроме того, установите «liveDragging» в true, чтобы мы могли видеть обновление, когда мы чистим большой палец ползунка.

Теперь нам нужно прослушать событие изменения ползунка для динамического изменения шкалы времени симуляции. Сначала импортируйте класс SliderEvent в классе документа.

1
import fl.events.SliderEvent;

А затем прослушайте событие изменения ползунка в конструкторе класса документа.

1
slider.addEventListener(SliderEvent.CHANGE, changeTimescale);

Наконец, измените временную шкалу симуляции в слушателе. Добавьте следующий слушатель к классу документа.

1
2
3
private function changeTimescale(e:SliderEvent):void {
    emitter.stepTimeInterval = slider.value;
}

Давайте посмотрим на результат. Обратите внимание, что когда вы чистите большой палец ползунка влево, симуляция частиц замедляется, и если вы чистите вправо, симуляция идет быстрее.


Stardust предоставляет специальный инициализатор SwitchInitializer. Его конструктор принимает два массива в качестве параметров; первый представляет собой массив инициализаторов, а второй, имеющий ту же длину, что и первый, является массивом «весов». Каждый раз, когда Stardust использует этот инициализатор для инициализации частиц, один из инициализаторов в первом массиве выбирается случайным образом для инициализации частиц. Чем больше вес инициализатора, тем больше вероятность, что он будет выбран для инициализации. В этом варианте мы заставим излучатель излучать не только звезды, но и белые круги.

Нарисуйте белый круг, преобразуйте его в символ, экспортированный для ActionScript, назовите символ и класс «Круг». Этот класс мы будем использовать для белых кругов.

Откройте класс StarEmitter и удалите следующую строку. Это отключает инициализатор DisplayObjectClass, который мы настроили на предыдущих шагах.

1
addInitializer(new DisplayObjectClass(Star));

Теперь добавьте следующий код в конструктор. Мы бы хотели, чтобы звезды и круги излучались с одинаковой вероятностью, поэтому мы дадим им обоим вес 1.

01
02
03
04
05
06
07
08
09
10
11
//the initializer for stars
var doc1:DisplayObjectClass = new DisplayObjectClass(Star);
 
//the initializer for white circles
var doc2:DisplayObjectClass = new DisplayObjectClass(Circle);
 
//the switch initializer
var si:SwitchInitializer = new SwitchInitializer([doc1, doc2], [1, 1]);
 
//add the switch initializer to the emitter
addInitializer(si);

Наконец, протестируйте фильм. Вы можете видеть, как звезды и круги испускаются из излучателя, хотя угловая скорость не оказывает большого влияния на окружности.


Это конец этого урока. Спасибо за чтение! Вы могли бы сказать, что это целый код, просто чтобы создать такой простой эффект, но, эй, вы помните написание какого-либо кода, кроме проектирования поведения частицы на высоком уровне? В будущем, если вы хотите изменить свой код, вы хотели бы видеть код, подобный этому:

1
2
3
4
particle.vx *= 0.9;
particle.vy *= 0.9;
particle.x += particle.vx * 2;
particle.y += particle.vy * 2;

… или как это?

1
2
3
emitter.addAction(new Damping(0.1));
emitter.addAction(new Move());
emitter.stepTimeInterval = 2;

Stardust здесь, чтобы помочь вам создавать эффекты частиц в покое. Спроектируйте поведение частиц на высоком уровне, сообщите Stardust, что делать, и пусть Stardust выполнит низкоуровневую реализацию для вас!

Я надеюсь, что вы поняли, как Stardust работает внутри, и теперь достаточно знакомы с основным рабочим процессом. В будущем я напишу больше учебников по пользовательским инициализаторам / действиям / средствам визуализации, эффектам трехмерных частиц и сериализации XML. Еще раз спасибо за чтение этого урока. С Днем Stardusting!