Пакет для растрирования анимаций
Когда писал про текстуропакер в комментариях кто-то просил выложить. Ну я его вот немного подправил, и выкладываю.
UPD: Обнаружился первоисточник, имя которого я уже давно потерял, так как от него по большому счету осталась только идея. Но всё же отдам дань, первоначально(почти год назад) это был пакет от touchmypixel. Потом раза четыре переписывался. Так-то.
1. Рисуем анимацию.
2. Скармливаем имя класса анимации этому фреймворчику
3. получаем анимацию построенную на битмапах.
Бонусы:
+ производительность по сравнению с проигрыванием обычных анимаций раза этак в три выше. (Тесты делал давно, повторять неохота, кто не верит пусть сам проверяет )
+ пакетное кеширование из XML списка.
+ при кешировании сразу указывается скейл с которым нужно закешировать. Бонус при работе с вектором очевиден - можно не таскать за собой анимацию партикля размером 100х100 если нам нужен 20х20. В библиотеке он будет сразу 20х20. Но и минус в том что анимации идентифицируются только по имени, так что если вам одна и та же анимация нужна с разными скейлами - можете приложить усилия и прикрутить что-то свое для этого дела.
Минусы:
- реализован свой класс Animation() - Т.е. вместо мувиклипа придется юзать его. Все необходимые функции мувиклипа передраны так что неудобств возникнуть не должно, но всё-таки хотелось бы общаться с чем-то более стандартным. Оч долго собирался сделать что-то типа extend MovieClip со всеми вытекающими, но руки так и не дошли. На текущий момент меня устраивает Animation
Пример использования:
var animationCache:AnimationCache = AnimationCache.getInstance(); animationCache.addToBulkCache(XML(new animationList())); //***************************************** var anim:Animation = animationCache.getAnimation("animName"); anim.addEventListener(Event.COMPLETE, onDied); this.addChild(anim); anim.gotoAndPlayRandomFrame();
Сама фабрика:
package com.earwig.animationcache { import flash.events.EventDispatcher; import flash.utils.setTimeout; /** * Класс кеширует векторные анимации в растр. */ public class AnimationCache extends EventDispatcher { public var replaceExisting:Boolean = false; private var animations:Object = {}; private static var instance:AnimationCache; //===================================================================== // CONSTRUCTOR //===================================================================== public function AnimationCache() { if(instance) throw(new Error("AnimationCache is a Singleton. Dont Instantiate!")); instance = this; } //===================================================================== // PUBLIC //===================================================================== /** * Выдает инстанс библиотеки * @return */ public static function getInstance():AnimationCache { return instance ? instance : new AnimationCache(); } /** * Кеширует анимацию с заданным именем класса мувика и скейлом * (растровые тайлы будут уже указанного размера) * @param id * @param scaleX * @param scaleY * @return */ public function cacheAnimation(id:String, scaleX:Number, scaleY:Number):Animation { // TODO придумать что-то чтобы можно было кешировать одну и ту же анимацию с разными скейлами var animation:Animation if (!animations[id] || replaceExisting) { animation = new Animation(); animation.buildCacheFromLibrary(id, scaleX, scaleY); animations[id] = animation; } else { animation = animations[id] } return animation; } /** * Выдает копию анимации из библиотеки с указанным именем * @param id * @return */ public function getAnimation(id:String):Animation { if (!animations[id]) { trace("MISSING ANIMATION :"+ id); return null; } var animation:Animation = new Animation(); animation.frames = animations[id].frames; animation.clip = animations[id].clip; animation.gotoAndPlay(1); return animation; } /** * Кеширует список анимаций пакетом * @param list список анимаций в формате XML. * <data> * <anim name="className" scaleX="0.5" scaleY="0.5" /> * </data> */ public function addToBulkCache(list:XML):void { for each(var anim:XML in list.anim) { cacheAnimation(anim.@name, parseFloat(anim.@scaleX) || 1, parseFloat(anim.@scaleY) || 1); //TODO добавить диспатчи событий о прогрессе при групповой загрузке } } } }
package com.earwig.animationcache { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.Event; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.utils.getDefinitionByName; public class Animation extends Sprite { private var _bitmap:Bitmap; private var _clip:MovieClip; private var _frames:Vector.<AnimationFrame>; private var _currentFrame:Number = 1; private var _playing:Boolean = false; private var _id:String; private var _scaleX:Number; private var _scaleY:Number; //===================================================================== // CONSTRUCTOR //===================================================================== public function Animation() { _bitmap = new Bitmap(null, "auto", true); _bitmap.smoothing = true; addChild(_bitmap); _frames = new Vector.<AnimationFrame>(); } //===================================================================== // CONSTRUCTOR //===================================================================== /** * Делает анимаци из класса загруженной флешки * @param params */ public function buildCacheFromLibrary(name:String = "", scaleX:Number = 1, scaleY:Number = 1):void { _id = name; _scaleX = scaleX; _scaleY = scaleY; _clip = new (getDefinitionByName(_id))(); _clip.gotoAndStop(1) var r:Rectangle; for (var i:int = 0; i < _clip.totalFrames; i++) { r = _clip.getBounds(_clip); var bitmapData:BitmapData = new BitmapData(r.width, r.height, true, 0x000000); var m:Matrix = new Matrix(); m.scale(_scaleX, _scaleY); m.translate(-r.x, -r.y); bitmapData.draw(_clip, m, null, null, null, true); _frames.push(new AnimationFrame(bitmapData, r.x, r.y)); _clip.nextFrame(); makeAllChildrenNextFrame(_clip); } } /** * Проигрывает анимацию циклически */ public function play():void { _playing = true; addEventListener(Event.ENTER_FRAME, enterFrame, false, 0, true); } /** * Стоп анимации */ public function stop():void { _playing = false; removeEventListener(Event.ENTER_FRAME, enterFrame) } /** * Переход к кадру и остановка * @param frame */ public function gotoAndStop(frame:int):void { goto(frame); stop(); } /** * Переход к кадру и проигрывание * @param frame */ public function gotoAndPlay(frame:int):void { goto(frame); play(); } /** * Переход к случайному кадру и проигрывание */ public function gotoAndPlayRandomFrame():void { gotoAndPlay(int(Math.random() * totalFrames)); } /** * Переход на след кадр * @param useSpeed */ public function nextFrame():void { goto(_currentFrame + 1); } /** * Переход на предыдущий кадр */ public function prevFrame():void { goto(_currentFrame - 1); } /** * Перезаполняет содержимое кеша заново. */ public function update():void { stop(); _frames = new Vector.<AnimationFrame>(); buildCacheFromLibrary(); } //===================================================================== // PRIVATE //===================================================================== /** * Переход к кадру * @param frame */ private function goto(num:int):void { if (num > totalFrames) num = num % totalFrames; //num - (totalFrames * int(num / totalFrames)); if (!num) num = totalFrames; _currentFrame = num; var frame:AnimationFrame = _frames[_currentFrame - 1]; _bitmap.bitmapData = frame.bitmapData; _bitmap.smoothing = true; _bitmap.x = frame.x; _bitmap.y = frame.y; } /** * Проходит по всем детям и прокручивает на кадр с указанным номером, * нужно в процессе кеширования. Утилитная функция. * @param m * @param f */ private function makeAllChildrenNextFrame(m:MovieClip):void { for (var i:int = 0; i < m.numChildren; i++) { var c:* = m.getChildAt(i); if (c is MovieClip) { makeAllChildrenNextFrame(c); c.nextFrame(); } } } //===================================================================== // HANDLERS //===================================================================== /** * Без комментариев (; * @param e */ private function enterFrame(e:Event = null):void { nextFrame(); if (_currentFrame >= totalFrames) dispatchEvent(new Event(Event.COMPLETE)) } //===================================================================== // ACCESSORS //===================================================================== /** Проигрывается ли флешка в данный момент */ public function get playing():Boolean { return _playing; } /** Полное кол-во кадров */ public function get totalFrames():int { return _frames.length; } /** Текущий кадр */ public function get currentFrame():int { return _currentFrame; } /** Масив(вектор) фреймов, нужен для клонирования */ internal function get frames():Vector.<AnimationFrame> { return _frames; } internal function set frames(value:Vector.<AnimationFrame>):void { _frames = value; } /** исходный мувиклип, нужен для клонирования */ internal function get clip():MovieClip { return _clip; } internal function set clip(value:MovieClip):void { _clip = value; } } }
package com.earwig.animationcache { import flash.display.BitmapData; /** * ... * @author Dukobpa3 */ public class AnimationFrame { private var _bitmapData:BitmapData; private var _x:int; private var _y:int; public function AnimationFrame(bmpd:BitmapData, x:int,y:int) { _bitmapData = bmpd; _x = x; _y = y; } internal function get bitmapData():BitmapData { return _bitmapData; } internal function get x():int { return _x; } internal function get y():int { return _y; } } }
Всего комментариев 22
Комментарии
09.04.2012 14:20 | |
А формат xml нам по коду додумывать?
|
09.04.2012 14:37 | |
09.04.2012 14:40 | |
Ага, нашел, первый класс не прочитал.
|
10.04.2012 22:27 | |
Антон Каролов пару дней назад выложил свою версию аналогичного скрипта
http://www.ant-karlov.ru/iz-vektora-....html#item4427 |
10.04.2012 22:41 | |
Лол.
Он это сделал из того же примера что и я с год назад)) Только мы разными путями развития пошли, у него остались косяки из исходного варианта, например вот это: Цитата:
Чтобы такого не получалось, вы можете задавать размер всех кадров вручную. Для этого достаточно перетащить какой-нибудь любой клип, растянуть его, так как вам нужно, и указать ему имя "e_bounds"
|
|
Обновил(-а) Dukobpa3 10.04.2012 в 22:51
|
13.04.2012 19:06 | |
16.04.2012 17:46 | |
Имхо всё верно как раз.
Сначала отскейлили потом переместили. А не наоборот. |
16.04.2012 22:21 | |
Примерно полгода назад делал свой велосипед тоже на основе touchmypixel. И на хабре была статья - чувак сделал свой велосипед, вдохновляясь тем же. Популярный у них продуктик вышел
|
17.04.2012 11:10 | |
Цитата:
Имхо всё верно как раз.
Сначала отскейлили потом переместили. А не наоборот. |
22.04.2012 19:36 | |
Наконец-то попробовал использовать этот класс у себя. Добавил возможность переходов по метке а не только номеру кадра, чтение скриптов анимации (gotoAndStop и т.д.). Но сразу же столкнулся с одной проблемой. Если в анимации вложенные клипы находятся не на целых координатах, то при растрировании анимация выходит дерганная. Вот пример: http://minus.com/mfWKAHzBw/
Товарищи, очень важный вопрос: ЧТО МОЖНО С ЭТИМ СДЕЛАТЬ? Переделывать 1000 кадров анимации наново под "целые пиксели" - это не выход. Во-первых - долго, во-вторых - не добьюсь нужного эффекта в анимации. Подскажите пожалуйста. |
|
Обновил(-а) HardCoder 22.04.2012 в 19:40
|
22.04.2012 19:44 | |
Я думаю проблема в скейлах. Если проканает такой вариант то попробуй сделать вектор сразу нужного размера.
|
23.04.2012 18:16 | |
HardCoder - можешь мне скинуть одну такую анимацию. Хочу посмотреть, может придумаю что-то. А сам не сталкивался с такой проблемой, потом видимо и не отловил.
|
06.06.2012 17:53 | |
Последние записи от Dukobpa3
- Strategy (Стратегия) (27.12.2013)
- State (Состояние) (27.12.2013)
- State-machine (конечный автомат, машина состояний) (25.12.2013)
- Медиатор, Прокси (14.11.2013)
- Инкапсуляция объекта vs инкапсуляция поведения (14.11.2013)