Альтернатива “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%

(35) Хитрых на тему «Альтернатива “Cache as bitmap” - разгружаем процессор»

  1. shaman4d

    Интересно. И что на размерах 1200 на 1200 не тормозит? И кстати ты не замерял какой расход памяти при cacheAsBitmap и при твоем способе с BitmapData?

  2. Platon

    Очень актуально, как раз вопрос такой возник :) Спасибо.

  3. Stormit

    Тормозов нет, по крайней мере не больше чем от обычной растровой картинки. Про память не скажу, как это проверить?

  4. shaman4d

    Память можно посмотреть в Task Manager сколько он выделяет на плеер. (обычно также можно смотреть утечки памяти)

  5. Stormit

    Это наверное фишки Flex?
    Я пока делаю всё во Flash IDE и FlashDevelop

  6. shaman4d

    Task Manager это программа Windows. Нажми вместе клавиши Ctrl+Shift+Esc и она откроется.

  7. Stormit

    Это значит, пора на отдых :)

  8. Stepan

    а вообще идея интересная, спасибо!

  9. Yaxxxa

    Идея очень интересная. Возможности флэша меня поражают все больше и больше

  10. Psih

    mem_txt.text = Number(System.totalMemory / 1024 / 1024 ).toFixed( 2 ) + ” Mb”;

  11. Alexsab

    я такой же метод давно уже использую.
    Например, если растворение сложного объекта происходит медленно, то и флешка начинает тормозить, а если его сделать bitmap-ом, то все проходит на ура!
    еще в конце функции у себя использую
    cont.swapDepths(some);
    т.е. меняю местами, чтобы к картинке заменяющей объект обращаться по тому же уровню, у тебя это можно вставить, если пользователь не указал “bdLev”.

  12. Paki

    Такой метод годится разве что для бекграундов и не анимированых обэктов. А какие идеи для клипов с внутреней анимацией?

  13. mad-beaver

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

  14. Stormit
  15. Zloba

    Чето я непонял а как кэшировать то?

  16. Zloba

    Кстате чем cached on spritesheet от cached as frames отличается?

  17. Stormit

    По первой ссылке можно исходники скачать.
    Типа “cached as frames” быстрее - по FPS видно (слева вверху).

  18. Paki

    4Stormit: спасибо. Очень полезная штука.

  19. Zloba

    cached as frames Это как?

  20. Stormit

    Не знаю, это же не мой код.
    Наверное автор так назвал свою методику, судя по названию - делал снимок каждого кадра и потом их программно отображал. Ну а spritesheet - таблица спрайтов. Раз таблица, может и внутреннюю анимацию поддерживает. Это всё догадки - код смотреть нужно и пробовать.

  21. Paki

    Автор предлагает 2 варианта либо делать битмап снимок каждого кадра и потом их по очереди показывать, либо делать снимки всех кадров на одной битмапдате (типа как кадры на кинопленке) и потом просто менять область отображения битмапдаты

  22. Bulat

    А покажи пожалуйста как оставлять следы от взывов, осколки , пятна и можно как нить сделать на флаше игровой онлайн клиент раскажи плиз

  23. BerG

    общение клиент-сервер осуществляется простыми запросами на сервер с параметрами, а сервер (например какойнибудь CGI-скрипт) обрабатывает и возвращает ответ, который ты можешь разобрать в своем ActionScripte. Например, координаты персонажей.

    А вообще, интересно узнать, вставала ли перед автором подобная задача, и статью про то, как он ее решил

  24. Stormit

    С мультиплеером я пока не работал.
    Взрывы и осколки оставляются просто: объект BitmapData с вашим растрированным фоном вызывает метод draw(), куда передаётся символ воронки от взрыва или символ с осколками. Дополнительно, в метод draw() можно передать объект Matrix, в котором задаются координаты, поворот и другие трансформации.

  25. Bulat

    спс зато ответы !!!
    Пиши плиз чаще!!!
    Очень кульный блог все написано понят и с хорошими примерами автор отзывтивый пока))) Молодец так держать!!!
    CGI скиньте плиз ссылочку где об этом можно узнать

  26. BerG

    Самый простой для освоения язык будет PHP
    http://www.php.ru/
    Но все равно, если игра делается на одном энтузиазме, при нехватке знаний, чаще всего она так и не рождается на свет.

  27. Bulat

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

  28. Tarwin

    We’ve got examples and source of the same kind of thing for animations here: http://blog.touchmypixel.com/archives/22

    Have fun!

  29. Igor

    Вообще то этот метод не особо подходит для использования в каждом такте. Будет очень тормозить. Да, cache as bitmap действительно не работает на больших клипах, но в одном из проектов мы использовали bitmapы, и это очень ресурсоёмко, каждый кадр создавать bitmap, и рисовать в него. Всё таки для больших клипов лучше использовать png.

  30. Stormit

    Так он задуман для статики. А для анимации достаточно ОДИН раз пробежаться по клипам, сделать снимки и потом показывать их по очереди.

  31. Zergius

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

  32. Stormit

    Я не знаю, но гугл подсказал

  33. Zergius

    Спасибо, а то я как его ни спрашивал, он мне все какую-то фигню выдавал. Ближе всего к теме ваш блог оказался :)

  34. maklaus

    Отличная статья! =)

    Правда пришлось переписать под 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);
    }

  35. Straga

    Цитирую:
    Так он задуман для статики. А для анимации достаточно ОДИН раз пробежаться по клипам, сделать снимки и потом показывать их по очереди.

    А как это сделать? У меня в мувиклипе есть куча меток (названий кадра). Это же как извращаться нужно?
    У меня есть некий класс Враг. Внутри него мувиклип Тело (с ним я и работаю). В Теле есть еще пару мувиклипов(Рука, Нога, Тело…). В мувиклипе Рука каждый кадр это новый вид руки (допустим для персонажа Враг, ЗлойВраг, Вражище). Но если пройтись по каждому кадру Тела и поменять всем его детям номер кадра допустим на 2, то оно отображается как надо только пока не сменился кадр. А каждый раз менять все-таки напряжно (10 врагов это 100 гоутуЭндСтоп). Есть идеи? )
    Заранее благодарен

Оставить комментарий