Из вектора в растр

Вопрос растеризации я уже не раз затрагивал в своем блоге, но все записи на эту тему в основном ограничивались лишь теорией и отсылками в другие блоги. Сегодня вновь вернемся к этой теме, но уже с исходниками и примером.

Как-то я пообещал рассказать и показать с примером, как превратить векторную флеш графику с анимацией в растровую. И вот, наконец настало время исполнить свое обещание.

Для чего нужна растеризация?

Большое количество векторной графики в играх рано или поздно приведет к тому, что игра начнет тормозить. Но как же так!? Ведь так удобно рисовать и анимировать прямо во флеше векторные картинки, и при этом за счет вектора получать красивый результат. Но, увы. Правда такова, что для рендеринга векторной графики, и тем более для твиновых (програмных) анимаций, нужно намного больше процессорных ресурсов, чем для рендеринга растровых картинок. И тут даже аппаратное ускорение не поможет, так как видеокарты не умеют рисовать векторную графику. И выход остается только один: создавать из векторной графики последовательность растровых картинок. Но если отнестись к процессу растеризации буквально, то на вас или на вашего художника свалится дополнительная работа по созданию растровых атласов (наборов кадров), а ваша игра может за счет растра сильно прибавить в весе. Ну что ж, не буду рассказывать, чем чревата для художника дополнительная нудная работа, и какие могут быть последствия от ожирения игры, а лучше сразу перейдем к оптимальному решению этой проблемы.

Программная растеризация

Флеш предоставляет весьма простые и удобные методы для рисования векторных клипов в растровую графику. Методы не самые быстрые, но их с головой хватит, чтобы во время запуска игры перегнать всю векторную графику в растровую, и забыть о тормозах из-за вектора, как о страшном сне. И у этого метода есть отличные преимущества и некоторые недостатки:

Преимущества

  • Приложение имеет стройный и сексуальный вид, так как все графические ресурсы/анимации внутри приложения хранятся в векторе;
  • Вы всегда можете внести изменения в анимацию и вам не придется экспортировать графику в растр и перекраивать атласы;
  • Высокая производительность;
  • Создание атласов прямо из своей игры для портирования игры на другие платформы (?).

Недостатки

  • При большом количестве графических ресурсов может потребоваться приличное количество времени для растеризации графики во время запуска приложения;
  • Так же при большом количестве графических ресурсов занимается много оперативной памяти для хранения растеризированной графики.

Не смотря на преимущества, недостатки кажутся весомыми, но на самом деле они страшны только для очень больших проектов и только в неумелых руках.

Звучит вкусно! Но как же использовать всю эту вкуснятинууу!? Сегодня я представляю вашему вниманию три небольших класса, которые позволят работать вам с растровой графикой, как с обычными клипами, без особой необходимости вникать в то, как работает растеризация.

Классы для работы с растром

Первый класс AntAnimCache — это класс хранилище всех растеризированных клипов. Он выполняет процесс кэширования всех указанных клипов и хранит результаты своей работы в течении всего времени работы приложения. Класс является синглтоном, поэтому к нему можно получать доступ откуда угодно, но вам скорее всего это не понадобится.

Второй класс AntAnim — это класс самой анимации, он имеет весьма простую структуру и является хранителем всех кадров какого-то клипа. Все кадры внутри класса хранятся в публичном массиве frames в формате BitmapData. Так же в этом классе находятся методы для преобразования векторного клипа в растр. Но вам эти методы скорее всего тоже не понадобятся, так как их использует класс AntAnimCache.

И последний класс AntActor — это класс графическая сущность, которая занимается воспроизведением и рендером растровых анимаций. А если проще, то это тот самый класс, который идет к вам на замену векторных MovieClip. Этот класс унаследован от Sprite и имеет такие же методы, как у MovieClip для работы с анимацией. То есть фактически, используя этот класс, вы работаете как самым обычным MovieClip, но лишь с той разницей, что вся графика внутри него растровая.

Пример использования классов

Расмотрим пример использования описанных выше классов:

private function init(event:Event):void
{
  // Первым делом получаем указатель на cache
  var cache:AntAnimCache = AntAnimCache.getInstance();

  // Добавляем имена клипов, которые необходимо растеризировать
  cache.addClipsToCacheQueue( [ "SomeClip_mc", "SomeClip2_mc", "e.t.c" ] );

  // Добавляем обработчики для слежения за ходом выполнения процесса кэширования
  cache.onProgressCallback = onCacheProgress;
  cache.onCompleteCallback = onCacheComplete;

  // Запускаем процесс кэширования
  cache.makeCache();
}

Предположим, что описанная выше функция init() запускается когда приложение загрузилось и прелоадер создал основной класс программы, а в нем вызвался этот самый метод. Первым делом тут мы получили указатель на кэш, потом добавили в него имена классов клипов, которые необходимо растеризировать. Обратите внимание, что имена передаются в массиве, то есть массив-список всех клипов вы можете задавать где-либо еще, а тут только передавать его кэшу, как аргумент. Далее мы передаем указатели на методы, которые будут вызываться по мере работы кэша. И последней строчкой запускаем процесс кэширования.

Теперь рассмотрим процесс отслеживания хода кэширования:

private function onCacheProgress(percent:Number):void
{
  trace(percent * 100);
}

В этом методе вы можете показывать пользователю текущий прогресс. Этот метод должен обязательно иметь один аргумент с типом Number, через который мы можем узнать сколько процентов выполнено. Теперь посмотрим на метод завершения процесса кэширования:

private function onCacheComplete():void
{
  // тут создаем главное меню игры или еще что-то
}

Этот метод будет вызван, когда процесс кэширования успешно завершился и можно отправлять игрока в игровое меню или сразу в игровой процесс. Теперь разберемся как же использовать содержимое растрового хранилища. Создадим анимированный спрайт:

var actor:AntActor = new AntActor();
actor.addAnimFromCache("SomeClip_mc", null, true);
actor.x = 100;
actor.y = 100;
actor.play();
addChild(actor);

Тут все вам должно быть знакомо, так как ничем не отличается от создания обычного клипа или спрайта, кроме второй строчки. Эта строчка ключевая в работе актера, и именно ей мы определяем, какое графическое содержимое будет иметь спрайт. Первым аргументом мы указываем имя клипа растерезированного ранее, который будет отображаться в актере. Вторым аргументом мы можем задать уникальное имя текущей анимации — имя анимации используется для переключения между анимациями внутри актера. Если уникальное имя не задано, то в качестве имени анимации используется имя клипа. И последний аргумент указывает необходимость переключиться на добавляемую анимацию немедленно. В актер может быть добавлено сколько угодно анимаций:

actor.addAnimFromCache("SomeClip_mc", null, true);
actor.addAnimFromCache("SomeClip2_mc");

Чтобы переключаться между анимациями, используйте метод switchAnim(animName:String):

actor.switchAnim("SomeClip2_mc");

Рассмотрим иной пример:

actor.addAnimFromCache("HeroStand_mc", "stand", false);
actor.addAnimFromCache("HeroWalk_mc", "walk");

Теперь, чтобы переключиться с анимации ожидания на анимацию ходьбы, нужно написать:

actor.switchAnim("walk");

Имя текущей анимации вы можете узнать так:

if (actor.animName == "stand")
{
  //...
}

В остальном работа с классом AntActor особо ничем не отличается от работы со Sprite или MovieClip.

Еще немного вкусняшек

Помимо стандартных возможностей, у класса AntActor есть еще несколько приятностей:

Вы можете задать скорость воспроизведения анимации, по умолчанию значение равно 1:

actor.animSpeed = 0.5; // Замедление в два раза

Вы можете отключить зациклинность анимации, например если анимацию нужно проиграть один раз:

actor.repeat = false;

Узнать, проигрывается ли сейчас анимация, можно так:

if (actor.playing)
{
  //...
}

Чтобы отследить завершение проигрывания анимации, вы можете воспользоваться перехватом события Event.COMPLETE или передать метод, который будет вызван, когда анимация завершит проигрываться:

actor.addEventListener(Event.COMPLETE, animCompleteHandler);
// или
actor.onCompleteCallback = animCompleteHandler;

Еще можно проигрывать анимацию в обратном порядке, если сделать так:

actor.reverse = true;

Ну и последнее, вы можете включать и отключать сглаживание для растра. По умолчанию smoothing == false и если вы не планируете масштабировать или вращать актера, то не включайте smoothing:

actor.smoothing = true;

И чуть не забыл, чтобы красиво и навсегда удалить актера, вызывайте метод free(). Если актер был добавлен куда-либо методом addChild(), то при вызове метода free() подчистятся все внутренности актера и он самоудалится оттуда, куда был добавлен:

actor.free();

Подготовка клипов для растеризации

При растеризации клипов еще есть маленький нюанс: когда вы растеризируете какой-либо клип с анимацией, то размер всех кадров определяется по первому кадру и таким образом, если последующие кадры будут занимать больше пространства, чем первый кадр (например персонаж разводит руками), то последующие кадры получатся обрезанными.

Чтобы такого не получалось, вы можете задавать размер всех кадров вручную. Для этого достаточно перетащить какой-нибудь любой клип, растянуть его, так как вам нужно, и указать ему имя "e_bounds", а при кэшировании, если программа найдет клип с такими именем, она будет использовать его размеры для всех кадров.

Центр вращения клипа которым обозначаются размеры кадров обязательно должен быть в левом верхнем углу, как на картинке.

И помните, что центр вращения AntActor будет равен центру вращения заданному внутри растеризируемого клипа.

Живой пример

А теперь можно посмотреть, как это быстро и красиво работает в жизни. Кстати, если не вращать и не изменять размеры актера, то растр почти не отличается от вектора. А что до замыливания при разворотах актера, то с этим придется смириться, так как растр нельзя масштабировать и поворачивать без потери качества, увы!

Добавляйте больше спрайтов кликая мышкой. Профайлер доступен из контекстного меню к флешке.

Ссылка на исходники — CS4, *.zip, 850Кб.

Обновление от 26 сентября 2012 — исправлена проблема с не точным рассчетом процесса в процентах при кэшировании графики.

Заключение

Данные классы далеко не оптимальны по производительности и качеству, так как по прежнему привязаны к обычным Sprite и ENTER_FRAME, но все же это отличная альтернатива стандартным MovieClip.

 

Кстати, сколько я не смотрел фреймворков на Flash, ни один фреймворк не дает возможности преобразовывать вектор в растр на лету. Даже хваленый Адобом Starling не имеет такой возможности и на форуме то и дело теперь всплывают вопросы «Чем бесплатно и удобно создавать атласы?». Ээх.. И ведь именно этот факт вынудил меня писать свой фреймворк.

Данные классы являются улучшенным вариантом классов для кэширования векторной графики от TouchMyPixel. И именно эти экземпляры классов я написал на днях, стараясь их сделать наиболее простыми и удобными для использования, поэтому если вы обнаружите недочеты или баги, обязательно пишите!

Ссылки по теме:

- Тотальная растеризация
- Решение проблемы производительности во Flash


Индикаторы: Action Script 3, Уроки
Постоянная ссылка

 

 

@Ant.Karlov
Антон, в сових проектах ты используешь эту реализацию или все таки все рисуется в один битмап в обход спрайтам и клипам?

Mufasa
4 Апреля 2012
— 05:31
#

@Mufasa, во всех играх которые выпущены, я использовал что-то такое как здесь описано. А в новой игре (Зомботрон 2) уже новая реализация с рендером в один битмап (буффер).

Ant.Karlov
4 Апреля 2012
— 06:08
#

Антон, ты, как и обычно, великолепен.
Спасибо за пост!

Сергей Драган
4 Апреля 2012
— 08:17
#

@Ant.Karlov а про реализацию в один битмап можно поконкретнее? Какие плюсы?

Иван
4 Апреля 2012
— 09:37
#

Для старлинга есть экстеншен для генерации атласов "на лету".

elmortem
4 Апреля 2012
— 09:44
#

@Иван посмотрите на flixel и flashpunk, в них используется именно такой способ рендеринга

Рома
4 Апреля 2012
— 10:04
#

