Альтернатива “Cache as bitmap” - разгружаем процессор
27.03.2009, автор Stormit, рубрики: ActionScriptНаверное многие знают и умело используют такую замечательную возможность во флеше как “Cache as bitmap“. При этом, с клипа делается виртуальный снимок в виде картинки и вектор не пересчитывается для отрисовки каждый кадр. Можно рисовать графику в векторе прямо во флеше, потом в панели свойств включить кэширование и на выходе плеер получает растровую картинку. Очень удобно, можно быстро вносить изменения и сразу любоваться результатом минуя фазу экспорта/импорта в PNG. Плюс ко всему - экономия траффика.
Звучит приятно и обычно всё хорошо работает, но я столкнулся с тем, что с клипами больших размеров, тормоза частично остаются. В одной платформенной игре у меня был фон 1200х1200, который лежал на заднем плане и должен был просто смещаться, пока персонаж бегает по уровню. Так вот, анимация происходила с небольшими рывками. Такое впечатление, что флэш время от времени пересчитывает данные и делает “обновлённый” снимок с клипа. Так или иначе, но факт имел место и такие тормоза были. Это решалось заменой векторного фона растровым (PNG), но такая флешка весила очень много.
Но не зря мы все так любим флеш - у него в арсенале достаточно инструментов чтобы решить эту проблему. Мы снимаем с флеша ответственность по растеризации векторной графики и берём её на себя. Будем использовать BitmapData и маленькую функцию, приведённую ниже. В добавок мы получаем возможность обрабатывать нашу картинку как игре угодно: добавить следы от взрыва, осколки, пятна.
На примере ниже можно увидеть как это работает. Большое количество градиентов и объектов нас больше не пугают, потому что пикселы отрисовать в разы быстрее чем рассчитать вектор по точкам. Так будет видеть flashPlayer наш клип:
Сам код функции такой:
function rasterizeMovieClip(obj, bdLev) { var _par = obj._parent; var _lev = (bdLev) ? bdLev : _par.getNextHighestDepth(); var bd_mc = _par.createEmptyMovieClip(obj._name + "BD", _lev); var bd = new flash.display.BitmapData(obj._width, obj._height, true, 0x00000000); bd.draw(obj); bd_mc.attachBitmap(bd,bd_mc.getNextHighestDepth()); bd_mc._x = obj._x; bd_mc._y = obj._y; obj.swapDepths(_par.getNextHighestDepth()); obj.removeMovieClip(); }
Её можно разместить в _root или сделать статическим методом утилитного класса. При вызове функции, ей нужно передать ссылку на MovieClip, который нужно растеризовать, и уровень (глубина слоя - необязательный параметр). Теперь клип можно поворачивать и масштабировать не боясь что будет новый пересчёт данных и дополнительные тормоза.
Проверено на практике.
Интересно на 56%




