Всем, кто имеет хоть какое– нибудь отношение к игроиндустрии или созданию спецэффектов, хорошо известно, насколько ресурсоемка такая вещь, как отображение векторной анимации и ее расчет в процессе рендеринга. Но к счастью, разработчики оставили нам путь к всеобщему процветанию и дали нам возможность использовать такую великолепную вещь, как вывод на экран предварительно откешированного в растр изображения. Помните, как устроен кинематограф, основанный на смене одной картинки на другую? Вот этим мы сейчас и займемся.
Для начала нам нужно получить кэш мувклипа.
package cache2bmp
{
import ru.dijump.wonf.cache2bmp.*;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.geom.Matrix;
import flash.geom.Rectangle;
public class CacheAsBitmap
{
private var _sourceMovieClip:MovieClip;
public function CacheAsBitmap():void
{
this._sourceMovieClip = new MovieClip();
}
public function cache(source:MovieClip):Vector.<CachedFrameModel>
{
//конечный материал будет состоять из массива простых объектов - кадров с
//необходимой информацией об выдираемом кадре
//и его "оттиском" в виде BitmapData
var clip:Vector.<CachedFrameModel> = new Vector.<CachedFrameModel>();
this.sourceMovieClip = source;
var totalFrames:int = sourceMovieClip.totalFrames;
//перебираем все кадры и кэшим их в наш массив
for (var i:int = 1; i <= totalFrames; i++)
{
sourceMovieClip.gotoAndStop(i);
clip.push(cacheFrame(sourceMovieClip));
}
return clip;
}
private function cacheFrame(sourceMovieClip:MovieClip):CachedFrameModel
{
//сохраняем всю информацию о кадре
var cachedFrame:CachedFrameModel = new CachedFrameModel();
cachedFrame.width = sourceMovieClip.width;
cachedFrame.height = sourceMovieClip.height;
cachedFrame.x = sourceMovieClip.x;
cachedFrame.y = sourceMovieClip.y;
cachedFrame.numFrame = sourceMovieClip.currentFrame;
//получаем прямоугольник, описанный вокруг изображения, с его размерами и
координатами в координатном пространстве
//нашего мувклипа:
var bounds:Rectangle = sourceMovieClip.getBounds(sourceMovieClip);
//матрица геометрических трансформаций - отдельная тема для разговора, о ней
//можно почитать в эдабовских справочниках.
//в данном случае она нам нужна, чтобы переместить отпечаток изображения в
//систему координат с началом в верхнем левом углу полученного
// прямоугольника:
var matrix:Matrix = new Matrix();
matrix.translate(-bounds.x, -bounds.y);
//Оччень тонкий и мутный момент. Если мы хотим чтобы пустые пространства были
//прозрачными, заливка битмапдаты
// ДОЛЖНА быть черной. Простая установка свойства transparent=true без этого
//не сработает.
var bitmapData:BitmapData = new BitmapData(bounds.width, bounds.height,true,0x000000);
bitmapData.draw(sourceMovieClip, matrix);
cachedFrame.bitmapData = bitmapData;
return cachedFrame;
}
public function get sourceMovieClip():MovieClip
{
return _sourceMovieClip;
}
public function set sourceMovieClip(value:MovieClip):void
{
_sourceMovieClip = value;
}
}
}
Вот как выглядит объект кадра:
package cache2bmp
{
import flash.display.BitmapData;
public class CachedFrameModel
{
private var _bitmapData:BitmapData;
private var _x:int;
private var _y:int;
private var _width:int;
private var _height:int;
private var _numFrame:int;
private var _action:Function;
private var _layer:String;
…
Гэттеры и сеттеры описывать не буду, ленивым можно сделать свойства публичными
Далее на очереди эмулятор мувклипа – проигрыватель нашей скэшированной анимации. Сами эдабовцы уверяют, что наибыстрейший способ вывода на экран – это свойство copyPixels() битмапдаты, и у нас нет оснований им не верить Вот что у нас должно получится (класс реализует необходимые для клипа органы управления, присущие MovieClip, которые я комментировать не буду – они элементарны.
package cache2bmp
{
import ru.dijump.wonf.cache2bmp.*;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
public class CachedMovieClip extends Bitmap
{
private var _clip:Vector.<CachedFrameModel>;
private var _currentFrame:int;
private var _totalFrame:int;
private var _destinationPoint:Point;
private var _areaRectangle:Rectangle;
private var _clearRectangle:Rectangle;
public function CachedMovieClip(width:int = 150, height:int = 150)
{
bitmapData = new BitmapData(width, height, true,0x000000);
this.width = width;
this.height = height;
this._clip = new Vector.<CachedFrameModel>();
this._destinationPoint = new Point();
this._areaRectangle = new Rectangle();
this._clearRectangle = new Rectangle(0, 0, width, height);
this.currentFrame=1;
}
public function gotoAndStop(frame:int = 1):void
{
if (frame < 1)
{
frame = 1;
}
if (frame > totalFrame)
{
frame = totalFrame;
}
render(frame);
this.currentFrame = frame;
this.removeEventListener(Event.ENTER_FRAME, playEnterFrameHandler);
}
private function render(frame:int):void
{
// считываем данные с нашего объекта-снимка
destinationPoint.x = clip[frame - 1].x;
destinationPoint.y = clip[frame - 1].y;
areaRectangle.width = clip[frame - 1].width;
areaRectangle.height = clip[frame - 1].height;
//Очень тонкий момент! Чтобы плеер не рендерил лишнюю муть, навроде очистки битмапдаты, лочим ее:
bitmapData.lock();
//Очищаем битмапдату от предыдущего изображения черной заливкой (это единственно верный путь):
this.bitmapData.fillRect(_clearRectangle,0x000000);
//рисуем новую картинку
this.bitmapData.copyPixels(clip[frame - 1].bitmapData, areaRectangle, destinationPoint);
//и позволяем вывести теперь ее на экран
bitmapData.unlock();
}
public function gotoAndPlay(frame:int = 1):void
{
if (frame < 1)
{
frame = 1;
}
if (frame > totalFrame)
{
frame = totalFrame;
}
render(frame);
this.currentFrame = frame;
this.addEventListener(Event.ENTER_FRAME, playEnterFrameHandler);
}
public function play():void
{
this.addEventListener(Event.ENTER_FRAME, playEnterFrameHandler);
}
public function stop():void
{
this.removeEventListener(Event.ENTER_FRAME, playEnterFrameHandler);
}
public function get clip():Vector.<CachedFrameModel>
{
return _clip;
}
public function set clip(value:Vector.<CachedFrameModel>):void
{
_clip = value;
this.totalFrame = value.length;
this.currentFrame = 1;
gotoAndStop(1);
}
public function get currentFrame():int
{
return _currentFrame;
}
public function set currentFrame(value:int):void
{
_currentFrame = value;
}
public function get totalFrame():int
{
return _totalFrame;
}
public function set totalFrame(value:int):void
{
_totalFrame = value;
}
public function get destinationPoint():Point
{
return _destinationPoint;
}
public function set destinationPoint(value:Point):void
{
_destinationPoint = value;
}
public function get areaRectangle():Rectangle
{
return _areaRectangle;
}
public function set areaRectangle(value:Rectangle):void
{
_areaRectangle = value;
}
private function playEnterFrameHandler(event:Event):void
{
if (currentFrame != totalFrame)
{
render(currentFrame);
currentFrame++;
}
else
{
render(1);
currentFrame = 1;
}
}
}
}
Вот, собственно, и все!