@Mufasa, Ant.Karlov:
Вставлю свои пять копеек про организацию отрисовки растров. Без одной важной детальки отрисовка в один буфер ничем не будет отличаться от способа, когда мы аттачим Bitmap`ы с растрами к клипам и позволяем плейеру их собрать и отрисовать.

Плейер не отдаст контроль приложению пока не отрисует все клипы в какой-то служебный буфер (или в видео память), при отрисовке в один буфер вы даже потратите больше времени (сперва вы рисуете в свой буфер, потом плейер ваш буфер рисует в свой или в видеопамять, лишняя буферизация получается).

Так вот важная деталька - лишней буферизации не избежать, но нужно получать максимальный профит от этого, организуя более эффективный подход к рендерингу.

В моем фреймворке я предоставляю любому объекту возможность "заказывать рендеринг" (он делает это каждый раз когда обновляется, если ему это требуется). Т.е. есть дерево поиска, которое полностью перестраивается перед каждой отрисовкой. В это дерево попадают указатели на функции (предоставляемые "заказчиками"), которые при обходе дерева вызываются в определенном порядке (в порядке возрастания Z координаты или глубины, указанной "заказчиком"). Вызываемая функция в качестве аргумента получает указатель на графический буфер и рисует в него некий графический контент (необязательно одно изображение, что запрограммируем то и будет рисовать).

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

BuxomBerry
4 Апреля 2012
— 10:09
#

@Ant.Karlov
а можешь добавить метод-аналог currentFrame для воспроизводящей анимации?

Мяско
4 Апреля 2012
— 12:05
#

@Ant.Karlov
написал про currentFrame до того как посмотрел код, оказывается все уже есть. цены тебе нет <3

Мяско
4 Апреля 2012
— 12:19
#

@Ant.Karlov
единственное, не понятно, зачем кадру указывать Number тип, к тому же потом ещё округлять всеравно: Math.floor(frame - 1). можно же просто сразу задать int ?

Мяско
4 Апреля 2012
— 12:22
#

Парни, а на Starling никто не обращал внимание? )))

fmaxx
4 Апреля 2012
— 12:36
#

А ты читал, статью, Антон обращал внимание.

Виталий
4 Апреля 2012
— 13:15
#

@Ant.Karlov, хотел бы предложить свою версию растеризации клипов, которая работает в моем "ядре" GreenCore - http://fundux.ru/article37637.

Здесь http://pastebin.com/bL5Wtj20 (функция addFrameAt) можно взглянуть на сам процесс растеризации. При такой растеризации отпадает необходимость в соблюдении особой структуры клипов.

Dracyla
4 Апреля 2012
— 13:45
#

@BuxomBerry, а когда ваш сайт то заработает?

Иван
4 Апреля 2012
— 14:44
#

@Иван:
скоро уж. доделываю оформление.

BuxomBerry
4 Апреля 2012
— 16:31
#

Спасибо за статью, Антон!
Тоже очень интересно услышать о преимуществах растеризации с рендером в один битмап. Хотя бы в двух словах...

jarofed
4 Апреля 2012
— 18:51
#

Рисовать и анимировать во флеше.
Потом переводим в SpriteSheet`ы с помощью SWFSheet

sl1p
4 Апреля 2012
— 20:09
#

Спасибо Вам, очень полезно! Пытаюсь использовать в своей игре.
При попытке подцепить кэширование анимации нашел следующие Фичи:

1) Фича: падения на вызове getDefinitionByName():
ReferenceError: Error #1065: Variable [class SomeClipXXX_mc] is not defined.
at global/flash.utils::getDefinitionByName()


Все дело в том, что вместо
var mov:MovieClip = new SomeClipXXX_mc();
теперь есть только
cache.addClipsToCacheQueue( [ "SomeClipXXX_mc"] );
А это значит что линкер FlashBuilder-a игнорирует вытягивание SomeClipXXX_mc с swc библиотеки, где у меня лежат все ресурсы.
Приходится делать принудительное объявление var dummy1: SomeClipXXX_mc; чтоб класс был включен в swf.


2) Фича: зацикливание анимации если порожденный класс (скажем Goblin) в ф-ии update() следит за кадрами самостоятельно. Причина – проскакивание первого кадра.

Ф-ия enterFrameHandler:

1) _curFrame = (_curFrame >= totalFrames) ? 1 : _curFrame;
2) nextFrame(true);

1-ая строчка проверяет достижения конца кадров. Если доходим до конца, то устанавливаем текущий кадр в единицу.
2-ая строка вызывает nextFrame и текучий кадр становится 2! Выходит, что первый кадр мы никогда не увидим!!!
Простой Фикс:
_curFrame = (_curFrame >= totalFrames) ? 0 : _curFrame;

FirstFlashGame
4 Апреля 2012
— 20:26
#

P.S.
Еще один вариант принудительного включения классов c swc это опции -includes or -include-libraries для mxmlc компилятора. Правда включать все классы принудительно, в том числе и те которые используются лишь иногда для дебага тоже не очень подходящее решение.

FirstFlashGame
4 Апреля 2012
— 20:34
#

FirstFlashGame, проще создать функцию, которую никогда не будешь вызывать, где перечислить все нужные тебе клипы. Хотя - это тоже не самый лучший выход.

Я, кстати, написал растеризатор для flixel, но выкладывать не хочу, т.к. там куча шлака. Надо будет почистить и выложить. Сам растеризатор написал для VileRobots. Он в принципе универсальный - делает BitmapData атласы из MovieClip-ов. Наверняка существуют более удобные альтернативы. Нужно будет поискать. Просто я еще класс FlxSprite расширил.

WeslomPo
4 Апреля 2012
— 21:13
#

BuxomBerry
А как возможно перерисовывать только некоторые объекты?
Предположим, есть некая карта, по которой двигаются юниты. И вот, юнит1 остановился, то есть необходимость его перерисовывать отпала. Но юнит2-то продолжил двигаться, и его с его предыдущей позиции надо как-то убрать. Как это реализуется? Берется кусочек карты и им, как заплаткой, заделывается юнит в предыдущем положении? Или целиком карта перерисовывается? Ежели так, то как получается, не перерисовывая юнит1, оставить его на экране?

приветкакдела
4 Апреля 2012
— 21:35
#

@приветкакдела:
Имелось ввиду отсечение по видимости (объект вышел за пределы видимой части экрана или просто стал невидимым), т.е. объект сам это определяет и просто не запрашивает отрисовку (для системы это равносильно отсутствию объекта).
То, что вы описали не имеет особого отношения к тому, что я писал выше.
Но это возможно (в том же AbeOdysee и Baldur`s Gate это реализовано), только делается немного по другому, и принцип схож с вашими "заплатками". Вот как это делается:
Юнит2 перемещающийся по игровому экрану копирует участок буфера, который собирается изменять (там уже нарисован юнит1). Копируемый участок запоминается, а в буфер на это место рисуется юнит2. Когда юнит2 покидает эту позицию он возвращает скопированный ранее участок на место (тем самым стирая себя из буфера). При постоянном порядке вызова функций отрисовки для всех объектов в течении всей игры этот метод будет работать на ура, если же порядок изменится, то возможна ситуация, когда юнит2 будет возвращен на покинутое им место кем-то другим, например юнитом3.

BuxomBerry
4 Апреля 2012
— 22:01
#

@BuxomBerry, преимущества у рендера в один битмап (буфер), безусловно есть — как минимум мы избавляемся от кучи DisplayObject`ов со всей той шелухой которая с ними тащится. В связи с этим решаются и некоторые визуальные проблемы, а так же появляются дополнительные возможности постобработки рендера и прочего.

>> Плейер не отдаст контроль приложению пока не отрисует все клипы в какой-то служебный буфер (или в видео память), при отрисовке в один буфер вы даже потратите больше времени (сперва вы рисуете в свой буфер, потом плейер ваш буфер рисует в свой или в видеопамять, лишняя буферизация получается).

Ничего лишнего не получается. +1 битмап размером с игровой экран, это не большая плата. Все те объекты которые не попадают в текущую видимую область экрана — не рисуются в этот буфер.

То что вы написали выглядит интересно, но слишком заумно и мне кажется такой подход не слишком рационален. Обычно необходимость рендера какого-либо спрайта можно определить 3 факторами: объект не скрыт, объект живой, объект в области видимости. И зачем ради трех этих факторов писать сложную систему заказа рендера когда можно сделать одно условие — мне не очень понятно. Тем более объекты должны рендерится всегда, не зависимо от того изменились они или нет. Другое дело если вы подразумеваете под изменением объекта его текущий кадр, наложение эффектов или еще что-то, при которых объект рендерит себя в свой внутренний буфер, а выводится на экран всегда при условии выше описанных 3х факторов — но тут уже получается на каждый объект по своему буферу плюс общий буфер, что в итоге может быть дорогой платой, и оправдана такая плата только пожалуй при наличии сложных эффектов.

Ant.Karlov
4 Апреля 2012
— 22:36
#

@Мяско, у текущего кадра Number внутри актера используется затем, чтобы можно было задавать скорость воспроизведения анимации. Например, если у вас в анимации хотбы персонажа (или анимации взрыва) каждый второй кадр является промежуточным, а ключевые кадры через один, то следует удалить промежуточные кадры — так вы с экономите вдвое на оперативной памяти, а скорость воспроизведения такому спрайту задать вдвое ниже обычной: 0.5. Таким образом у вас получится 1 кадр в два захода enterFrame.

Ant.Karlov
4 Апреля 2012
— 22:40
#

@fmaxx, Starling ничего так, я правда сильно его не ковырял. Но в будущем думаю по ближе ознакомится. А пока игры с использованием аппаратного ускорения еще рано выпускать. Более половины пользователей успешно обновились на новый плеер, а вот порталы не спешат включать аппаратную поддержку на своих сайтах и игры со Stage3D на таких сайтах работать не будут — а таких сайтов пока пожалуй 95%! Так что играм на Starling и на stage3d в плане дистрибуции пока некуда деваться.

Ant.Karlov
4 Апреля 2012
— 22:44
#

@sl1p, безусловно конвертировать клипы можно всякими бесплатными инструментами. Но, например, если бы я конвертировал весь арт для Зомботрона предложенным вами способом, то игра в место 8мб (из которых 5 мб занимают музыка и звуки), весила бы 200мб, что для Flash игры смертельно!

Ant.Karlov
4 Апреля 2012
— 22:46
#

@FirstFlashGame,

1. Можно вместо стринговых имен клипов передавать имена классов клипов, тогда в целом получится получится тот же new SomeClip_mc();

cache.addClipToCacheQueue(SomeClip_mc);

Но для этого надо будет немножко все переписать :)

2. Спасибо за найденный мини баг. Поправлю :)

Ant.Karlov
4 Апреля 2012
— 22:53
#

@WeslomPo,

>> FirstFlashGame, проще создать функцию, которую никогда не будешь вызывать, где перечислить все нужные тебе клипы. Хотя - это тоже не самый лучший выход.

Так, а как же тогда? Если вы в коде не указываете некоторые классы клипов из библиотек, то компилятор считает их не используемыми и выкидывает их из структуры программы. Поэтому если вам нужны какие-то клипы, пусть они даже нужны только для растеризации, то их нужно либо указывать, либо принудительно включать в код соответствующей опцией. Других вариантов либо нет, либо я о них просто не знаю :)

Ant.Karlov
4 Апреля 2012
— 22:57
#

Третий вариант оптимален:
Объявляем все мувики в массиве, но не как строки, а как «указатели» на объекты. Дальше перебором каждого элемента получаем имя класса ф-ей getQualifiedClassName.

import avmplus.getQualifiedClassName;

var includeClasses:Array = [Movie1_mc, Movie2_mc, Movie3_mc];
var rastrList:Array = [];
for (var i:int = includeClasses.length-1; i >= 0; i--) {
rastrList.push(getQualifiedClassName(includeClasses[i]));
}
cache.addClipsToCacheList(rastrList);

FirstFlashGame
4 Апреля 2012
— 23:23
#

@Ant.Karlov:
DisplayObject ведь всего-лишь контейнер, такой же как Bitmap, для BitmapData. Все равно придется хранить кучу этих контейнеров, просто они не будут привязаны к stage и не будут получать никаких событий. (невозможно всю графику хранить в одном атласе, он ограничен в размерах)

Мы по разному понимаем описанный мной подход и соответственно пользу от него видим по разному. Основной профит не в определении "рисовать объект или нет", дело совсем не в этом. Подумайте как с вашей текущей системой вы бы реализовали игру, где каждые 5 секунд на экране появляются и исчезают сотни объектов (персонажи в изометрии, состоящие из нескольких частей, пули, взрывы, частицы), которые еще при этом постоянно меняют свою глубину отрисовки (вобщем alien shooter представьте). Это наведет вас на правильные мысли.
Думаю стоит накидать статейку, так будет легче описать все в деталях.

BuxomBerry
5 Апреля 2012
— 00:01
#

@FirstFlashGame, слишком много действий получается: перечислить клипы в массив, потом получить имена клипов, потом добавить в кэш. Тогда уж лучше передавать кэшу какие клипы нужно растерезировать непосредственно перечислением имен классов, а кэш уж пусть сам извлекает их имена и использует эти имена как ключи для анимаций. Делов-то. Попробую реализовать.

Ant.Karlov
5 Апреля 2012
— 00:03
#

Огромное спасибо, Антон, за статью!

Rise
5 Апреля 2012
— 00:10
#

