
Эффект растворения

На самом деле ничего сложного там не было, эффект был сделан из анимированной маски, постепенно скрывающей персонажа и скрипта который добавлял частицы. Недостатков у этого способа было много. Во первых приходилось каждый раз для каждого персонажа создавать анимированную маску и вставлять скрипт в кадр, что было бы достаточно трудоемко и неэффективно, если бы персонажей было не 3, а намного больше. Во вторых частицы появлялись по всей ширине анимированной маски, т.е. частица могла появиться за пределами формы персонажа, что смотрелось не очень красиво. Поэтому я решил довести эффект до «ума» и написать класс, с помощью которого можно было бы без особых усилий создавать эффект растворения и который в будущем облегчил бы мне жизнь.
Пользоваться классом достаточно просто, создаем экземпляр класса в который передаем один обязательный параметр aTarget – это наш персонаж который будет растворяться, и четыре параметра по желанию
Animated — булевый параметр — true если МувиеКлип анимирова, иначе false
aSideCut — направление растворения (снизу вверх и сверху вниз)
aParticleSprite — linkage name мувика частицы
aParticlCount – количество частиц
AnimationDeley – продолжительность анимации растворения
aWind – скорость ветра
package
{
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.system.System;
/**
* ...
* @author evilMax
*/
public class DeathEffect extends Sprite
{
private var _mask:Sprite;
private var _particles:Array = [];
private var _particleSprite:Class;
private var _particleCount:int;
private var _heightSign:int;
private var _bitmapDataTarget:BitmapData
private var _bitmapTarget:Bitmap;
private var _target:DisplayObjectContainer;
private var _windX:Number;
private var _speedY:Number;
private var _step:Number;
private var _percent:Number;
private var _maxMaskHeight:Number;
private var _isDrawn:Boolean;
private var _animated:Boolean;
private var _completeAnimation:Boolean = false;
private var _cutSide:Boolean;
public function DeathEffect(aTarget:DisplayObjectContainer, Animated:Boolean = false,aSideCut:Boolean=true, aParticleSprite:Class = null, aParticlCount:int = 18, AnimationDeley:Number = 1.5, aWind:Number = 0):void
{
_target = aTarget;
_animated = Animated;
_particleSprite = aParticleSprite;
_particleCount = aParticlCount;
_windX = aWind;
_speedY = 3;
_percent = 100;
_isDrawn = false;
_step = _percent / (AnimationDeley * 30);
_cutSide = aSideCut;
setHeightSign();
addEventListener(Event.ENTER_FRAME, update);
addEventListener(Event.REMOVED_FROM_STAGE, destruct);
}
private function drawDefParticle():MovieClip
{
var particle:MovieClip = new MovieClip();
particle.graphics.beginFill(0x00FF44);
particle.graphics.moveTo( -5, -6);
particle.graphics.lineTo(2, -4);
particle.graphics.lineTo(6, 1);
particle.graphics.lineTo(6, 6);
particle.graphics.lineTo(0, 4);
particle.graphics.lineTo( -4, 1);
particle.graphics.lineTo( -5, -6);
return particle;
}
private function copyTarget(aTarget:DisplayObjectContainer):void
{
var r:Rectangle=aTarget.getBounds(aTarget);
var m:Matrix=new Matrix();
m.identity();
m.translate(-r.x,-r.y);
m.scale(aTarget.scaleX,aTarget.scaleY);
_bitmapDataTarget = new BitmapData(aTarget.width, aTarget.height, true, 0x000000);
_bitmapDataTarget.draw(aTarget,m);
_bitmapTarget = new Bitmap(_bitmapDataTarget);
_bitmapTarget.x = aTarget.x+r.x;
_bitmapTarget.y = aTarget.y+r.y;
aTarget.parent.addChild(_bitmapTarget);
aTarget.visible = false;
}
private function drawMask(aTarget:DisplayObjectContainer,aWidth:Number, aHeight:Number):void
{
var r:Rectangle=aTarget.getBounds(aTarget);
_mask = new Sprite();
_mask.graphics.lineStyle(1);
_mask.graphics.beginFill(0x00FF44);
_mask.graphics.drawRect(0, 0, aWidth, aHeight);
_mask.x = aTarget.x + r.x;
_mask.y = aTarget.y + r.y;
aTarget.parent.addChild(_mask);
}
private function addParticle():void
{
if (_completeAnimation)
return;
for (var i:int=0;i<_particleCount;i++)
{
var segments:Array = new Array();
var geted:Boolean = false;
for (var j:int=_bitmapDataTarget.width;j>=0;j--)
{
var AlphaValue:Number = _bitmapDataTarget.getPixel32(j-1, getYPos())>>24&0xFF;
if (AlphaValue!= 0 && !geted)
{
var obj:Object = new Object();
obj.sP = j;
geted = true;
}
else if (AlphaValue == 0 && geted)
{
obj.fP = j;
geted = false;
segments.push(obj);
}
}
if (segments.length == 0)
return;
var rnd:int = int(Math.random() * segments.length);
var pixelXPos:Number = segments[rnd].sP - Math.random() * Math.abs(segments[rnd].fP - segments[rnd].sP);
var pixelYPos:Number = getYPos();
var color:ColorTransform = new ColorTransform();
var pixelColor:uint = _bitmapDataTarget.getPixel32(pixelXPos, pixelYPos);
var speedY:Number = Math.random() * _speedY + _speedY/2;
color.color = pixelColor;
var particle:MovieClip;
if (_particleSprite == null)
particle = drawDefParticle();
else
particle = new _particleSprite();
_target.parent.addChild(particle);
particle.x = _mask.x + (pixelXPos);
particle.y = _mask.y +3;
if (!_cutSide)
particle.y = _mask.y +_mask.height;
particle.transform.colorTransform = color;
particle.rotation = Math.random() * 180;
particle.scaleX = particle.scaleY = Math.random() * .5 + .5;
_particles.push( { t:(Math.random() * 5), mc:particle, speed:speedY} );
}
}
private function update(e:Event=null):void
{
if (_animated && !_completeAnimation)
{
clearing();
drawMask(_target, _target.width, _target.height);
copyTarget(_target);
_bitmapTarget.mask = _mask;
_maxMaskHeight = _mask.height;
}
else if(!_animated && !_isDrawn)
{
drawMask(_target, _target.width, _target.height);
copyTarget(_target);
_bitmapTarget.mask = _mask;
_maxMaskHeight = _mask.height;
_isDrawn = true;
}
cuttingMask();
addParticle();
for (var i:int=0;i<_particles.length;i++)
{
var item:Object = _particles[i];
item.t+=.19;
item.mc.x+=Math.sin(item.t)*1.2+_windX;
item.mc.y-=item.speed;
item.mc.alpha-=.02;
item.mc.scaleX = item.mc.scaleY -= .02;
if(item.mc.alpha<=0)
{
_particles[i].mc.parent.removeChild(item.mc);
_particles[i]=null;
_particles.splice(i,1);
i--;
}
}
if (_particles.length == 0 && _completeAnimation)
destruct();
}
private function cuttingMask():void
{
var value:Number;
_percent -= _step;
if (_percent > 0)
{
if(_animated)
value = _maxMaskHeight - _maxMaskHeight * _percent / 100;
else
value = _maxMaskHeight * _step / 100;
_mask.height -= value;
_mask.y += value*_heightSign;
}
if (_percent <= 0)
{
_completeAnimation = true;
_mask.height = 0;
}
}
private function setHeightSign():void
{
_heightSign = 0;
if (_cutSide)
_heightSign = 1;
}
private function getYPos():Number
{
var yPos:Number=_mask.height;
if(_cutSide)
yPos = _target.height - _mask.height;
return yPos;
}
private function clearing():void
{
if (_mask != null)
{
_mask.parent.removeChild(_mask);
_mask = null;
}
if (_bitmapTarget != null)
{
_bitmapTarget.parent.removeChild(_bitmapTarget);
_bitmapTarget = null;
}
}
private function destruct(e:Event=null):void
{
removeEventListener(Event.REMOVED_FROM_STAGE, destruct);
removeEventListener(Event.ENTER_FRAME, update);
for (var i:int=0;i<_particles.length;i++)
{
_particles[i].parent.removeChild(_particles[i]);
_particles[i] = null;
_particles.splice(i, 1);
i--;
}
clearing();
//System.gc();
}
}
}
Исходник
Ну и результат:
UPD:
Как и обещал, доработал эффект. Теперь его можно применять и к анимированным МувиеКлипам и добавлена возможность выбора направления растворения — сверху вниз или снизу вверх.
К сожалению погонять класс в боевых условиях не было времени, а посему вполне возможны баги и недоработки. Да, и от ваших предложений по улучшению кода не откажусь)
Результат
Исходник
- +33
- EvilMax
Комментарии (13)