Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Dukobpa3

Рейтинг: 5.00. Голосов: 2.

Пакет для растрирования анимаций

Запись от Dukobpa3 размещена 07.04.2012 в 15:00
Обновил(-а) Dukobpa3 10.04.2012 в 22:49

Когда писал про текстуропакер в комментариях кто-то просил выложить. Ну я его вот немного подправил, и выкладываю.

UPD: Обнаружился первоисточник, имя которого я уже давно потерял, так как от него по большому счету осталась только идея. Но всё же отдам дань, первоначально(почти год назад) это был пакет от touchmypixel. Потом раза четыре переписывался. Так-то.

1. Рисуем анимацию.
2. Скармливаем имя класса анимации этому фреймворчику
3. получаем анимацию построенную на битмапах.

Бонусы:
+ производительность по сравнению с проигрыванием обычных анимаций раза этак в три выше. (Тесты делал давно, повторять неохота, кто не верит пусть сам проверяет )
+ пакетное кеширование из XML списка.
+ при кешировании сразу указывается скейл с которым нужно закешировать. Бонус при работе с вектором очевиден - можно не таскать за собой анимацию партикля размером 100х100 если нам нужен 20х20. В библиотеке он будет сразу 20х20. Но и минус в том что анимации идентифицируются только по имени, так что если вам одна и та же анимация нужна с разными скейлами - можете приложить усилия и прикрутить что-то свое для этого дела.

Минусы:
- реализован свой класс Animation() - Т.е. вместо мувиклипа придется юзать его. Все необходимые функции мувиклипа передраны так что неудобств возникнуть не должно, но всё-таки хотелось бы общаться с чем-то более стандартным. Оч долго собирался сделать что-то типа extend MovieClip со всеми вытекающими, но руки так и не дошли. На текущий момент меня устраивает Animation

Пример использования:
Код AS3:
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();
Ниже собственно классы

Сама фабрика:
Код AS3:
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 добавить диспатчи событий о прогрессе при групповой загрузке
			}
		}
	}
}
Класс анимации:
Код AS3:
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; }
 
 
	}
}
Утилитный классец одного фрейма:
Код AS3:
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 Dima_DPE вне форума
Dima_DPE
А формат xml нам по коду додумывать?
Старый 09.04.2012 14:37 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
Достаточно читать комментарии.

Код AS3:
/**
 * Кеширует список анимаций пакетом
 * @param	list список анимаций в формате XML. 
 * 	<data>
 * 		<anim name="className" scaleX="0.5" scaleY="0.5" />
 * 	</data>
 */
Код AS3:
<data>
	<anim name="className" scaleX="0.5" scaleY="0.5" />
</data>
Старый 09.04.2012 14:40 Dima_DPE вне форума
Dima_DPE
Ага, нашел, первый класс не прочитал.
Старый 09.04.2012 15:45 Dima_DPE вне форума
Dima_DPE
Сказать по правде, никогда не понимал почему какието настройки передают в xml непосредственно рабочему классу. Начнем с того, что xml как правило загружают и его не плохо бы проверить на валидность и распарсить в нечто более удобное для работы, чем анонимный динамический xml. А самому кешу уже передавать эту расперсеную дату. В добавок при смене формата xml, не надо лезть в аниматор, а достаточно поправить парсер xml. Потом можно несколько парсеров написать, для json еще например и т.д. Это я так, мысли вслух.
Старый 09.04.2012 16:03 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
У меня так изначально и было.
Пакет был раза в два толще. В итоге я клонился к простоте и удобству и обрезал всё лишнее.

Собственно сама фабрика для этого и предназначена, можно прямо в нее при надобности втулить нужные форматы. Так как вполне себе можно пользоваться и напрямую Animation - только кешироваться растр не будет.
Старый 10.04.2012 22:27 artcraft вне форума
artcraft
 