@BuxomBerry, если мы говорим именно о "ручном рендере" когда все битмапы вручную рисуются в один буфер, то тут уже никакие контейнеры не нужны, безусловно должен быть какой-то один bitmap на спрайт который будет использоваться как холст для хранения текущего кадра и который будет рисовать себя в общий буфер, но ему уже не нужно добавляться в структуру отображения, флеш не будет его процессить и не будет посылать ему сообщения.

>> Мы по разному понимаем описанный мной подход и соответственно пользу от него видим по разному.

Текущая система идентична тому какую систему представляет собой флеш, то есть используя эти классы для реализации изометрической игры я бы также мучался как и с обычными Sprite и MovieClip :) Сортируем списки спрайтов, удаляем все со сцены, потом добавляем все по новой в правильном порядке и т.п. Ваш подход не рационален для большинства флеш игр которым не нужна сортировка по глубине, впрочем даже если нужна глубина, то можно сделать разные группы/слои которые рендерятся в порядке заданной очереди. Как раз поэтому я и написал что ваш подход заумен и не рационален — его использование не очень оправдано для большинства игр. Но если речь заходит об изометрии, то тут конечно уже совсем другой разговор. :)

Ant.Karlov
5 Апреля 2012
— 00:17
#

Кстати, если кто позабыл, про ручной рендер в общий буфер я писал здесь. Там же есть примеры для сравнения производительности приведенного здесь метода против рендера в общий буфер. Сразу скажу что рендер в общий буфер по производительности выиграл.

Ant.Karlov
5 Апреля 2012
— 00:24
#

1)
Немного прогнул под себя:
trace("AntActor::switchAnim() - WARNING:...
trace("AntAnimCache::getAnim() - WARNING:...

Для своего удобства переработал на throw new Error("Class not cached!"); Потому что в лог выводится слишком много информации и данные trace там просто теряются, а ошибка потом всплывает уже совсем в другом месте. Для меня более удобно срабатывание ASSERT сразу, для возможности быстрой идентификации проблемы.

2) Получаю кешированный клип так:
_art.addAnimFromCache("MovieXXX_mc");
Дальше пытаюсь сделать _art.gotoAndPlay(2); и в результате
TypeError: Error #1009: Не удается вызвать свойство или метод со ссылкой на объект "null".
Все дело в том что вместо _art.addAnimFromCache("MovieXXX_mc")
я должен был вызвать _art.addAnimFromCache("MovieXXX_mc", null, true
)
Немножко не логично, как для первого знакомства с классом. Для меня привычней так:

public function addAnimFromCache(key:String, uniqueName:String = null, switchToAnim:Boolean = false):void
{
var animName:String = (uniqueName == null) ? key : uniqueName;
_animations[animName] = AntAnimCache.getInstance().getAnim(key);

if (switchToAnim)
{
switchAnim(animName);
} else {
if ("undefined" == _curAnimName) {
switchAnim(animName);
}
}

}

Если еще не установлено имя текущей анимации, то переключаемся на первую, которой инициализируем класс. Естественно, это исключительно мое умозаключение, возможно кому-то удобней сначала набить класс анимациями, а уже потом назначить текущую на которую необходимо переключится.

FirstFlashGame
5 Апреля 2012
— 00:28
#

@Ant.Karlov
> Создание атласов прямо из своей игры для портирования игры на другие платформы (?).
В яблочко :)

bischak
5 Апреля 2012
— 00:29
#

@Ant.Karlov:
Как только мы начинаем говорить о растеризации и уходе от стандартного addChild, removeChild, мы пересекаем ту черту, где кончается флеш и начинается нечто заумное, вам так не кажется?

Разве можно назвать нерациональной систему, максимально самостоятельную и универсальную для любых задач?

К примеру, я использую ту же древовидную структуру и те же алгоритмы, что и для сортировки по глубине (буквально те же классы), чтобы хранить игровые объекты и делать это таким образом, чтобы соседние в дереве объекты были соседями и в игровом пространстве (находятся в одном и том же квадрате на игровой карте). Структура лишь немного усложнилась, дерево стало вложенным, а вместо одного параметра Z дерево теперь нагружается двумя X и Y (координаты квадрата). Ну и хранятся в дереве теперь не ссылки на функции, а ссылки на игровые объекты наследующие общий интерфейс.

Только не подумайте, что я спорю или умничаю, мне просто нравится рассуждать на подобные темы. Это позволяет по новому взглянуть на уже известные вещи.

BuxomBerry
5 Апреля 2012
— 00:51
#

Будет экономичнее для каждого из кадров использовать битмапу индивидуального размера, и это как раз избавит от необходимости вручную задавать границы. Вот здесь есть реализация такого подхода за авторством Scmorr`а.
Правда, при медленном вращении разнокалиберных битмап может наблюдаться небольшое дребезжание из-за различий в интерполяции битмап разного размера.

При повороте могут вылезти артефакты на крайних пикселях (по периметру которые) по-этому лучше по периметру оставлять прозрачный зазор в один пиксель, рамка также позитивно влияет на смягчение "лесенки" которая возникает при повороте прямых линий на небольшой угол, хотя к фигурным спрайтам это не сильно относится, они и так не сильно прилегают к краям.

Перед процессом растеризации можно выставить качество BEST, после вернуть, или если битмапы не крутятся вообще можно сбросить на LOW, это немного прибавит резвости отрисовке.

claymore
5 Апреля 2012
— 04:46
#

Добавлю к сказанному Claymore, что реазизация Scmorr`a очень удобна еще тем, что высчитывает для каждого кадра смещение по Х и У, так что началом координат растеризованного клипа будет не верхний-левый угол, а точка привязки клипа из Flash IDE (координаты 0,0)

Особенно это приятно когда в игре есть составные объекты из кучи частей. И вращать опять же растеризованный клип вокруг точки привязки очень удобно, не нужно расставлять в клипе дополнительные объекты, или в коде прописывать точки поворота.

orbit
5 Апреля 2012
— 11:47
#

Новая Фича:
ArgumentError: Error #2015: Invalid BitmapData.
at flash.display::BitmapData/ctor()
at flash.display::BitmapData()
at ru.antkarlov.animation::AntAnim/cacheFromClip()

Проблема в приведении типов.
Есть у меня анимация ствола пушки. Ее параметры во FlashIDE: width:21.20, height:1.0
В середине ф-ии cacheFromClip используется clip.getRect(clip). Этот вызов возвращает немного уточненные координаты: width:21.20, height:0.9
Теперь нужно обратить внимание на тип поля Rectangle.height – это Number. А вот конструктор BitmapData принимает int-ы. Поэтому происходит округление к ближайшему меньшему числу, а это 0. Вот и причина падения.
Самое просто исправление – округление к ближайшему большему целому:
public function cacheFromClip(clip:MovieClip):void
{

var bmpd:BitmapData = new BitmapData(Math.ceil(r.width), Math.ceil(r.height), true, 0x00000000);

}

FirstFlashGame
5 Апреля 2012
— 11:57
#

Полезная статья, спасибо!

Кстати, почему бы при кешировании каждого кадра не брать Rectangle текущего кадра, и отрисовывать новый кадр с новыми размерами?
Тогда мы избавились бы от проблемы, - "если последующие кадры будут занимать больше пространства, чем первый кадр, то последующие кадры получатся обрезанными."

Darkwing Duck
5 Апреля 2012
— 13:11
#

1) Рамку везде устанавливать не очень интересно, все время путаюсь. Поэтому избавился от рамки следующим образом: перебираю все клипы и определяю самую верхнюю левую и самую нижнюю правую точки. Вот на основании этих точек и создаю Rectangle.
2) Чтоб постоянно не создавать Matrix в цикле, вынес наверх создание матрицы и применяю identity() для ее очистки.


public function cacheFromClip(clip:MovieClip):void
{
var r:Rectangle;

// Если размеры кадров заданы вложенным клипом с именем "e_bounds".
if (clip["e_bounds"])
{
var s:Sprite = clip["e_bounds"] as Sprite;
r = new Rectangle(s.x, s.y, s.width, s.height);
s.visible = false;
}

// Иначе определяем размер кадров в автоматическом режиме
else
{
var minX:Number = 0, minY:Number = 0, maxX:Number = 0, maxY:Number = 0;
var bounds:Rectangle = null;
for (var k:int = clip.totalFrames; k >= 1; k--) {
clip.gotoAndStop(k);
allChildGotoFrame(clip, k);
bounds = clip.getBounds(clip);
minX = Math.min(bounds.topLeft.x, minX);
minY = Math.min(bounds.topLeft.y, minY);
maxX = Math.max(bounds.bottomRight.x, maxX);
maxY = Math.max(bounds.bottomRight.y, maxY);
}
r = new Rectangle(minX, minY, maxX - minX, maxY - minY);
}



var m:Matrix = new Matrix();

var n:int = clip.totalFrames;
for (var i:int = 1; i <= n; i++)
{
clip.gotoAndStop(i);
allChildGotoFrame(clip, i);
var bmpd:BitmapData = new BitmapData(Math.ceil(r.width), Math.ceil(r.height), true, 0x00000000);

m.identity();

m.translate(-r.x, -r.y);
m.scale(clip.scaleX, clip.scaleY);
bmpd.draw(clip, m);
frames[frames.length] = bmpd;
}

offsetX = r.x;
offsetY = r.y;
}

FirstFlashGame
5 Апреля 2012
— 14:39
#

Фича внутри функции AntAnimCache->step: Процент загрузки никогда не равен 100%.
Хотя возможно так задумано, потому что есть отдельный callback для оповещения об окончании процесса кеширования.

Фикс:
protected function step():void
{

(onProgressCallback as Function).apply(this, [ (_curProcessingItem + 1) / _cacheQueue.length ]);

}

FirstFlashGame
5 Апреля 2012
— 17:46
#

@BuxomBerry, к сожалению я не до конца понимаю о какой структуре визуализации мы рассуждаем, поэтому мне сложно более предметно разговаривать о рациональности использования подобных подходах в любых проектах. Я правильно понимаю что вы говорите о BSP дереве, или у вас какая-то иная структура?

Ant.Karlov
5 Апреля 2012
— 19:35
#

@claymore, у scmorr интересная реализация, я пробовал её использовать. Но в ходе экспериментов оказалось что недостатков у такого подхода больше чем плюсов. Может быть проблемы возникли только у меня из-за того что я пытался применить её с использованием общего буфера и мой код был не идеален, но у меня небыло времени заниматься исследованием и решением сложившихся проблем.

Основные недостатки подхода scmorr:
- Хранение смещений для каждого кадра;
- Расчет bounds rect для каждого кадра (например если спрайт повернулся и нужно определить занимаемый им прямоугольник с учетом поворота);
- Поддержка разноразмерных фреймов на других платформах!? Создание атласов с разноразмерными кадрами??
- Изменение размеров временного буфера спрайта под каждый из кадров, или создание буфера размером с самый большой кадр, а если кадры имеют большие смещения? Расчет максимально занимаемой области всеми кадрами и создание временного буфера соответствующего размера (для ручного рендера).
- и т.п.

В общем я решил придерживаться классических спрайтовых лент где для анимированного спрайта каждый кадр имеет одинаковые размеры.

Ant.Karlov
5 Апреля 2012
— 20:01
#

@orbit, представленная здесь реализация так же учитывает смещения спрайта относительно центра вращения. Центр вращения слева вверху должен быть только у клипа "e_bounds" которым задается размер максимального кадра, иначе размеры кадра будут рассчитаны не верно. Само содержимое клипа которое будет растеризироваться может быть расположено как угодно относительно центра вращения.

Ant.Karlov
5 Апреля 2012
— 20:04
#

@Darkwing Duck, классическая анимация состоит из кадров одинакового размера. Если вы будете экономить на памяти создавая кадры разного размера, не забудьте для каждого кадра хранить его смещения относительно центра клипа. Так же потом за счет такой оптимизации могут возникнуть сложности с расчетами в коде игры если вам нужно будет привязываться к размерам спрайта или еще что-то.

Ant.Karlov
5 Апреля 2012
— 20:08
#

@FirstFlashGame, спасибо за найденные проблемы и пожелания! Сегодня выложу обновленные классы с учетом твоих пожеланий :)

Ant.Karlov
5 Апреля 2012
— 20:11
#