Интересно. И что на размерах 1200 на 1200 не тормозит? И кстати ты не замерял какой расход памяти при cacheAsBitmap и при твоем способе с BitmapData?
Очень актуально, как раз вопрос такой возник
Спасибо.
Тормозов нет, по крайней мере не больше чем от обычной растровой картинки. Про память не скажу, как это проверить?
Память можно посмотреть в Task Manager сколько он выделяет на плеер. (обычно также можно смотреть утечки памяти)
Это наверное фишки Flex?
Я пока делаю всё во Flash IDE и FlashDevelop
Task Manager это программа Windows. Нажми вместе клавиши Ctrl+Shift+Esc и она откроется.
Это значит, пора на отдых
а вообще идея интересная, спасибо!
Идея очень интересная. Возможности флэша меня поражают все больше и больше
mem_txt.text = Number(System.totalMemory / 1024 / 1024 ).toFixed( 2 ) + ” Mb”;
я такой же метод давно уже использую.
Например, если растворение сложного объекта происходит медленно, то и флешка начинает тормозить, а если его сделать bitmap-ом, то все проходит на ура!
еще в конце функции у себя использую
cont.swapDepths(some);
т.е. меняю местами, чтобы к картинке заменяющей объект обращаться по тому же уровню, у тебя это можно вставить, если пользователь не указал “bdLev”.
Такой метод годится разве что для бекграундов и не анимированых обэктов. А какие идеи для клипов с внутреней анимацией?
Такой метод прекрасно годится и для объектов с анимацией.
Просто при загрузке игры рендерим наш клип в серию битмэпов, затем делаем их все невидимыми и добавляем в качестве детей в производный от Sprite объект, который каждый кадр будет делать один из битмэпов видимым. Эмулируем MovieClip, так сказать.
Результат - эффекты практически любой сложности при отсутствии тормозов во время игры.
Кэширование анимации
А вот и сам пример
Чето я непонял а как кэшировать то?
Кстате чем cached on spritesheet от cached as frames отличается?
По первой ссылке можно исходники скачать.
Типа “cached as frames” быстрее - по FPS видно (слева вверху).
4Stormit: спасибо. Очень полезная штука.
cached as frames Это как?
Не знаю, это же не мой код.
Наверное автор так назвал свою методику, судя по названию - делал снимок каждого кадра и потом их программно отображал. Ну а spritesheet - таблица спрайтов. Раз таблица, может и внутреннюю анимацию поддерживает. Это всё догадки - код смотреть нужно и пробовать.
Автор предлагает 2 варианта либо делать битмап снимок каждого кадра и потом их по очереди показывать, либо делать снимки всех кадров на одной битмапдате (типа как кадры на кинопленке) и потом просто менять область отображения битмапдаты
А покажи пожалуйста как оставлять следы от взывов, осколки , пятна и можно как нить сделать на флаше игровой онлайн клиент раскажи плиз
общение клиент-сервер осуществляется простыми запросами на сервер с параметрами, а сервер (например какойнибудь CGI-скрипт) обрабатывает и возвращает ответ, который ты можешь разобрать в своем ActionScripte. Например, координаты персонажей.
А вообще, интересно узнать, вставала ли перед автором подобная задача, и статью про то, как он ее решил
С мультиплеером я пока не работал.
Взрывы и осколки оставляются просто: объект BitmapData с вашим растрированным фоном вызывает метод draw(), куда передаётся символ воронки от взрыва или символ с осколками. Дополнительно, в метод draw() можно передать объект Matrix, в котором задаются координаты, поворот и другие трансформации.
спс зато ответы !!!
Пиши плиз чаще!!!
Очень кульный блог все написано понят и с хорошими примерами автор отзывтивый пока))) Молодец так держать!!!
CGI скиньте плиз ссылочку где об этом можно узнать
Самый простой для освоения язык будет PHP
http://www.php.ru/
Но все равно, если игра делается на одном энтузиазме, при нехватке знаний, чаще всего она так и не рождается на свет.
хз мож родится ченить)))))))
а так я ее хочу попытася сделать толко для своего общего развития
We’ve got examples and source of the same kind of thing for animations here: http://blog.touchmypixel.com/archives/22
Have fun!
Вообще то этот метод не особо подходит для использования в каждом такте. Будет очень тормозить. Да, cache as bitmap действительно не работает на больших клипах, но в одном из проектов мы использовали bitmapы, и это очень ресурсоёмко, каждый кадр создавать bitmap, и рисовать в него. Всё таки для больших клипов лучше использовать png.
Так он задуман для статики. А для анимации достаточно ОДИН раз пробежаться по клипам, сделать снимки и потом показывать их по очереди.
Есть вопрос на смежную тему.
У меня в игре переключаются два экрана - основной и с правилами. Анимируется это переключение при помощи свойства rotationX (flash10,as3). Но эта анимация смазывает фоновую картинку, даже если вернуть rotationX в 0. Пробовал прятать исходный клип и анимировать только его bitmap-копию, но почему-то ничего не вышло. Есть какие-нибудь еще идеи по этому поводу?
Я не знаю, но гугл подсказал
Спасибо, а то я как его ни спрашивал, он мне все какую-то фигню выдавал. Ближе всего к теме ваш блог оказался
Отличная статья! =)
Правда пришлось переписать под AS3. =( Если кому интересно, пожалуйста:
public static function Rasterize(clip : Sprite):void {
var clipContentsBound:Rectangle = clip.getBounds(clip);
var bitmapData : BitmapData = new BitmapData(clipContentsBound.width, clipContentsBound.height, true, 0×00000000);
var matrix:Matrix = new Matrix();
matrix.translate( - clipContentsBound.x, - clipContentsBound.y);
bitmapData.draw(clip, matrix);
while (clip.numChildren > 0) clip.removeChildAt(0);
clip.graphics.clear();
var bitmap:Bitmap = new Bitmap(bitmapData);
bitmap.x = clipContentsBound.x;
bitmap.y = clipContentsBound.y;
clip.addChild(bitmap);
}
Цитирую:
Так он задуман для статики. А для анимации достаточно ОДИН раз пробежаться по клипам, сделать снимки и потом показывать их по очереди.
А как это сделать? У меня в мувиклипе есть куча меток (названий кадра). Это же как извращаться нужно?
У меня есть некий класс Враг. Внутри него мувиклип Тело (с ним я и работаю). В Теле есть еще пару мувиклипов(Рука, Нога, Тело…). В мувиклипе Рука каждый кадр это новый вид руки (допустим для персонажа Враг, ЗлойВраг, Вражище). Но если пройтись по каждому кадру Тела и поменять всем его детям номер кадра допустим на 2, то оно отображается как надо только пока не сменился кадр. А каждый раз менять все-таки напряжно (10 врагов это 100 гоутуЭндСтоп). Есть идеи? )
Заранее благодарен