Аватар для artcraft
Антон Каролов пару дней назад выложил свою версию аналогичного скрипта
http://www.ant-karlov.ru/iz-vektora-....html#item4427
Старый 10.04.2012 22:41 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
Лол.
Он это сделал из того же примера что и я с год назад))
Только мы разными путями развития пошли, у него остались косяки из исходного варианта, например вот это:
Цитата:
Чтобы такого не получалось, вы можете задавать размер всех кадров вручную. Для этого достаточно перетащить какой-нибудь любой клип, растянуть его, так как вам нужно, и указать ему имя "e_bounds"
Я это выпилил, и много чего другого тоже выпилил, а он наоборот допилил.
Обновил(-а) Dukobpa3 10.04.2012 в 22:51
Старый 10.04.2012 22:43 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
Цитата:
Данные классы являются улучшенным вариантом классов для кэширования векторной графики от TouchMyPixel. И именно эти экземпляры классов я написал на днях, стараясь их сделать наиболее простыми и удобными для использования, поэтому если вы обнаружите недочеты или баги, обязательно пишите!
Кстати вот он об этом и написал
Только я не помнил откуда сперто, теперь буду знать.
Старый 13.04.2012 19:06 toFL вне форума
toFL
Бро, у тебя строчки перепутаны:
Код AS3:
m.scale(_scaleX, _scaleY);
m.translate(-r.x, -r.y);
А нужно:
Код AS3:
m.translate(-r.x, -r.y);
m.scale(_scaleX, _scaleY);
Старый 16.04.2012 17:46 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
Имхо всё верно как раз.
Сначала отскейлили потом переместили. А не наоборот.
Старый 16.04.2012 22:21 carrotoff вне форума
carrotoff
 
Аватар для carrotoff
Примерно полгода назад делал свой велосипед тоже на основе touchmypixel. И на хабре была статья - чувак сделал свой велосипед, вдохновляясь тем же. Популярный у них продуктик вышел
Старый 17.04.2012 11:10 toFL вне форума
toFL
Цитата:
Имхо всё верно как раз.
Сначала отскейлили потом переместили. А не наоборот.
Нет не верно. В твоем варианте будет баг, а в моем не будет - проверил на себе.
Старый 17.04.2012 11:39 ChuwY вне форума
ChuwY
 
Аватар для ChuwY
Продуктик вышел так себе. Да и не только они такую штуку в свободный доступ выложили.
Больше людей вдохновились и написали свои велосипеды или перепилили этот до неузнаваемости.
За что, впрочем, touchmypixel тоже спасибо.

Правда, попытки прикрутить этот "велосипед на тему" кончились неудачно, так как анимации слишком долго конвертились в рантайме и пришлось растрировать вручную
Старый 17.04.2012 12:03 carrotoff вне форума
carrotoff
 
Аватар для carrotoff
Цитата:
Сообщение от ChuwY
Продуктик вышел так себе. Да и не только они такую штуку в свободный доступ выложили.
Больше людей вдохновились и написали свои велосипеды или перепилили этот до неузнаваемости.
За что, впрочем, touchmypixel тоже спасибо.
Ну ровно это я и имел в виду
Старый 22.04.2012 19:36 HardCoder вне форума
HardCoder
 
Аватар для HardCoder
Наконец-то попробовал использовать этот класс у себя. Добавил возможность переходов по метке а не только номеру кадра, чтение скриптов анимации (gotoAndStop и т.д.). Но сразу же столкнулся с одной проблемой. Если в анимации вложенные клипы находятся не на целых координатах, то при растрировании анимация выходит дерганная. Вот пример: http://minus.com/mfWKAHzBw/
Товарищи, очень важный вопрос: ЧТО МОЖНО С ЭТИМ СДЕЛАТЬ? Переделывать 1000 кадров анимации наново под "целые пиксели" - это не выход. Во-первых - долго, во-вторых - не добьюсь нужного эффекта в анимации. Подскажите пожалуйста.
Обновил(-а) HardCoder 22.04.2012 в 19:40
Старый 22.04.2012 19:44 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
Я думаю проблема в скейлах. Если проканает такой вариант то попробуй сделать вектор сразу нужного размера.
Старый 22.04.2012 20:27 HardCoder вне форума
HardCoder
 