@Ant.Karlov, может не до конца понял тебя, но попробую оппонировать , сразу оговорюсь что классом Scmorr`а не пользовался и может не до конца вник во все нюансы реализации.

"Хранение смещений"
Мне кажется, два числа типа Number на кадр это не слишком критично, и потом с лихвой компенсируются уменьшением занимаемой конечными битмапами памяти. Если все-таки - критично, то вполне можно в один Number запихать два значения смещения, а с усечением точности и 4, при необходимости засунуть в байтаррей и упаковать.

"Расчет bounds rect для каждого кадра"

В приведенном примере, на мой взгляд, считать границы понадобится только для текущего кадра. В любом случае можно сохранить матрицу поворота (или отдельно синус и косинус ) после расчета первого кадра, и использовать эти значения для всех остальных.

"Поддержка разноразмерных фреймов на других платформах!?"

На уровне фреймворков практически везде поддерживается, на крайний случай можно реализовать самостоятельно. На счет "классических спрайтовых лент", похоже ты имеешь ввиду совсем уж "классические" времен 8-битных приставок, сейчас на платформах с GPU рендером наоборот стараются упаковать как можно больше всего в один атлас, потому что все что находится на одной текстуре можно отрисовать за один вызов, этот финт используют практически все 2d-движки, и он дает ощутимый прирост в производительности, сейчас "классический" подход скорее выглядит так: http://img341.imageshack.us/img341/767/spritesheetw.jpg

"Изменение размеров временного буфера"
Здесь, к сожалению, не понял.

И , если тревожат дополнительные расчеты на этапе растеризации, временные битмапы и прочие доп. расходы, то в окончательном варианте можно все рассчитанные размеры сохранить и вшить в финальный билд.

claymore
5 Апреля 2012
— 21:39
#

@claymore, когда разрабатываешь игры в одиночку, приходится анализировать каждую задачу и прогнозировать последствия. Например, я попытался использовать класс Scmorr под свои нужды, и вылезла куча проблем — все эти проблемы безусловно решаются но потребовали от меня больше трудозатрат чем я ожидал изначально. Я примерно прикинул что 5-10 мб оперативной памяти не стоят моей недели времени на поиск решений и решил отказаться от такого подхода. Безусловно тру программиста которому не надо моделировать уровни, рисовать графику и анимировать персонажей — не остановила бы такая интересная задача с поиском решений для нее. Но вот мне приходится балансировать где-то посерединке :)

Поэтому не тру программистам я не советую для каждого кадра одной анимации (для одного спрайта) делать свой размер чтобы потом не столкнуться с непредвиденными проблемами и задачами.

На атласе который ты привел в пример все кадры из одной серии (анимации) имеют одинаковые размеры, но расположены они крайне оптимально. Мы же обсуждаем произвольный размер каждого кадра для одной анимации.

Ant.Karlov
5 Апреля 2012
— 23:17
#

Добавил отдельным набором доработанные классы с учетом пожеланий оставленных в комментариях. Оригинальные исходники примера и классов так же доступны чтобы большая часть статьи не растеряла своей актуальности :)

Ant.Karlov
6 Апреля 2012
— 00:16
#

Все просто отлично, лишь вот в этом месте нужно поставить круглые скобки:
(onProgressCallback as Function).apply(this, [ (_curProcessingItem + 1 )/ _cacheQueue.length ]);

Правда, я порождаю класс AntActor не от Sprite а от MovieClip. Не все клипы я кеширую, а некоторые сервисные функции принимают в качестве аргументов именно MovieClip. Но выбор предка на общую функциональность никак не влияет.

Реализовал кэширование графики в середине App перед созданием Game.

Для удобства ввел статическую ф-ию, задача которой по указанному имени клипа вернуть созданный объект AntActor:
public static function createAntActor(strClass:String):AntActor
{
var actor:AntActor = new AntActor();
actor.addAnimFromCache(strClass);
return actor;
}

Раньше создание анимации выглядело так:
_movie = new MoveiClipXXX_mc;
а теперь так:
_movie = AntActor.createAntActor("MoveiClipXXX_mc");

Ну а чтоб 40 клипов не прописывать ручками в Array я использовал командную строку:
grep -hr ".createAntActor" С:ProjectPathMyGame | sed -r "s/([^"]+")([^"]+)(".*)/2,/"
и на выходе имею список классов готовых для встраивания в массив.

Теперь имею растеризацию без плясок с бубном, без сверх навороченных классов, с понятными механизмами работы, а главное – с все той же простой архитектурой без критических переделок.
Огромное Вам спасибо за Ваш труд!

FirstFlashGame
6 Апреля 2012
— 00:56
#

@Ant.Karlov
Само собой выбор используемых библиотек - индивидуальный вопрос, и время внедрения немаловажный фактор. Я и не настаиваю, что библиотека Scmorr`a идеальна для всех случаев, я сразу там после ссылки и описал недостатки разнокалиберности битмап, но для игр где не используется вращение битмап (для такой игры она и разрабатывалась) она подходит на все сто, и внедряется без каких-то дополнительных усилий, думаю, в Грибника бы встала как влитая. Не совсем согласен с твоим безапелляционным "не советую связываться", думаю эта библиотека , как один из вариантов решения, достойна упоминания в комментариях.

В атласе, там вроде есть различия в размерах, некоторые фазы с прыжком, хотя с линейкой не мерил. Но суть даже не в этом, просто хотел проиллюстрировать, что смещения задаются для каждой картинки в отдельности.

claymore
6 Апреля 2012
— 01:19
#

@claymore
У меня в текущем проекте используется именно такой метод и почему-то я основной плюс нашел в скорости обработки. Такой вариант у меня рисуется быстрее чем атласный. На всякий случай уточню, что повороты у меня не используются, т.к. copyPixels шустрее draw. Поэтому повороты растеризуются заранее. Большой минус: занимает достаточно много оперативной памяти. Большой плюс - сотни анимированных объектов на один скрин.
@BuxomBerry
При рендере в один буфер, один DisplayObject (), при рендере как в статье DisplayObject`ов больше. Это в том числе может дать разницу в производительности.
Я так понимаю фича твоего подхода в том, что объекты которые не требуют перерисовки просто вообще никак графически не обабатываются. Однако проверку на необходимость прорисовки проходить должны ведь в любом случае каждый кадр. То есть достаточно ощутимый прирост это даст при сортировке по Z-буферу.
У меня используется подход разделения всей графики на статику и динамику. Статика растеризуется один раз, а рендериться в собственный буфер заного только при необходимости (при реализации например параллакса в платформере бэкграунд перерендеривается только при перемещении на 100 пикселей, остальное движение обеспечивается движением объекта битмапы, прирост в данном случае от изначального расчитанной векторной графики). Динамика растеризуется один раз и рендериться постоянно за исключением не попадающих на скрин или поле зрения (прирост по той же причине плюс отброс ненужных объектов).

Mufasa
6 Апреля 2012
— 05:54
#

@Ant.Karlov:
Ага, оно самое, BSP-дерево. Только в 2d варианте и секущие плоскости параллельны плоскости экрана. При разборе дерева объекты получают доступ к буферу и рисуют.

Кстати, вот Mufasa говорит, что большое количество DisplayObject влияет на производительность. Скажите мне, если я DisplayObject не присоединяю ни к чему, пускай он просто используется как коробка, куда я положил изображение, или несколько векторных примитивов. Валяется себе в памяти и все.

Почему это вдруг влияет на производительность? Чем это хуже, чем хранить несколько голых BitmapData без контейнеров или вообще всю графику в одном BitmapData?
Экономия памяти не в счет, DisplayObject(именно сам класс) по сравнению с хранимым им BitmapData, почти ничего не весит.
А всю графику в одном изображении хранить, это только если мы какую-то микроигрушку делаем, как на денди и все тайликами. Но там это хоть оправдано было.

@Mufasa:
Нет, фичу вы не уловили, и я видимо просто не смог кратко все объяснить так, чтобы фичу можно было уловить.
Объекты которые не требуют перерисовки и в других подходах не обрабатываются, но проверка конечно-же всегда присутствует, иначе как узнать, что объект не требует перерисовки.
Попробую объяснить фичу "на пальцах", представьте вы сидите дома, читаете газетку. Вдруг вам захотелось порисовать, вы достаете телефон и отправляете смс провайдеру (я хочу рисовать в такое-то время, например в 13:00). Потом вы опять читаете газетку и забываете о смс. Вдруг когда часы бьют 13 вы магическим образом телепортируетесь в просторное помещение с огромным холстом со всю стену, на полу стоят ведра с краской, кисти, карандаши, линейки, вобщем все что нужно. Холст не чистый, там уже кто-то порисовал, но в помещении никого нет, вы одни. Вы берете кисти краски и рисуете, слой за слоем, пока вам не надоест. Когда вам надоедает, вы просто идете к дальней стене комнаты и выходите в единственную дверь. Стоит вам открыть дверь как вы оказываетесь опять дома, в кресле с газеткой в руках. Когда вы открывали дверь, вы услышали как позади вас кто-то появился в комнате (очередной рисовальщик). У вас есть выбор, отправлять смс еще раз или нет.

А теперь для сравнения могу рассказать как работает обычная схема отрисовки в флеше на таком же примере:
вы появились в своей комнате, в углу стоит кресло, на нем лежит газетка. Но у вас из спины торчит толстенный кабель, его никак не вынуть и он уходит куда-то в окно. Вы намереваетесь взять газетку и почитать, как вас вдруг резко выдергивают с помощью кабеля на улицу, с огромной скоростью тащат через весь город и затягивают в узенькую трубу где-то в промзоне. Двигаясь по трубе вы видите спереди и сзади таких же бедолаг, как вы. Не успеваете вы перекинуться и парой слов с соседями, как вылетаете с другого конца трубы. Вы зависаете на своем кабеле и замечаете, что висите прямо перед измалеванном краской полотном. Вдруг, сверху спускается пульверизатор, обливает вас краской с головы до ног, затем что-то тянет вас и со всего разгона впечатывает покрашенной стороной вашего тела в полотно. После всех ваших мучений вас утаскивают назад домой к уютному креслу, чтобы повторить процедуру минут этак через 15.

Возможно фичу не объяснил, но это было весело. :)

BuxomBerry
6 Апреля 2012
— 09:50
#

@BuxomBerry
Да уж весело. =) Нехорошо так с человеками.
Ну я и имел в виду, что когда объект не требует обработки то он никак не обрабатывается (то есть газету читает, пъет кофе, сидя тапками к камину). Только не учел, что может быть ситуация когда объект вовсе никак не обновляется и следовательно не требует ресурсов для отрисовки (если предположить что на "холсте" он остался). Это ведь так? Поэтому предположил, что основной пирост производительности можно ожидать от "автоматической" Z-сортировки.
По поводу DisplayObject: я имел в виду именно тот случай когда он добавлен на сцену (как в статье) и соответственно тратит процесоррное время на внутренний "Flash`эвский" рендер. Плюс если в нем хранятся какие либо относительно сложные векторные данные, то вывод такого объекта займет больше временни нежели вывод битмапы.


P.S. Описание действительно забавное. =)

Mufasa
6 Апреля 2012
— 10:53
#

@Mufasa:
Наверное стоит упомянуть, что объект и след, оставленный им в буфере (когда мы его нарисовали) никак не могут быть связаны. То, что сам объект не требует перерисовки (ну не изменился он, поворот, размеры, позиция, все то же самое), вовсе не значит, что его след в буфере не потребуется нарисовать снова.

Мне кажется то, о чем вы говорите - это Z-буферизация. К сожалению, как этот алгоритм реализовать с приемлимой скоростью на флеше я совершенно не представляю. Разве что в версиях плейера с аппаратным ускорением, видеокарты этот алгоритм еще с бородатых времен поддерживают.

BuxomBerry
6 Апреля 2012
— 11:42
#

Антон, жаль видеть как ваши игры живут только во флэше! Посмотрите на проект www.haxenme.org ! Его делают два очень толковых человека, проект активен и регулярно выходят свежие релизы. В частности имеется поддержка рендеринга векторного (swf) арта на c++ таргетах, в частности для того же кэширования.

Nomed
6 Апреля 2012
— 13:52
#

@Ant.Karlov
не подскажешь как лучше всего сделать движущийся задний фон(например облака), с точки зрения производительности? анимация обычно идет по циклу ведь.

Мяско
7 Апреля 2012
— 11:50
#

Для моей игры, в зависимости от цветовой схемы уровня, необходимо немного корректировать изображение врагов. Вот теперь думаю, как это реализовать.
Каждый раз, при загрузке новой карты, вызывается спец.функция-фильтр, принимающая номер уровня и список врагов подлежащих изменениям в цвете.
Эта функция получает AntAnim (оригиналы BitmapData) для каждого врага и применяет преобразования. Измененные BitmapData складываются в определенный массив AntAnim-а и потом используются для рисования внутри AntActor.

Другими словами, AntAnim должен содержать два списка BitmapData – оригинал и изображения с примененным фильтром. AntActor использует фильтрованное изображение, если оно есть, или чистый оригинал.

Вообще, каков порядок работы с BitmapData, при потребности наложения фильтров после растеризации изображения?

FirstFlashGame
7 Апреля 2012
— 21:24
#

