Rain Animation with Splashing Raindrops - AS3 Effect
Примеры разбрызгивания дождевых капель
Мы предоставляем вам гибкий пользовательский AS3 класс RainDisplay, который позволяет создавать множество 2D анимаций дождевых капель. Капли образуют брызги при ударе о землю. Ниже мы показываем два примера, чтобы проиллюстрировать основное использование класса RainDisplay.
Загрузка
•Загрузите файлы исходников: rain.rar Все исходные файлы в пакете хорошо откомментированы. Существует несколько интересных аспектов в коде в этом уроке: использование связанных списков и "корзины" для увеличения оперативности, и техника анимации, которая вызывает отрисовку в битмап и использование фильтров для того, чтобы создать появление плавного движения. Путем легкого изменения параметров может быть получен другой эффект в следующем примере.
Класс RainDisplay и другие пользовательские AS3 классы
Класс RainDisplay
Класс RainDisplay - это расширение класса Sprite; экземпляр класса RainDisplay - это дисплей объект, который можно добавить на сцену.
После того, как экземпляр класса RainDisplay был построен и добавлен на сцену, и заданы желаемые параметры, в основном коде просто необходимо вызвать функцию addDrop для добавления отдельных капель на сцену и функцию update для обновления анимации. В функции addDrop могут быть установлены различные параметры для вновь созданной капли дождя, такие как начальная скорость и цвет.
После того, как капли были добавлены к дисплей листу, их движение регулируется под гравитацией и скоростью ветра. В основном коде просто необходимо сделать повторяющиеся вызовы функции update (на основе или EnterFrame или TimerEvent), чтобы воспроизводить анимацию.
В функции addDrop капля добавляется в дисплей лист (капли используются как члены класса LineRaindrop). Первые два требуемых параметра дают положение новой капли. Функция возвращает добавленную дождевую каплю, которая может быть удобна для ссылки на новые капли в коде.
Есть несколько необязательных параметров в функции addDrop, которые определяют атрибуты для новой капли. Они сами себя объясняют, за исключением атрибута splashing. Если капля имеет булевский атрибут splashing и он установлен в true, будет создаваться всплеск, когда она пересечет землю. Если нет, она исчезнет без всплеска.
Одно замечание относительно скорости капли дождя: капля дождя может иметь постоянную "предельную скорость", это будет достигнуто при установке ее булевой переменной atTerminalVelocity равной true. Такие капли не будет иметь скорости, и пострадают от тяжести.
Если необязательные параметры не определяются в функции addDrop, используются значения по умолчанию. Значения по умолчанию содержатся в паблик переменных, которые сами себя объясняют в коде RainDisplay.
Капли можно раскрашивать несколькими методами. Они могут иметь (1) постоянный цвет, (2) случайный серый цвет, (3) случайный цвет в градиенте между двумя определенными цветами, или (4) полностью случайный цвет. Переменные и булевы флаги используются для этих различных методов раскрашивания, это должно быть ясно при изучении кода RainDisplay класса.
Капли - это члены класса LineRaindrop, они отрисовываются просто как отрезки линий. Эти капли могут быть установлены как "long" или "short" типы. Короткая капля рисуется как линия от последней позиции капли к новой позиции. Длинная капля отрисовывается от своего положения два фрейма преимущественно к ее настоящему положению. Этот метод отрисовки создает эффект длинной непрерывной полосы движения, а не дискретных точек.
Есть много переменных, включенных в классе RainDisplay, которые позволяют настроить внешний вид и поведение анимации. Если вы внимательно посмотрите на код, то увидите, что большинство переменных говорят сами за себя.
Несколько комментов по коду
Хотя для разработчика нет нужды понимать всю внутреннюю работу класса RainDisplay, некоторые аспекты в коде могут представлять интерес. Чтобы запомнить списки капель, код в этом классе использует связанные списки вместо массивов, поскольку массивы, как правило, в коде работают более медленнее. Другой эффективный метод - сделать использование “корзины” для капель, которые покидают видимый диапазон анимации. Вместо удаления капель, когда они выходят за видимый диапазон анимации, они делаются невидимыми, удаляются из основного листа капель и помещаются в корзину (которая является еще одним связанным списком) где они запоминаются для дальнейшего использования. Когда нужны новые капли, они берутся из корзины (если она не пустая); цвет и другие параметры для дождевой капли восстанавливаются в соответствии с пожеланиями. Этот метод предоставляет постоянное создание новых капель дождя, а также частые добавления и удаления капель со сцены, все это является трудоемкой задачей.
Брызги, которые возникают от капель дождя, достигающих земли, могут привнести в код сложный аспект, но на самом деле всплески было довольно легко закодировать. Каждый всплеск просто создается путем добавления дополнительных капель дождя на сцену, с возрастающимися скоростями (с некоторой рандомизацией). Однако это представляет потенциальную проблему: как предотвратить новые капли от водяных брызг снова после столкновения с землей, создавая все больше и больше капель дождя в дисплей листе? Секрет содержится в булевом атрибуте splashing в классе LineRaindrop. Только капли, которые имеют атрибут splashing, установленный в true, создают брызги при ударе. Капли, которые создаются как часть всплеска, имеют свой атрибут splashing установленным в false, поэтому когда они ударяются о землю, они исчезают без создания новых брызг.
Как RainDisplay класс используется в примерах
В обоих представленных примерах rain display в действительности не добавляется на сцену. Вместо этого он отрисовывается в битмап на сцене в каждом фрейме анимации. Это позволяет использовать некоторую технику фильтров. Вместо того, чтобы стирать битмап и перерисовывать каждый фрейм, старая картинка должна затухать с помощью ColorTransform. Это приводит к тому, что красивый след остается позади опускающейся капли, что создает хороший эстетический эффект. Вы можете поэкспериментировать с другими типами фильтров (например,использовать BlurFilter вместо ColorTransform) для создания различных видов эффектов.
Пример RainDisplay_Basic иллюстрирует наиболее частое использование класса RainDisplay для создания падающих и разбрызгивающихся капель. Скорость ветра изменяет анимацию на всем протяжении.
Пример RainDisplay_Dripping создает эффект падающих капель с рандомной раскраской. Здесь мы даем каждой капле начальную нулевую скорость, таким образом, чтобы капли начали свое падение от положения покоя. Мы также явно используем некоторые переменные, которые оставались в дефолтных значениях в первом примере, но которые были разработаны для красочного эффекта капания. Есть переменные globalBreakawayTime и breakawayTimeVariance. globalBreakawayTime устанавливает время в течение которого должна находиться на сцене пред тем, как начнет двигаться (она названа “global” чтобы отразить, что эффекты всех капель добавляются, чтобы отобразить дождь). Переменная breakawayTimeVariance придает некоторую случайную вариацию для этого breakaway time.
Привожу вашему вниманию этот класс
Code
/* Класс RainDisplay -это расширение класса Sprite; экземпляр класса RainDisplay это display object, который можно добавить на сцену.
Взаимодействие с объектом RainDisplay в основном коде главным образом полностью происходит с помощью 2 методов: addDrop и update. Функция addDrop добавляет капли для отображения, а функция update изменяет и перерисовывает капли в дисплей листе, перемещая капли в соответствии с их скоростями и ускорением благодаря гравитации.
Первые два параметра дают положение для новой капли. Необязательные параметры определяют другие атрибуты для новой капли, и говорят сами за себя, за исключением атрибута "splashing". Если капля имеет булев атрибут splashing, установленный в true, будет создаваться всплеск, когда она коснется земли. Если же нет, она исчезнет без всплеска.
//linked list - onStageList - это список всех капель, //которые в настоящее время анимированы. private var onStageList:LinkedList; //recycleBin хранит капли, которые больше не являются частью анимации, но //которые могут использоваться снова, когда нужны новые капли. private var recycleBin:LinkedList;
public var numOnStage:Number; public var numInRecycleBin:Number; public var displayWidth:Number; public var displayHeight:Number;
//вектор, определяющий скорость ветра: public var wind:Point;
public var defaultInitialVelocity:Point; public var defaultDropThickness:Number; public var windOnSplash:Number; public var noSplashes:Boolean;
//defaultDropColor используется только, когда капли не окрашиваются рандомно //а окрашены в серые тона, градиент, или полностью случайного цвета. public var defaultDropColor:uint;
public var randomizeColor:Boolean; public var colorMethod:String; public var minGray:Number; public var maxGray:Number; public var _gradientColor1:uint; public var _gradientColor2:uint; public var dropLength:String; public var minSplashDrops:Number; public var maxSplashDrops:Number; public var defaultDropAlpha:Number; public var splashAlpha:Number; public var splashThickness:Number; public var splashMinVelX:Number; public var splashMaxVelX:Number; public var splashMinVelY:Number; public var splashMaxVelY:Number;
//если капли выходят за пределы видимости окна xRange, они могут быть //удалены из анимации или сохранены для проигрыания. Если ветер изменяется быстро, //существует возможность, чтобы капли появились вновь со стороны, итак //вы можете пожелать сохранять их, и установить следующую переменную в false. public var removeDropsOutsideXRange:Boolean;
//Эти параметры для изменений разрешают контролировать //случайные вариации в скоростях капелек public var initialVelocityVarianceX:Number; public var initialVelocityVarianceY:Number; public var initialVelocityVariancePercent:Number;
public var globalBreakawayTime:Number; public var breakawayTimeVariance:Number;
private var displayMask:Sprite; private var left:Number; private var right:Number; private var r1:Number; private var g1:Number; private var b1:Number; private var r2:Number; private var g2:Number; private var b2:Number; private var param:Number; private var r:Number; private var g:Number; private var b:Number; private var numSplashDrops:int; private var outsideTest:Boolean; private var variance:Number; private var dropX:Number;
public function RainDisplay(w = 400, h=300, useMask = true) { displayWidth = w; displayHeight = h; onStageList = new LinkedList(); recycleBin = new LinkedList(); wind = new Point(0,0); defaultInitialVelocity = new Point(0,0); initialVelocityVarianceX = 0; initialVelocityVarianceY = 0; initialVelocityVariancePercent = 0; windOnSplash = 0.20;
noSplashes = false;
numOnStage = 0; numInRecycleBin = 0;
if (useMask) { displayMask = new Sprite(); displayMask.graphics.beginFill(0xFFFF00); displayMask.graphics.drawRect(0,0,w,h); displayMask.graphics.endFill(); this.addChild(displayMask); this.mask = displayMask; }
public function get gradientColor1():uint { return _gradientColor1; }
public function get gradientColor2():uint { return _gradientColor2; }
public function set gradientColor1(input) { _gradientColor1 = uint(input); r1 = (_gradientColor1 >>16) & 0xFF; g1 = (_gradientColor1 >>8) & 0xFF; b1 = _gradientColor1 & 0xFF; }
public function set gradientColor2(input) { _gradientColor2 = uint(input); r2 = (_gradientColor2 >>16) & 0xFF; g2 = (_gradientColor2 >>8) & 0xFF; b2 = _gradientColor2 & 0xFF; }
//аргументы - x, y, velx, vely, color, thickness, splashing public function addDrop(x0:Number, y0:Number, ...args):LineRaindrop { numOnStage++; var drop:LineRaindrop; var dropColor:uint; var dropThickness:Number;
//проверим recycle bin для доступной капли: if (recycleBin.first != null) { numInRecycleBin--; drop = recycleBin.first; //удаляем из bin if (drop.next != null) { recycleBin.first = drop.next; drop.next.prev = null; } else { recycleBin.first = null; } drop.resetPosition(x0,y0); drop.visible = true; } //если recycle bin пустой, создаем новую каплю: else { drop = new LineRaindrop(x0,y0); //добавляем в дисплей лист this.addChild(drop); }
//установим тип есть брызги/нет брызг type if (args.length > 5) { drop.splashing = args[5]; } else { //выключим всплеск (splashing) если глобально noSplashes установлено в true. //иначе сдеаем каплю типа splashing. drop.splashing = !noSplashes; }
public function update():void { var drop:LineRaindrop = onStageList.first; var nextDrop:LineRaindrop; while (drop != null) { //перед тем как списки изменятся, запишем следующую каплю nextDrop = drop.next; //двигаем все капли. Для каждой капли в onStageList:
drop.lifespan++;
//изменяем только, если время жизни капли превысило время breakaway. if (drop.lifespan > drop.breakawayTime) {
//изменим vel if (!drop.atTerminalVelocity) { drop.vel.y += gravity; }
//изменим позицию p1 //Из эстетических соображений мы применим меньший ветер к брызгам, чем к //падающим каплям. if (drop.splashing) { drop.p1.x += drop.vel.x + wind.x; drop.p1.y += drop.vel.y + wind.y; } else { drop.p1.x += drop.vel.x + windOnSplash*wind.x; drop.p1.y += drop.vel.y + windOnSplash*wind.y; }
//изменим p0 if (dropLength == "long") { //use for longer drops: drop.p0.x = drop.lastLastPos.x; drop.p0.y = drop.lastLastPos.y; } else if (dropLength == "short") { drop.p0.x = drop.lastPos.x; drop.p0.y = drop.lastPos.y; } else { //можем добавить другие виды типа dropLength, например, постоянной длины. }
//если капля вышла за сцену, добавляем ее в recycle bin, делаем невидимой if (removeDropsOutsideXRange) { left = Math.min(drop.p0.x,drop.p1.x); right = Math.max(drop.p0.x,drop.p1.x); outsideTest = ((drop.p0.y > displayHeight)||(right < 0)||(left > displayWidth)); //у нас нет необходимости создавать всплеск для капель, которые исчезают по сторонам drop.splashing = false; } else { outsideTest = (drop.p0.y > displayHeight); } if (outsideTest) { recycleDrop(drop); } }
//вызываем функцию перерисовкми drop.redraw();
drop = nextDrop; } }
public function recycleDrop(drop:LineRaindrop):void { numOnStage--; numInRecycleBin++;
if (drop.splashing) { //находим правильное положение для всплеска (интерполируем в обратном направлении по времени //чтобы найти, где капля пересечет уровень земли, при условии, что скорость постоянна) dropX = drop.p0.x + (displayHeight-drop.p0.y)*(drop.p1.x-drop.p0.x)/(drop.p1.y-drop.p0.y); createSplash(dropX, drop.color); }