Аватар для HardCoder
Dukobpa3, нет. У меня вектор и растр должны быть одинакового размера. Т.е. при растрировании скейл задается "1". При воспроизведении растровой анимации - скейл тоже "1".
В примере я увеличил нижнюю картинку лишь для того, чтобы лучше было видно подергивание.
На верхней (меньшей) картинке, если присмотреться - видно дергание. Это ничего, если это анимация ходьбы. Но если, например, герой стоит на месте, но только машет руками - очень некрасиво выглядит: машет только руками - а трясется все тело.
Старый 23.04.2012 18:16 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
HardCoder - можешь мне скинуть одну такую анимацию. Хочу посмотреть, может придумаю что-то. А сам не сталкивался с такой проблемой, потом видимо и не отловил.
Старый 28.04.2012 13:34 HardCoder вне форума
HardCoder
 
Аватар для HardCoder
Dukobpa3, я уже разобрался, просто не имел возможности отписать. И так дело вот в чем. Если getBounds анимации (прямоугольник) имеет НЕ целые значения (x, y, width, height), то при отрисовке в битмап (в другой прямоугольник) эти значения округляются. В следующем кадре, когда анимация изменяет размеры - меняется размер прямоугольника (getBounds). И при отрисовке опять округляется. В итоге в каждом кадре значения округляются в большую или меньшую стороны. То есть прямоугольник начинает прыгать на один пиксель влево или вправо, вверх или вниз.
Решил проблему таким путем: на самом нижнем слое анимации создал мувиклип прозрачного прямоугольника, который бы отвечал за границы анимации (getBounds). Назвал его mcBounds. Все значения его подогнал под целые пиксели. При отрисовке, вместо:
Код AS3:
r = _clip.getBounds(_clip);
написал:
Код AS3:
r = _clip.mcBounds.getBounds(_clip);
Старый 28.04.2012 13:37 HardCoder вне форума
HardCoder
 
Аватар для HardCoder
Только, ведь не понятно как никто с этим не столкнулся до сих пор. Это же очевидно! Я, сколько ни старался - не смог создать простейшую анимацию, которая бы не дрожала при проигрывании после отрисовки. Пока не догадался рисовать прозрачные "целые" прямоугольники.
Старый 28.04.2012 14:03 СлаваRa вне форума
СлаваRa
 
Аватар для СлаваRa
у меня есть вот такой вот метод для отрисовки фреймов:
Код AS3:
private function getFrame(clip:IBitmapDrawable):Frame {
  var bd:BitmapData;
  var label:String = '';
  if (clip is BitmapData) {
    bd = (clip as BitmapData).clone();
  } else if (clip is Bitmap) {
    bd = (clip as Bitmap).bitmapData.clone();
  } else if (clip is DisplayObject) {
    var $clip:DisplayObject = clip as DisplayObject;
    var r:Rectangle = $clip.getBounds($clip);
    if (!r.isEmpty()) {
      bd = new BitmapData(int(r.width), int(r.height), true, 0x00000000);
      bd.lock();
      var m:Matrix = new Matrix();
      m.identity();
      m.translate(Math.ceil(-r.x + 1), Math.ceil(-r.y + 1));
      m.scale($clip.scaleX, $clip.scaleY);
      bd.draw($clip, m);
      bd.unlock();
    }
    if ($clip is MovieClip) label = ($clip as MovieClip).currentLabel;
  } else {
    Error.throwError(ArgumentError, 0);
  }
  return new Frame(bd, r.x * $clip.scaleX, r.y * $clip.scaleY, label)
}
проблем не возникало
Старый 06.06.2012 17:53 Dukobpa3 вне форума
Dukobpa3
 
Аватар для Dukobpa3
@СлаваRa, не сразу понял в чем разница
Код AS3:
m.translate(Math.ceil(-r.x + 1), Math.ceil(-r.y + 1));
Math.ceil( x + 1) - в этом соль мануально округляем так как нам надо, спасибо, внесу себе такую строчечку
 

 


Часовой пояс GMT +4, время: 15:57.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.