@claymore, посмотрел на атлас приведенный в пример по внимательнее и действительно на особенно крупных кадрах заметны небольшие изменения в размерах.

Вообще, действительно, я кажется понял свою ошибку: делая шаг в сторону изобретения своего велосипеда и собственно показывая как его изобретать я по прежнему агитирую не связываться с его изобретением :)

Тем более реализация Scmorr для кэширования клипов отлично ложится на приведенный здесь способ использования кэшированной графики так как большую часть всех расчетов на себя берут стандартные классы флеша. А мое без апеляционное "не связываетесь", все же было направлено на использование таких кадров в ручном рендере — хотя это уже как раз за гранью казуальной разработки и если уж дело до этого дошло, то размер кадров не должен пугать :) Сам я конено, рано или поздно собираюсь разобраться с размером кадров на ручном рендере но уже в следующем проекте.

Ant.Karlov
8 Апреля 2012
— 00:19
#

@BuxomBerry, мне как-то не приходило в голову использовать BSP структуру для рендера объектов и/или для их обработки в целом в 2д, подумаю/почитаю об этом на досуге, спасибо за наводку! :)

То что DisplayObject в какой-то степени влияет на производительность — это факт, посмотрите прошлую запись про тотальную растеризацию, принцип растеризации и использования графики одинаков, а вот на выводе стандартные DisplayObject`ы теряют немного в производительности. Кстати, там же я писал что графика которая не имеет трансформаций в ручном рендере может выводится через copyPixels, и это, в итоге, тоже дает солидный прирост производительности (например все тайлы карты могут рисоваться таким методом, а тайлов бывает как минимум в 2-3 визуальных слоя).

А всю графику в одной текстуре нужно хранить только для аппаратного ускорения чтобы отправлять спрайты пачками на рендер в видео карту. В софтверном же рендере не имеет значения как хранить ресурсы в памяти. При использовании общего атласа в софтверном рендере может быть даже потеря в производительности на этапе вырезания текущего кадра из атласа, с последующей вставкой (рендером) на сцену. Поэтому для софтверного рендера, на мой взгляд, кадры лучше в нарезке хранить ;)

Ant.Karlov
8 Апреля 2012
— 00:31
#

@Nomed, спасибо за предложение, но к сожалению моих сил и времени уже не хватает на портирование своих игр на другие платформы/языки.

Ant.Karlov
8 Апреля 2012
— 00:37
#

@Мяско, если делать облака, то лучше создавать 3-4 спрайта и двигать их, а когда облако выходит за пределы экрана, то просто "респавнить" его с противоположной стороны экрана и так бесконечно ;)

Ant.Karlov
8 Апреля 2012
— 00:41
#

@FirstFlashGame,

>> Вообще, каков порядок работы с BitmapData, при потребности наложения фильтров после растеризации изображения?

У меня лично таких потребностей не возникало поэтому я об этом не задумывался. Но если реализовывать твой вариант (копировать оригинальные кадры цветоизменять их и хранить отдельно), то может случится так что памяти не хватит. Поэтому тут бы я наверное делал, например так, как это сделано во Flixel. Там для каждого спрайта есть небольшой временный буфер (bitmap) куда рисуется текущий кадр с цветовыми трансформациями и другими эффектами, можно даже что-то поверх все этого еще вручную рисовать, а потом уже из этого временного буфера спрайт рисуется на сцену со всеми эффектами. То есть видоизменяется только копия текущего кадра.

Ant.Karlov
8 Апреля 2012
— 00:46
#

@Ant.Karlov

а что насчет того что, все облака сделать одной картинкой, а затем юзать copyPixels + scroll()?

просто я хочу понять, ведь логичней их сделать растром нежели вектором? облака обычно большие, двигаются постоянно.

Мяско
8 Апреля 2012
— 12:07
#

@Мяско:
Если облака разного размера и стиль графики мультяшный, то векторный вариант намного лучше будет смотреться. Комбинировать векторную и растровую графику ведь никто не запрещает.

И не забывайте, что есть такая штука как beginBitmapFill, endBitmapFill. Сделайте бесшовную по горизонтали картинку облаков и заливайте ей прямоугольник, чтобы двигать облака используйте смещение заливки.

BuxomBerry
8 Апреля 2012
— 12:38
#

@BuxomBerry
вопрос не в комбинации и стиле, вопрос в другом. "как лучше всего сделать с точки зрения производительности?" =)
ведь нарисовать можно в векторе, в том же стиле и перегнать уже в битмапу.

а вы уверены что beginBitmapFill будет быстрее copyPixels?=)

Мяско
8 Апреля 2012
— 15:02
#

А что тут гадать, вы засеките время и посмотрите миллион отрисовок сперва одним методом, потом другим. Сами и узнаете.

У вас много облаков будет чтоли? Зачем вам такие требования к производительности, для такого элемента как облака?
Обычно так щепетильно относятся к частицам или каким-то мелким спрайтам в большом количестве (тайлам к примеру).

BuxomBerry
8 Апреля 2012
— 15:50
#

@BuxomBerry
не в количестве дело, а в том что облака могут занимать 1/4 или 1/3 экрана.
не злитесь на меня Т_Т это предположения, как рендер работает досконально не изучал, поэтому опытных спрашиваю, естественно оба метода реализую и посмотрю.

Мяско
8 Апреля 2012
— 16:43
#

В интернете нет эмоций :) Вы даже не можете быть уверены, что я не кот, который в тайне от всех пописывает с хозяйского компа в блог Антону Карлову.

BuxomBerry
8 Апреля 2012
— 17:02
#

2Мяско не страдай ты фигней, возьми готовый движок с растровым рендером (flashpunk или fixel), там и скроллящиеся бэкграунды с паралаксами и быстрый рендер (copypixel без масштабирования/поворота, draw в остальных случаях), разберись и сосредоточься на самой игре. Ну или оттуда можешь выковрять нужные тебе процедуры.

Если у тебя не планируется несколько сотен двигающихся/масштабируемых/вращающихся объектов на экране, то производительности растрового рендера тебе хватит даже если ты будешь свою картинку рисовыать draw`ом.

Я недавно тестил игру на eee pc 901, там 512мб оперативки и процессор меньше гигагерца, флешка 720х480 идет на 60 фпс, при этом там пачка бэкграундов с паралаксами, несколько слоев облаков, все это рисуется в битмапдату размером с флешку и выводится на экран.

orbit
8 Апреля 2012
— 17:17
#

@orbit откуда привычка судить по тому, что неизвестно? =) это не страдание фигней, это любопытство.

Игра готова на 70-80% решил заняться оптимизацией, чтобы добавить побольше новых эффектов.

Первым делом взялся за эти несчастные облака, потому что сейчас это просто 8 sprites, которые двигаются по Y и "респаунятся" потом в противоположной стороне экрана. Посчитал логичным, что такое простое явление, как скроллинг бэкграунда, можно сделать производительней.

Мяско
8 Апреля 2012
— 17:44
#

@Мяско:
Не оптимизируйте такие вещи, только время зря потратите. Обычно в программировании оптимизируют только так называемые "bottleneck" приложения, а не все подряд.

BuxomBerry
8 Апреля 2012
— 18:08
#

"Bottleneck" - это узкие места в системе, на вики можно ознакомиться с этим понятием.

BuxomBerry
8 Апреля 2012
— 18:09
#

2Мяско это именно страдание фигней, т.к. твои действия на конечную производительность оказывают влияние в размере меньше 1%.

orbit
8 Апреля 2012
— 18:34
#

@orbit лол вот ты даешь, о моих действиях ты узнал после того как ты написал про "фигню страдать", в данном случае ты фигней страдаешь, умничай в другом месте.

Мяско
8 Апреля 2012
— 18:49
#

@Ant.Karlov я в своем проекте использую классы напрямую из swc библиотек, благо их не так много. Сейчас подумал, о том что можно сделать большой большой клип куда пихнуть все нужные клипы и где нибудь в коде просто вызвать его имя, компилятор включит его и все зависимости в проект. Но это тоже тот еще огород :).

WeslomPo
10 Апреля 2012
— 01:47
#

@WeslomPo, когда работал во FlashIDE (имеется в виду компилировал программу), то был у меня там такой клип в который я складывал все клипы которые экспортируются в код чтобы прелоадер работал :)

Ant.Karlov
10 Апреля 2012
— 02:59
#

Мвахах :)
http://www.flasher.ru/forum/blog.php?blogid=552
Вот я пару дней назад писал, потом в каментах ссылку на твой пост дали:)
Кстати как оказалось моя поделка тоже из того же источника что и твои наработки, правда я это допиливаю уже с год наверное, на выходных наконец-то в порядок привел.
Ну и я забыл уже откуда брал, спасибо за ссылка на ТучМайПиксель:)

Dukobpa3
10 Апреля 2012
— 21:46
#

@WeslomPo, @Ant.Karlov
Если вы юзаете ассеты из swc библиотек и хотите чтоб все они были доступны посредством getDefinitionByName вместо того чтоб связывать их все метатегом [Embed... или "пихать их в какой то общий клип" включите используемую библиотеку полностью в код. (По умолчанию инклудятся только связанные классы).

Во FlashDevelop это делается так:
ПКМ по нужной swc --> ставим галочку Add To Library --> Опять ПКМ по этой же swc --> Options --> выбираем подходящий вариант инклуда. (для описанной выше цели это будет Include Library (Include completely) ).

Извините, если я не правильно понял преследуемую вами цель.

Andrew Mur.
12 Апреля 2012
— 17:11
#

@Andrew Mur., опа, и в правду так, работает. Не знал - спасибо.

WeslomPo
13 Апреля 2012
— 14:58
#

@playerversion Flash 9.0

@Ant.Karlov, ты компилируешь проекты для 9 плеера?

genm
21 Апреля 2012
— 13:04
#

Оценил "e_bounds"! Для некоторых анимаций полезно обрезать по краям, не заморачиваясь над прятаньем лишнего за спец.спрайтами или наложением масок или установкой scrollRect-а

FirtsFlashGame
28 Апреля 2012
— 23:44
#

А как быть, если у нас в кэшируемом мувиклипе есть другой вложенный мувиклип?

Giovanni
30 Апреля 2012
— 17:04
#

@Giovanni, вложенные клипы тоже растеризируется. Если у вложенного клипа есть аниманция (кадры) или другие вложенные клипы с анимацией (кадрами), их кадры тоже переключаются согласно основному клипу.

Ant.Karlov
30 Апреля 2012
— 20:15
#

@genm, да, компилирую под 9ый плеер так как не не использую фичи 10го плеера.

Ant.Karlov
30 Апреля 2012
— 20:18
#

Спасибо за ответ, Антон! Мне стоило уточнить один момент. Что делать, если в кэшируемом мувиклипе только один кадр (то есть он по сути является спрайтом-контейнером для других мувиклипов (например для нескольких мувиклипов с драконами))?

Giovanni
1 Мая 2012
— 20:25
#

И еще уточнение. Возможно, кэширование и с одним кадром в мувиклипе работатет, но у меня не получается заставить в этом одном кадре проигрывать анимации вложенных мувиклипов. То есть если я положу в мувиклип с одним кадром несколько мувиклипов с драконами и закэширую мувиклип-контейнер, то у драконов анимация не будет проигрываться, а будет остановлена в первом кадре.

Giovanni
1 Мая 2012
— 20:37
#

to Giovanni
Вам стоит изучить реализацию кэширования в классе AntAnim:
В ф-и cacheFromClip происходит перебор всех кадров контейнера и переключения всех вложенных клипов на указанный кадр.

В двух словах: сколько кадров имеет контейнер – столько кадров проигрывается во вложенных клипах.
Если один кадр в контейнере, то и во всех вложенных клипах будет отображаться только один кадр в процесса кэширования. Увеличьте число кадров в контейнере и увеличатся кадры, проигрываемые во вложенных клипах.

FirtsFlashGame
2 Мая 2012
— 20:46
#

@Giovanni, при использовании данных классов вам больше не нужно делать клипов контейнеров так как принцип работы с клипами (анимацией) становится иным.

По сути AntAnimCache — становится большим контейнером в который помещаются все растеризированные клипы, а чтобы их разделить по игровым объеткам (юнитам), например что-бы быстро переключатся между анимациями как вы это делали с клипом контейнером (myClip.gotoAndStop("label_stand")), нужно добавить все необходимые анимации в AntActor и переключатся между ними методом switchAnim("anim_name") — это будет так же как вы привыкли работать с обычными клипами.

Иными словами, теперь в *.fla все клипы с анимациями у вас хранятся раздельно — не нужно их групировать в клипах контейнерах. Теперь группирование клипов (анимаций) вам нужно делать непосредственно в коде после того как вы выполнили кэширование, например при инициализации юнита.

Ant.Karlov
2 Мая 2012
— 21:58
#

Каковы рекомендации к весу игры на диске и в памяти?
Вес флешки на харде: до XX MB?
В памяти: до XX MB?

FirtsFlashGame
10 Мая 2012
— 16:36
#

@FirtsFlashGame,

Размер игры в идеале до 10 мб. Но чем меньше тем лучше, т.к. от этого сильно зависит как игра будет расходится по порталам, на многих порталах требование чтобы размер игры не превышал 5мб. Максимум 20мб, но при условии, что игра действительно достойна этих самых 20мб.

Размер занимаемой памяти, в идеале до 100мб, максимум 200мб, а больше уже рискованно.

Ant.Karlov
10 Мая 2012
— 16:54
#

Спасибо Вам. Я помаленьку приближаюсь к 100мб в памяти, хотя графика примитивна и далеко не все еще реализовано. Нужно будет искать баланс между вектором и растром, уменьшить количество кадров, оптимизировать размеры мувиков...

Вот немного добавил кода для распечатывания самых больших мувиков в целях отладки:

1) Добавил функцию для подсчета размера объекта AntAnim:

