AnimationManager - мой взгляд на замену MovieClip
Запись от Aloran размещена 21.03.2011 в 01:34
Вот я и решился на первый свой бложек =) Заранее прошу за некоторую косноязычность в написании - я исправлюсь со временем.
В своей работе я стараюсь как можно реже открывать Flash CSx... Ну не лежит у меня душа к сему творенью. По этой причине я всячески стараюсь все делать программно.
Одним из желаний было перевести анимацию на программные рельсы. И вот как я это делаю:
Класс AnimationManager
задачи:
- Получать на вход картинку в виде набора тайлов, "разрезать" ее на отдельные BitmapData и хранить их для последующего использования
- по запросу из вне возвращать AnimationBitmap (класс в отображающий анимацию)
- реагировать на события от AnimationBitmap, по сути управлять им.
package al.managers { import al.display.AnimationBitmap; import al.event.AnimationEvent; import al.utils.ObjectPool; import flash.display.BitmapData; import flash.events.TimerEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.Timer; /** * ... * @author Stepan Ermilov */ public class AnimationManager { private static const BASE_POINT:Point = new Point(); private var _frames:Object; //Array of Bitmaps private var _bitmaps:Array; //AnimationBitmap private var _timers:Array; private var _pool:ObjectPool; public function AnimationManager() { _frames = { }; _bitmaps = []; _timers = []; _pool = new ObjectPool(new AnimationBitmap()); } public function free():void { removeAllAnimationFrames(); _frames = null; removeAllAnimationBitmap(); _bitmaps = null; _timers = null; _pool.free(); _pool = null; } public function addAnimationFrames(name:String, sourceBitmapData:BitmapData, animRect:Rectangle):Boolean { var result:Boolean; var frames:Array = createFrames(sourceBitmapData, animRect); if (frames.length) { _frames[name] = frames; result = true; } return result; } public function removeAnimationFrames(name:String):void { _frames[name] = null; } public function removeAllAnimationFrames():void { for (var name:String in _frames) { removeAnimationFrames(name); } } public function addAnimationBitmap(name:String, startIndex:int = 0, finishIndex:int = 0):AnimationBitmap { var bitmap:AnimationBitmap; if (_frames[name]) { bitmap = _pool.getItem() as AnimationBitmap; bitmap.animationType = name; bitmap.startIndex = startIndex ? startIndex : 0; bitmap.finishIndex = finishIndex ? finishIndex : (_frames[name] as Array).length - 1; bitmap.frameIndex = bitmap.startIndex; bitmap.bitmapData = (_frames[name] as Array)[bitmap.frameIndex] as BitmapData; addBitmapListeners(bitmap); _bitmaps.push(bitmap); var timer:Timer = new Timer(0, 1); timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete); _timers.push(timer); } return bitmap; } public function removeAnimationBitmap(bitmap:AnimationBitmap):void { var index:int = _bitmaps.indexOf(bitmap); if (index > -1) { removeBitmapListeners(bitmap); bitmap.clear(); bitmap = null; var timer:Timer = _timers[index] as Timer; timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete); timer.stop(); timer = null; } } public function removeAllAnimationBitmap():void { for (var i:int = 0; i < _bitmaps.length; i++) { removeAnimationBitmap(_bitmaps[i] as AnimationBitmap); } } private function createFrames(sourceBitmapData:BitmapData, animRect:Rectangle):Array { var numCol:int = int(sourceBitmapData.width / animRect.width); var numRow:int = int(sourceBitmapData.height / animRect.height); var numFrames:int = numCol * numRow; var result:Array = new Array(numFrames); var index:int; if (numFrames) { var item:BitmapData; for (var i:int = 0; i < numRow; i++) { for (var j:int = 0; j < numCol; j++) { animRect.x = j * animRect.width; animRect.y = i * animRect.height; item = new BitmapData(animRect.width, animRect.height); item.copyPixels(sourceBitmapData, animRect, BASE_POINT); index = j + i * numCol; result[index] = item; } } } return result; } private function addBitmapListeners(bitmap:AnimationBitmap):void { bitmap.addEventListener(AnimationEvent.ANIMATION_START, onBitmapEvent); bitmap.addEventListener(AnimationEvent.ANIMATION_STOP, onBitmapEvent); bitmap.addEventListener(AnimationEvent.ANIMATION_RESUME, onBitmapEvent); } private function removeBitmapListeners(bitmap:AnimationBitmap):void { bitmap.removeEventListener(AnimationEvent.ANIMATION_START, onBitmapEvent); bitmap.removeEventListener(AnimationEvent.ANIMATION_STOP, onBitmapEvent); bitmap.removeEventListener(AnimationEvent.ANIMATION_RESUME, onBitmapEvent); } private function onBitmapEvent(event:AnimationEvent):void { var bitmap:AnimationBitmap = event.target as AnimationBitmap; var index:int = _bitmaps.indexOf(bitmap); var timer:Timer = _timers[index] as Timer; switch (event.type) { case AnimationEvent.ANIMATION_START: bitmap.frameIndex = bitmap.startIndex; bitmap.isPlayed = true; setFrame(bitmap, timer); break; case AnimationEvent.ANIMATION_STOP: timer.stop(); bitmap.isPlayed = false; break; case AnimationEvent.ANIMATION_RESUME: timer.start(); bitmap.isPlayed = true; break; } } private function setFrame(bitmap:AnimationBitmap, timer:Timer):void { bitmap.bitmapData = (_frames[bitmap.animationType] as Array)[bitmap.frameIndex] as BitmapData; timer.delay = bitmap.duration; if (bitmap.isPlayed) timer.start(); } private function onTimerComplete(event:TimerEvent):void { var timer:Timer = event.target as Timer; var index:int = _timers.indexOf(timer); var bitmap:AnimationBitmap = _bitmaps[index] as AnimationBitmap; if (bitmap.frameIndex == bitmap.finishIndex) { if (bitmap.isLooped) bitmap.frameIndex = bitmap.startIndex; else bitmap.isPlayed = false; } else { bitmap.frameIndex++; } if (bitmap.isPlayed) setFrame(bitmap, timer); } } }
Класс AnimationBitmap
задачи:
- Отображать анимацию
package al.display { import al.event.AnimationEvent; import al.utils.IPoolObject; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; /** * ... * @author Stepan Ermilov */ public class AnimationBitmap extends Bitmap implements IPoolObject, IDisplayObject { private var _durations:Array; private var _isLooped:Boolean; private var _animationType:String; private var _frameIndex:int; private var _startIndex:int; private var _finishIndex:int; private var _isPlayed:Boolean; public function AnimationBitmap() { super(); _durations = []; } public function free():void { bitmapData = null; } public function copy():IPoolObject { return new AnimationBitmap(); } public function clear():void { bitmapData = null; _durations = []; isPlayed = false; dispatchEvent(new Event(Event.CLEAR)); } public function start():void { dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_START)); } public function stop():void { dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_STOP)); } public function resume():void { dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_RESUME)); } public function get animationType():String { return _animationType; } public function set animationType(value:String):void { if (_animationType != value) _animationType = value; } public function get frameIndex():int { return _frameIndex; } public function set frameIndex(value:int):void { if (_frameIndex != value) _frameIndex = value; } public function get startIndex():int { return _startIndex; } public function set startIndex(value:int):void { if (_startIndex != value) _startIndex = value; } public function get finishIndex():int { return _finishIndex; } public function set finishIndex(value:int):void { if (_finishIndex != value) _finishIndex = value; } public function get isLooped():Boolean { return _isLooped; } public function set isLooped(value:Boolean):void { if (_isLooped != value) _isLooped = value; } override public function set bitmapData(value:BitmapData):void { super.bitmapData = value; if (value) dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_UPDATE)); } public function get isPlayed():Boolean { return _isPlayed; } public function set isPlayed(value:Boolean):void { if (_isPlayed != value) _isPlayed = value; } public function get durations():Array { return _durations; } public function set durations(value:Array):void { if (_durations != value) _durations = value; } public function get duration():Number { var index:int = frameIndex - startIndex; return durations[index] ? durations[index] : durations[0]; } } }
Main.as
package { import al.display.AnimationBitmap; import al.managers.AnimationManager; import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; import flash.geom.Rectangle; import flash.text.TextField; /** * ... * @author Stepan Ermilov */ public class Main extends Sprite { [Embed(source = '../lib/dcbb4e3fecdc.png')] private static const image:Class; private var _tfText:TextField; private var _animationManager:AnimationManager; private var _animation:AnimationBitmap; public function Main():void { if (stage) onAddedToStage(); else addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); } private function onAddedToStage(event:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage); var bitmap:Bitmap = new image(); _animationManager = new AnimationManager(); _animationManager.addAnimationFrames("horse", bitmap.bitmapData, new Rectangle(0, 0, 80, 80)); _animation = _animationManager.addAnimationBitmap("horse", 4, 7); //в анимации используем только кадры с 4 по 7 _animation.durations = [100]; // длительность для всех кадров одинакова _animation.isLooped = true; // анимация циклична addChild(_animation); _animation.start(); } } }
Первое что интересует это, где держать таймер в менеджере, как сейчас, или все таки он должен быть в AnimationBitmap?
Второе это необходимость в самом классе AnimationBitmap. Есть мысли юзать просто Bitmap, а все переменные и операции старт/стоп вынести в менеджер?
Вот в принципе и все. Хотелось бы услышать ваше мнение о правильности такого подхода, наверняка я что то делаю не так. =)
Образец картинки с тайлами:
[IMG]http://s006.***********/i213/1102/f0/dcbb4e3fecdc.png[/IMG]
Демо-флешку и код можно посмотреть тут git@github.com:stepan23/aloran.git
Всего комментариев 14
Комментарии
21.03.2011 10:14 | |
Так то-да. Но художники-аниматоры не оценят.
|
21.03.2011 10:57 | |
Обновил(-а) scarbo 21.03.2011 в 11:08
|
21.03.2011 11:54 | |
Ну, это скорее исключение.
Обычно аниматоры, анимирующие в Flash IDE ратуют за мувиклипы) |
21.03.2011 12:09 | |
По образцу картинки не скажешь, что анимацию делали во флеше.
Думаю, таких не мало. |
21.03.2011 12:31 | |
Цитата:
А чем зануление объекта хуже создания пустого?
|
21.03.2011 17:01 | |
Зачем вообще нарезать на отдельные битмапы? Чем не угодил scrollRect или копирование пикселей "на лету"?
ИМХО AnimationManager вообще не нужен. |
|
Обновил(-а) alatar 21.03.2011 в 17:04
|
21.03.2011 22:44 | |
Это же определённого рода ущербность - работать только с растровыми изображениями. Тем более во флэш.
|
22.03.2011 00:40 | |
22.03.2011 00:54 | |
Кстати, старенькая статья, но кое-что расскажет.
|
22.03.2011 23:26 | |
Спасибо большое за ссылочку. Очень интересное чтиво.
|
23.03.2011 00:50 | |
я использовал вот это:
http://blog.hexagonstar.com/animatedbitmapclass/ |
Последние записи от Aloran
- AnimationManager - мой взгляд на замену MovieClip (21.03.2011)