public function getObjectSize():Number
{
var size:Number = getSize(this);
for (var i:int = frames.length; i >= 0; i--) {
size += getSize(frames[i]);
}
return size;
}/*getObjectSize*/

2) В классе AntAnimCache добавил гетер для _animations
3) В ф-ии onCacheComplete() после завершения кеширования добавил отсортированный вывод размера закешированой анимации:

var totalSize:Number = 0;
var curSize:Number = 0;
var obj:Object = AntAnimCache.getInstance().animations;
var vector:Vector.<Object> = new Vector.<Object>;
for each(var anim:AntAnim in obj) {
curSize = anim.getObjectSize();
totalSize += curSize;
vector.push({name:anim.name, size:curSize});
}

var compareFunc:Function = function(obj1:Object, obj2:Object):Number
{
if (obj1.size > obj2.size) return -1;
else if (obj1.size == obj2.size) return 0;
else return 1;
}

vector.sort(compareFunc);

for each(obj in vector) trace(obj.name + " -> " + obj.size);
trace("----------------nSummary size (" + totalSize + ")");


Теперь видно самые большие клипы.

FirtsFlashGame
10 Мая 2012
— 18:25
#

Дефолтный FPS в прелоадере выставляется в 35, размеры флеша - 640x480. Каков рекомендуемый размер прелоадера (первого кадра флешки)? Я так понимаю, что слишком большой размер(вес) приведет к запоздалой демонстрации начала загрузки флеш, и при малой скорости интернета пользователь может решить что игра повисла…

FirtsFlashGame
11 Мая 2012
— 16:43
#

Доброго времени суток )
Хороший, а главное удобный фреймворк получился ))) спасибо
У меня возникла проблемка:
пишу игру, где собираюсь смещать задний фон. Сам задний фон состоит из прямоугольных кусочков размером равным флешке. эти кусочки циклично смещаются в бок (когда самый левый кусок окажется слишком далеко, он переносится в самое право). Так вот при движении этих кусочков наблюдаются их искажения (все кусочки растеризованы, НЕ масштабированы и смещаются на ЦЕЛОЕ количество пикселей). Пробовал и код оптимизировать, и шаг смещения подбирать - все равно дергания и искажения прут ((((( причем не важно сколько кусочков - пять или пятьсот (пробовал) - искажения и подергивания одинаковы.
Слегка помогает, если включить GPU-ускорение, но не в полной мере
Вопрос к знатокам: скажите кто-нибудь сталкивался с подобным? если да, то как бороться

Hagemay
17 Мая 2012
— 10:14
#

@FirtsFlashGame, первый кадр флешки 100-200кб, максимум 500кб. Поэтому лучше сильно не мудрить с внешним видом прелоадеров и делать их максимально простыми и легкими чтобы быстро появлялись.

Ant.Karlov
18 Мая 2012
— 12:10
#

@Hagemay, попробуйте включить сглаживание изображениям (smoothing) чтобы избавится от искажений. А вот подергивание при скролле — это известная проблема флеша и, к сожалению, никак не лечится.

Ant.Karlov
18 Мая 2012
— 12:12
#

@Ant.Karlov, спасибо за отклик. Есть вопрос - возможно ли включить GPU-ускорение программно (родная адобовская документация предлагает это делать вручную через параметры публикации, гугление других результатов тож не дало)

Hagemay
18 Мая 2012
— 17:02
#

@Hagemay, если вы о wmode="direct", то это актуально только для Stage3D. Иное аппаратное ускорение (которым обладают младшие версии плеера 10.x), по умолчанию, вроде, включено, если конечно пользователь не выключил его принудительно в настройках плеера. Но по моим экспериментам это программное ускорение не дает существенного прироста производительности.

Если вы хотите настоящее аппартное ускорение то работайте со Stage3D, а другое программное ускорение какое-то псевдо-аппаратное ускорение, поэтому можете на него особо не рассчитывать даже :) (имхо)

Ant.Karlov
18 Мая 2012
— 20:03
#

@Ant.Karlov, спасибо ))
Прирост я наблюдал компилируя эту свою игру из Flasf IDE - дерганий действительно становилось меньше (не исчезали совсем, но меньше) (если, конечно, воспользоваться Publish, а не Ctrl+Enter), хотя... где гарантия, что на компе конечного игрока флешка поведет себя точно также...
В любом случае спасибо - я получил ответы на вопросы и теперь с чистой совестью забросил гугление и вернулся к разработке )))

Hagemay
18 Мая 2012
— 20:39
#

P.S. как Вы и сказали - существенного прироста не было

Hagemay
18 Мая 2012
— 20:40
#

@Hagemay, секрет уменьшения дерганий при использовании Publish кроется скорее всего в том, что в браузере у вас релизная версия плеера, а при Crtl+Enter дебаг версия плеера, и тут могу с уверенностью сказать что дебаг версия очень тормозная так как ведется сбор информации о работе программы, кода и т.п. чтобы при возникновении ошибки тут же сообщить разработчику где возникла проблема. А вот релиз версия плеера работает намного быстрее и не сообщает пользователю об ошибках и именно она стоит у всех игроков флеш игр, поэтому ориентируйтесь в конечном итоге именно на нее.

Ant.Karlov
19 Мая 2012
— 09:25
#

@Ant.Karlov
спасибо )

Hagemay
19 Мая 2012
— 11:59
#

И релизная флэшка кстати весит меньше, во флексовом проекте у меня аж в 2 раза разница была в объеме.

MXPain
19 Мая 2012
— 16:49
#

Дак вот ведь в чем косяк - порылся на досуге, опыты поставил - в итоге:
во флеш ИДЕ можно включать это ускорение;
автоматически оно не включается;
в FD+FlexSDK не нашел как включается и включается ли вообще (*бьюсь головой о стену*).
А жаль - в FD код удобнее писать (((

Hagemay
25 Мая 2012
— 00:02
#

Большое спасибо за статью, обязательно применю в следующей игре. Думал переходить на Starling, но как сказали 95% сайтов не поддерживают Stage3D. Антон, как думаешь, можно ли прикрутить твоё решение к Starling и как много это потребует времени?

squirrelsquare
25 Мая 2012
— 12:37
#

@Ant.Karlov

squirrelsquare
25 Мая 2012
— 12:38
#

@Ant.Karlov
У меня опять очередной вопрос )))
почему-то не получается проверить getPixel`ом AntActor`а.
Выражение ()
this._bitmap.bitmapData.getPixel(shX,shY))
всегда выдает ноль вне зависимости от того перекрывает ли координаты shX,shY персонаж или нет ((((
Может хитрость какая? Или я чего не понял...

Hagemay
26 Мая 2012
— 21:33
#

@Ant.Karlov
P.S. Если этот метод использовать нельзя, то напишите пожалуйста как можно узнать перекрывается ли точка рисунком (не квадратом битмапа)

Hagemay
26 Мая 2012
— 21:40
#

@Ant.Karlov
Все ))) Спасибо за внимание, извините за беспокойство и спам - просто взгляд замылился и сам не все учел. Я победил эту фигню )))

На всякий случай поясню в чем дело было, дабы никто не повторял мои ошибки:
дело в том, что getPixel принимает координаты относительно левого верхнего угла КВАДРАТА БИТМАПА. А у мну изначально векторный рисунок (который позже растеризовал и с ним мучался) точку отсчета имел внизу/всередине и позиционировался на сцене также относительно низа/середины. Одним словом ввел поправки в аргументы getPixel и все заработало )

Еще раз извините за спам

Hagemay
26 Мая 2012
— 22:14
#

Adobe Flash CS6 MovieClip->Отображение->Визуализация->Кэшировать как растровое изображение ? Не решение ли проблемы... ?

Aid
8 Июня 2012
— 19:06
#

Антон, здравствуйте.

Встретился с проблемой растеризации больших сложных анимаций, на флэшере создал тему (там много слов и вложение, тут дублировать нет смысла)

http://www.flasher.ru/forum/showthread.php?t=180842

Как бы вы решали такую проблему?

Кирилл
9 Июня 2012
— 18:53
#

@Кирилл, пример из форума мне посмотреть не удалось (наверное у вас Flash CS 5.5, а у меня CS4), в общем у меня не открылся файл. Но прочитав топик у меня создалось ощущение что для вашего случае подошло то решение которое мы совсем недавно обсуждали с BuxomBerry, вот тут...

Если в двух словах, то, возможно, для вашего случае было бы наиболее оптимально рисовать методом Draw только видимую область экрана, то есть растеризировать непосредственно ту часть уровня которую видит игрок в данный момнет, возможно вместе с юнитами. Но нужно тестировать. А вообще 150мб - это не большая плата за растеризацию всех юнитов. Кстати, отражения и повороты юнитов вы так же заранее растеризируете?

Ant.Karlov
10 Июня 2012
— 12:14
#

Антон, 150 мб не на растеризацию всех юнитов, а на растеризацию 1 юнита (~400 кадров 500х500). А юнитов около 10, поэтому делать полный пререндер не вариант.

Проблему решил - сделал быстрый пререндер перед атакой. Долго не мог это сделать, т. к. gotoAndStop() не синхронизует таймлайны, а в 9м плеере ещё и баг - не доступны child`ы. Я об этом не знал, потому единственным выходом видел растеризацию в реалтайме, а это нужно делать перед боёвкой для всех юнитов, что очень долго. В 10м плеере наладил быструю растеризацию (200мс) 200 кадров прямо перед атакой, с синхронизацией всех внутренних таймлайнов.

Кстати, попробовал starling - работает отлично и быстро. Но судя по собранной позавчера стате (20к человек, РФ) у 34% или нет плеера 11го, или стоит software rendering. Потому вариант отпадает пока.

Кирилл
12 Июня 2012
— 16:14
#

@Кирилл:
Откройте ваш FLA файл в FlashIDE, потом выделите все кадры некоторой анимации на всех слоях и convertToKeyframe. После этого у вас goToAndStop начнет показывать каждый кадр правильно, так как трансформации масштаба и повороты больше не будут расчитываться на лету. Преобразуйте так все анимации.

Дальше можете уже растеризовать не дожидаясь пока анимация проиграется в реальном времени. Даже cacheAsBitmap заработает.

noname
12 Июня 2012
— 19:47
#

@Кирилл:
Да, и removeTween на все кадры анимации не забудьте сделать.

noname
12 Июня 2012
— 19:49
#

noname, большое спасибо за совет! :) Не знал, что во флэше можно все в keyFrame преобразовать. Но так сильно увеличивается размер флэшек. Я уже написал функцию nextFrameFull(), синхронизующую все анимации на лету.

Кирилл
12 Июня 2012
— 23:23
#

Антон, а вы решили проблему слегка дерганного скролла, если скроллить битмапу с постоянной скоростью?

Кирилл
16 Июня 2012
— 16:57
#

Здравствуйте. Воспользовался библиотекой.
Но не совсем с таким результатом, которого ожидал.
Есть задача.
Растеризированный квадрат скалить вокруг его центра. А скалиться вокруг точки 0,0.

Нарыл в сети, что можно положить еще в один спрайт и сместить к центру.
И соответственно скалить внешний спрайт.

Есть ли альтернатива вложенному спрайту.

толик
1 Июля 2012
— 21:59
#

@Кирилл, проблема дерганности почти никак не решается во флеше :( Можно только пытаться как-то хитрить со скоростью скролла чтобы минимизировать эффект дерганности визуально, но в целом проблема не решается. Причем, даже с аппаратным ускорением (Molehil) дерганность присутствует :(

Ant.Karlov
2 Июля 2012
— 20:07
#

@толик, в качестве альтернативы вложенному спрайту попробуйте разместить квадрат (шейп) таким образом, чтобы нулевая точка (крестик в клипе) был расположен по центру квадрата :)

Ant.Karlov
2 Июля 2012
— 20:09
#

Нет. Как бы не располагался шейп, после растеризации все равно точка регистрации в 0,0.
Помогло- после присваивания растеризованного изображения:
box.x=-box.width/2;
box.y=-box.height/2;
(пишу по памяти)
Вообщем сдвигаем на половину и вуоля!!!!
Хош крутиш, хош скалиш.

толик
2 Июля 2012
— 20:57
#

При использовании данных классов у меня упорно выскакивает ошибка:
Error: AntAnimCache::getAnim() - ERROR: Missing animation "Hero_mc".
at animation::AntAnimCache/getAnim()
at animation::AntActor/addAnimFromCache()

Это происходит, если я указываю имя мувика так:
cache.addClipsToCacheQueue( [characters.Hero_mc] );

Если класс находится не в пакете - все хорошо работает. Но стоит положить класс с мувиком в пакет - выдает эту ошибку.

Андрей
4 Июля 2012
— 15:39
#

@Андрей, если вы используете *.swc то вам следует кэшировать графику не из библиотеки, а непосредственно из экземпляров клипов:

myCache.cacheFromClip(new Hero_mc());

Проблема тут заключается в том, что по умолчанию клипы скомпилированные в swc не доступны через getDefinitionByName().

Если вы используете FlashDevelop, то там должна быть опция позволяющая включать все клипы в код таким образом чтобы они были доступны через getDefinitionByName() — я об этом часто слышал но на деле никогда не использовал так как не имею возможности работать во FlashDevelop.

Ant.Karlov
5 Июля 2012
— 15:56
#

Большое спасибо.

Андрей
5 Июля 2012
— 17:02
#

А если я клипы из библиотеки таскаю на сцену? Как мне быть??

Антон
9 Июля 2012
— 11:02
#

2Ant.Karlov : А можно как-то чистить кэш с растеризированной графикой?

Виталий
31 Июля 2012
— 21:46
#

@Виталий, очистить кэш можно. Но только при очищении кэша прийдется пересоздавать и спрайты которые ссылаются на анимации в кэше, или как-то сообщать спрайтам, что анимации удалились чтобы они не пытались обратится к несуществующим анимашкам. Если есть острая необходимость то я могу доработать классы и добавить в них возможность очищения кэша. Мне же в своих проектах особой необходимости в этом небыло.

Ant.Karlov
31 Июля 2012
— 22:06
#

Острой необходимости нет. Просто интересно, если в игре имеются уровни, и у каждого уровня своё графическое оформление, если растеризировать графическое оформление всех уровней сразу, это съест кучу оперативной памяти, другой вариант - это растеризировать графику по мере прохождения игры, в таком случае на первом уровне игры оперативной памяти будет требоваться мало, но к последнему уровню значение занимаемой памяти возрастет в разы. Я возможно чего-то не понимаю, и где-то заблуждаюсь, просто интересно, как решить этот вопрос (наиболее логичным кажется, удаление отработавшей графики из кэша, и кэширование требуемой).

Виталий
31 Июля 2012
— 23:48
#

@Виталий, если у каждого игрового уровня свое оформление, то тут следует уже думать об отдельном классе который будет заниматься растеризацией уровней. Данные классы подразумевают, что графику которую вы растеризируете используется в игре везде, поэтому очищение кэша или удаление отдельных анимаций не предусмотрено.

В целом, по такому же принципу можно делать и оформление уровней: вы растеризируете отдельные статические объекты, и составляете уровень из AntActor использующих эти растровые картинки. Таким образом получится так, что у вас в памяти всего один BitmapData с изображением ящика, а ящиков в игре может быть бесконечно много.

Ant.Karlov
1 Августа 2012
— 11:56
#

Спасибо за ответ. А что будет больше кушать оперативной памяти: какой-то одноцветный фон + ящики AntActor`ы или одноцветный фон с ящиками (т.е. как единая картинка), мне просто казалось что вес пикселя равносилен, и если я отрастеризирую обычный зелёный квадрат 256x256 и какую нибудь картину того же размера, на выходе получу одинаковый вес. (сейчас наблюдая за профайлером понимаю что это не так. Я заметил в Zombotron`e тоже было статическое оформление с ящиками, вы тоже там крепили их как AntActor`ы? Сколько в мегабайтах оперативной памяти занимаемой флешкой считается нормальным (конечно ориентируясь на компьютеры среднестатистического игрока) ?

Виталий
3 Августа 2012
— 17:32
#

Хотя возможно отличий в весе нет (отрастеризированного одноцветного квадрата и какой-то картинки составленной из MovieClip во Flash IDE того же размера) может быть это связано с чем-то другим?

Виталий
3 Августа 2012
— 17:43
#

@Виталий, да, вы правы. Bitmap в памяти будет занимать всегда одинаково места не зависимо от того каким образом меняется содержимое его пикселей. В битмапе каждый пиксель имеет некую информацию и количество этой информации для каждого пикселя зависит от того в какой цветовой разрядности вы сохраняете изображение. Например, битмапы с 32 битным цветом и включенной прозрачностью будут занимать больше места в памяти чем, например 16 битные, причем место в памяти они будут занимать по полной, даже если все пиксели будут прозрачными.

> ...какой-то одноцветный фон + ящики AntActor`ы или одноцветный фон с ящиками (т.е. как единая картинка), мне просто казалось что вес пикселя равносилен, и если я отрастеризирую обычный зелёный квадрат 256x256 и какую нибудь картину того же размера, на выходе получу одинаковый вес.

Если вы растеризируете просто одноцветный фон, то вес такого растрового изображения не изменится, даже если вы туда нарисуете кучу ящиков и всякого другого. А если вы сделаете растеризированный фон и отдельно ящик, то к размеру занимаемой памяти фоном нужно будет добавить еще размер изображения ящика. Но, в таком случае, сколько бы много ящиков у вас небыло, они будут использовать одну и туже картинку (bitmapData) из оперативной памяти. Но тут стоит отметить, что в последнем случае, в игровом мире ящики представляются отдельными игровыми объектами и на их обработку будет уходить время, хоть и мизерное.

В Зомботронах все то с чем игрок не может взаимодействовать — это растеризированный разрезанный на тайлы фон :)

Для средне-статического компьютера максимально допустимый размер занимаемой оперативной памяти — 200мб. Можно конечно и больше, но если на борту компьютера малый объем памяти то ресурс памяти выделяемой флешке может быть сильно ограничен и в таком случае, если вашей игре не хватит памяти на компьютере пользователя — она зависнет :) Золотая середина — это 100мб. И помните еще о том, что в отладочном плеере обычная флешка может занимать почти вдвое больше оперативной памяти т.к. отладочный плеер хранит много лишней информации о работе программы для отладки.

> отрастеризированного одноцветного квадрата и какой-то картинки составленной из MovieClip во Flash IDE того же размера

Если MovieClip векторные, то они будут меньше занимать оперативной памяти. Но при этом на их рендер будет уходить больше времени чем на растр. Так что тут стоит не простой выбор: жертвовать памятью или производительностью.

Ant.Karlov
4 Августа 2012
— 10:48
#

Как я заметил в Zombotron`e растеризация графики идёт по ходу прохождения игры. И к последнему левелу все оформления уровней будут висеть в памяти? Вот мне и показалось, что может быть было бы выгодно удалять ненужную графику (оформления левелов) из кэша, если это конечно не повлечёт за собой каких-то других проблем более серьезных, чем ссылание AntActor`ов и глюков со сборщиком мусора.

Виталий
4 Августа 2012
— 13:57
#

Объясните кто-нибудь. Если я растеризирую мувик Hero, у которого есть child клип "hand", как мне теперь этот хенд вращать? Или при растеризации это невозможно?

Heartless
10 Августа 2012
— 23:23
#

@Heartless, после растеризации вложенные мувики становятся одним целым. Чтобы получить нужный вам эффект, растеризируйте клип Hero и Hand отдельно, а вложенность восстанавливайте через код, например:

var hero:AntActor = new AntActor();
hero.addAnim("...");

var hand:AntActor = new AntActor();
hand.addAnim("...");

// Вкладываем руку в героя...
hero.addChild(hand);

// Вращаем руку героя...
hand.angle += 1;

Ant.Karlov
11 Августа 2012
— 00:17
#

>И помните еще о том, что в отладочном плеере обычная флешка может занимать почти вдвое больше оперативной памяти т.к. отладочный плеер хранит много лишней информации о работе программы для отладки.

Хм, решил проверить на своей игре: что откомпилированная флешка, что готовая в диспетчере задач, и в профайлере выдают одинаковые цифры. Мб что-то не так понял, подскажите пожалуйста?

Виталий
14 Августа 2012
— 02:20
#

Хм, запустил флешку в Chrome, количество съедаемой памяти упало в 10 раз, а страница на которой была запущена флешка стала съедать оперативной памяти в диспетчере в 3 раза меньше. Но я так понимаю хромовские показатели не совсем истинный результат.

Виталий
15 Августа 2012
— 15:30
#

@Виталий, дело в том, что существует две версии плеера, первая версия "Flash Player Release" — такой флеш плеер стоит в основном у пользователей и он не имеет отладочных функций, то есть если в таком плеере ваша игра вдруг упадет, то пользователь увидит только неправильно работающую или частично зависшую игру и никаких сообщений об ошибках. В таком плеере не работает trace() и много еще чего отсутствует. Но за счет отсутствия этого функционала, флешки в таком плеере работают быстрее и оперативной памяти нужно меньше.

Вторая версия плеера это "Flash Player Debug" — вот этот плеер более тормозной и неповоротлив в работе так как хранит больше информации о работе флешки и в случае критической ошибки обязательно выведет окно о том какая и где ошибка возникла. Такой плеер разработчики обычно ставят сами, либо он идет в комплекте с Flash IDE.

То что у вас в Хроме совсем другой результат как раз говорит о том, что в Хроме у вас релизная версия плеера, а плагин и standalone версия плеера — отладочные. Хром не использует системный плагин флеш плеера, а использует "свою версию" плеера.

В игровой консоли, о которой я рассказывал ранее, есть команда "-sys" там можно видеть версию плеера включая опцию отладки, так что посмотрев пример с консолью вы сможете узнать какой где у вас плеер ;)

Ant.Karlov
16 Августа 2012
— 21:29
#

Получается,чтобы растеризованная анимация совпадала с оригинальной, надо чтобы в дочерних клипах количество кадров анимации было таким же как и в основном? Т.е. если у меня есть анимация зверька на 50 кадров, а внутри неё клип-глаз с анимацией моргания в 20 кадров, то,чтобы всё было ок надо растянуть анимацию моргания до 50?

cot45
19 Августа 2012
— 02:26
#

@cot45, в текущей реалзиации, да — желательно чтобы количество кадров у вложенных клипов соответствовало количеству кадров оригинального клипа. Это конечно не удобно. Но если честно, то мне ни разу не доводилось делать анимацию героев таким образом чтобы они состояли из разных клипов которые анимированны отдельно :)

Если нужно, то я могу доработать классы таким образом чтобы при растерезации кадры вложенных клипов просто переключались на один кадр и можно не привязываться к количеству кадров основного клипа, анимация внутренних классов будет проигрываться по кругу во время растеризации.

Ant.Karlov
21 Августа 2012
— 21:43
#

Антон, и всё таки, очень интересно получается с анимациями. Если кто-то вдруг решит создавать игру, где каждый левел будет иметь своё уникальное, разительно отличающееся от остальных левелов, графическое оформление. Допустим в игре 20 левелов, а отрастеризированное оформление для одного левела будет занимать 10 мб. В таком случае, разве не логично, чтобы в памяти по прохождении 10 левелов в памяти висело вместо 100 мб оформлений (10 левелов * 10 мб), а всего лишь 10 мб, для текущего левела? Я попытался порыться в классах, и стало ясно, чтобы удалить ненужную графику следует заnullить анимации в объекте _animations в AntAnimCache, и заnullить анимации в AntActor`ах. Если очистить _animations сразу после создания кэша, всё впорядке - память освобождаётся. А вот если очистить _animations из кэша после создания AntActor`а (какого-либо) и очистить _animations AntActor`а, то память конечно освобождается, но почему-то не до конца (наверное какие-то из ссылок я всё же незаметил заnullить).
Антон, было бы замечательно, если бы в Anthill`e была бы какая-то возможность избавиться от ненужных анимаций (конечно, если вышенаписанное имеет какой-то здравый смысл).

Виталий
2 Сентября 2012
— 04:47
#

@Виталий, все дело в том, что данные классы не предназначены для кэширования графики игровых уровней. Данные классы следует использовать для кэширования объектов и персонажий которые используются от уровня к уровню. А для растеризации уровней следует использовать немного иной подход. Например разделять все пространство игрового уровня на тайлы (небольшие битмапы) и зарисовывать соответствующую часть уровня в эти тайлы. Для каждого из уровней использовать одни и теже тайлы (то есть при растеризации делать отпечатки в тайлы из предыдущего уровня) и таким образом в памяти не будет копится мусор. В Anthill уже реализованы классы для кэширования игровых уровней.

Чтобы корректно удалить битмапы из памяти, помимо зануления указателей следует для BitmapData вызывать метод dispose() прежде чем занулить на него указатель.

В Anthill уже реализована возможность удалять и кэшировать при необходимости анимации которые используются редко, но при этом у разработчика возникает задача следить за тем чтобы нужные для объектов анимации были в кэше.

Ant.Karlov
2 Сентября 2012
— 12:49
#

Антон, спасибо большое за разъяснение. Применил к своему проекту, не могу нарадоваться!) А главное, что не нужно думать о мусоре)

Виталий
3 Сентября 2012
— 00:36
#

Антон, мне кажется, полезной функцией для анимаций было бы добавление точек её остановки. При работе с MovieClip было удобно в некторых кадрах анимации прописывать stop(), чтобы потом удобно работалось c MovieClip (хотя конечно есть альтернатива с переключением анимаций). Работа состоит в следующем:
var antActor:AntActor = new AntActor();
antActor.addAnimFromCache("robot", null, true);
antActor.setStopFrames(7,20);
antActor.play();
Т.е. при проигрывании анимация замрёт на седьмом кадре, а если вновь прописать play() доиграет до 20-го и вновь замрёт.
Я конечно же сам для себя класс доработал. Но думаю такая функция лишней не будет

Виталий
11 Сентября 2012
— 21:55
#

очень интересно! но, но все же есть. у бтмапа есть проблема падения качества, но впринцепе если не крутить и не зумить клип. так вот и я сейчас сталкнулся с тем , что у меня упало качество графики так как я использую маштаб в своем приложении. есть ли какая то хитрасть позволяющая использовать зум, с минимальными потерями качества, например как в социальной игре зомби ферма.

саша
25 Сентября 2012
— 20:09
#

@саша,

> есть ли какая то хитрасть позволяющая использовать зум, с минимальными потерями качества,

Битмап — это просто набор пикселий, и не возможно изменять размер пиксельного изображения без потери качества (четкости). В качестве хитрости можно только кэшировать вектор в соответствующем размере для каждого шага увеличения/приближения. Так же, например сделано в Google Maps.

Ant.Karlov
28 Сентября 2012
— 17:27
#

@Ant.Karlov,

Скорее всего отрисовка при таком методе не совсем будет динамической. Я хочу сказать, что каждый шаг при зумировании будет отрисовывать битмап.
Или то что ранее отрисовано можно хранить в кеше?
Или сразу все возможные варианты зума загнать в кеш а уже оттуда показывать динамически))

Саша
1 Октября 2012
— 16:26
#

и немного глупый вопрос, что означает эта строка, что происходит?

clipsPerStep = (clipsPerStep == 1) ? 2 : clipsPerStep;

Саша
2 Октября 2012
— 16:53
#

Вот такая ошибка вылазит:
Error: AntAnimCache::getAnim() - ERROR: Missing animation "EurKnight_obj".
at animation::AntAnimCache/getAnim()
at animation::AntActor/addAnimFromCache()

Появляется когда пытаюсь растеризовать классы, находящиеся в пакетах (packege).

Не подскажите, в чем может быть проблема? Спасибо.

Андрей
10 Октября 2012
— 18:44
#

@Саша, данная строчка — это сокращенное условие, здесь происходит проверка количества клипов которые обрабатываются за один проход, если количество клипов за проход равно 1, то присваивается двойка чтобы цикл гарантировано отработал одну итерацию. Если переделать условие внутри цикла то можно избавится от данной строки.

Ant.Karlov
11 Октября 2012
— 00:43
#

@Андрей, данная ошибка возникает потому что вы пытаетесь достать из кэша анимацию которой не существует.

Вероятнее всего причиной тому создание графических объектов до заполнения кэша растеризированными клипами.

Убедитесь в том, что графические объекты создаются после того как завершена растеризация клипов, так как на растерезацию требуется время в течении которого флеш продолжает работу.

Перехватить завершение растеризации можно поместив указатель на метод обработчик в AntAnimCache::onCompleteCallback.

Ant.Karlov
11 Октября 2012
— 00:48
#

Дело в том, что такая ошибка появляется только в том случае, если класс находится в пакете.

Т.е. так работает нормально: cache.addClipToCacheQueue(EurKnight_obj);

А вот так уже работать отказывается: cache.addClipToCacheQueue(heroes.EurKnight_obj);

Вообщем-то решение напрашивается само собой - не использовать пакеты классов, а хранить все в корне. :) Но это крайне не удобно.

Андрей
11 Октября 2012
— 01:39
#

Добрый день.
Столкнулся с такой проблемой:
Нужно перед кешированием сменить цвет кожи и одежду персонажа которые находятся в мувиклипах вложенных в персонажа.
Может можно сначала добавить клип персонажа на сцену или еще как-то, чтобы сначала кастомизировать его, а потом уже закешировать?

Евгений
27 Декабря 2012
— 02:41
#

Или может можно дописать метод обращения к внутренним мувиклипам, и дописать кастомизацию в процесс кеширования.

Евгений
27 Декабря 2012
— 02:43
#

Все кажется разобрался:
Можно все манипуляции вставить перед

// Кэшируем клип в AntAnim.as =)

Евгений
27 Декабря 2012
— 02:55
#

@Евгений, да, необходимую кастомизацию клипов можно делать непосредственно перед кэшированием в AntAnim.

Но я бы вам посоветовал сделать альтернативный метод кэширования который бы принимал не класс клипа, а непосредственно экземпляр класса клипа. То есть в таком случае для растеризации вы передаете непосредственно уже кастомизированный клип ;)

Ant.Karlov
27 Декабря 2012
— 04:15
#

т.е. как-то так
var player1:Player=new Player()
var player2:Player=new Player()
player1.head.gotoAndStop(3)
player2.head.gotoAndStop(2)
cache.addClipsToCacheQueue( [ "player1", "player2", "e.t.c" ] );
нужно в таком случае править что-то в AntAnimCache ?

Евгений
27 Декабря 2012
— 04:42
#

а вместо public function cacheAnim(clipClass:Class):AntAnim
пишем
public function addClipToCacheQueue(clipClass:Player):void
остальной код в частности
var key:String = getQualifiedClassName(clipClass);
будет по идее работать, правильно?

Евгений
27 Декабря 2012
— 13:29
#

Попробовал и не получилось=
изменил в кешировании напрямую отправку клипа
anim.cacheFromClip(clip);
но потом когда пытаешься получить анимацию ее почему-то не находит=

Евгений
29 Декабря 2012
— 11:52
#

разобрался, все правильно было в коде, а с добавлением в библиотеку натупил и пытался достать из кеша то чего там не было %)

Евгений
30 Декабря 2012
— 19:42
#

После добавления порядка 30 разных анимированных персонажей очень сильно проседают фпс на твинах=

Евгений
3 Января 2013
— 22:09
#

C твинами разобрался, а вот память жрет беспощадно=

Евгений
5 Января 2013
— 17:31
#

а в браузере в 5 раз меньше памяти берет совсем не понятно ничего что происходит %)

Евгений
5 Января 2013
— 17:53
#

@Евгений, скорее всего у вас непосредственно твины сожрали производительность. Следует использовать наиболее оптимизированные библиотеки для реализации твинов.

Да, кэширование жрет память, но память как паравило более дешевый ресурс чем процессорное время. Но все же за объемом занимаемой памяти следует следить.

> а в браузере в 5 раз меньше памяти берет совсем не понятно ничего что происходит %)

В браузере скорее всего у вас релизный плеер (не дебаг), а в релизном плеере не ведется сбор информации на случай падения флешки и соответственно в релизном плеере приложение занимает для своей работы меньше памяти. Так же в релизном плеере флешки быстрее работают — меньше тормозят и имеют больший запас производительности ;)

Ant.Karlov
6 Января 2013
— 10:34
#

Раньше как-то не обращал внимания на разницу в дебаг плеере и в релизном, спасибо за информацию :)

Евгений
8 Января 2013
— 03:07
#

Добрый день!

У меня в проекте есть много мувиков, с текстовыми полями. Заполняются данными с сервера. Таблица.

Можно ли использовать данные классы для растеризации мувиков? Так как показ большого числа мувиков и их скроллирование много памяти съедает.

Саша
28 Февраля 2013
— 11:03
#

@Саша, нет, в такой схеме как вам нужна — данные классы не подойдут для работы, так как текстовые поля будут преобразованы в картинку и вы не сможете изменять их содержимое.

Но вы можете пойти немного другим путем: удалите текстовые поля из клипов и добавляйте их программно непосредственно в AntActor уже после того анимации будут растерезированны. Тогда возможности сохранятся, а графика будет рендерится быстрее.

Ant.Karlov
28 Февраля 2013
— 15:28
#

@Ant.Karlov спасибо. Попробую так сделать

Саша
28 Февраля 2013
— 16:04
#

Антон, ОГРОМНОЕ СПАСИБО за код. Сегодня разобрался, поставил,растеризировал все, нагрузка на проц стала в 3 раза меньше.
А еще отдельное спасибо за такой ништяк как упр-е скоростью анимации.

revan
2 Марта 2013
— 23:18
#

Здраствуйте Антон.
Пробовал просто подключать Ваш класс все отлично работает, а вот в связке FlashDevelop+swc
не хочит выводить.
Хочу привязать swc, в ней есть клип Soldier_mc.

public static var hero:Soldier_mc=new Soldier_mc();

пишу
cache.addClipsToCacheQueue(["Soldier_mc"]);
ответ
[Fault] exception, information=TypeError: Error #1034: Type Coercion failed: cannot convert "Soldier_mc" to Class.

пишу
cache.addClipsToCacheQueue(["hero"]);
ответ
[Fault] exception, information=TypeError: Error #1034: Type Coercion failed: cannot convert "hero" to Class.

просто клип на экран выводиться
addChild(hero);
а вот кэшироваться не хочет.

Толик
18 Мая 2013
— 18:52
#

Все решилось , я не посмотрел основной файл с примерами, где написано, что теперь передаются сами классы а не их строковые представления.

Толик
18 Мая 2013
— 23:47
#

Антон, не знаю, читаешь ли ты комменты к старым статьям, но я был бы очень благодарен за ответ.

В классе AntAnimCache, а потом и в AntActor ты используешь конструкцию function.apply

Более конкретно, например, вот:
(onProgressCallback as Function).apply(this, [ (_curProcessingItem + 1) / _cacheQueue.length ]);
это строка из AntAnimCache

Зачем используется именно такая конструкция, если точно так же работает:
onProgressCallback ((_curProcessingItem + 1) / _cacheQueue.length);

Буду очень благодарен за ответ, так как не могу понять.

jarofed
7 Ноября 2013
— 17:10
#

@jarofed, в целом способ вызова метода не принципиален. Но у apply() есть пару преимуществ:

1. Первым аргументом для apply() можно указать значение thisObject, используемого в любой функции, вызываемой ActionScript.

2. Вторым аргументом можно задать массив который будет передан в вызываемый метод как список параметров — это удобно в том случае если количество аргументов не известно до конца.

Для меня метод apply() еще удобнее за счет того, что так лучше читается вызов метода по указателю, поэтому я взял для себя за правило использовать его вместо прямого вызова метода.

Обычно я слежу за всеми комментариями для всех записей :) Отвечаю право не всегда своевременно в силу разных причин, к сожалению.

Ant.Karlov
9 Ноября 2013
— 14:33
#

Спасибо. Еще один вопрос:

почему используется конструкция
onProgressCallback as Function

если просто onProgressCallback так же работает?

jarofed
14 Ноября 2013
— 12:28
#

Спасибо за статью, оказалась крайне кстати. Подскажи, можно ли каким-то образом обратиться к конкретному мувику (например, body_mc) внутри каждой из добавленных из кеша анимаций.

При обращении просто в духе:
myObj:AntActor = new AntActor();
myObj.addAnimFromCache("Obj_Run", "run");
trace(myObj.body_mc);

Выдает ошибку...

volcanoflash
28 Декабря 2013
— 01:14
#

Тоже столкнулся с проблемой как у volcanoflash. Вобщем у меня у каждого состояния героя есть мувик Hero_weapon, который служит контейнером для разного рода оружия с инвентаря. Пока было все в векторе, обращался я к нему по имени. Теперь-же стала проблема как реализовать такой контейнер в нужных координатах в откешированной анимации. Есть какие соображения по этому поводу? Буду благодарен.

Profik
30 Сентября 2014
— 20:28
#