Тотальная растеризация

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

Все началось с того, что меня вдруг неожиданно начало напрягать неравномерное движение спрайтов в игре — это такой не очень приятный эффект подергивания отдельных объектов при прокрутке игрового мира. Думаю, многие с этим сталкивались. Какого-то универсального и правильного способа кардинально решить эту проблему я так и не нашел. Есть, конечно, всякие разные костыли, которые могут капельку скрасить этот недостаток, но в конечном итоге это все не то. ? правильный выход только один: сделать ручной рендер.

Ручной рендер — это когда все MovieClip'ы переводятся в растр и хранятся в BitmapData, а при рендере рисуются в буффер, который привязан к обычному Bitmap.

Благодаря ручному рендеру в руках программиста оказывается немного больше возможностей для создания хэндмэйд эффектов типа блюра, затуханий, моделирования теней и т.п. А на стандартный флеш рендер ложится только одна задача — нарисовать подготовленный нами битмап. То есть, в итоге всякие проблемы подергивания объектов должны решиться раз и на всегда! ? перед мной встала задача выяснить, насколько это быстро работает и как много нужно для этого сделать. Впрочем, сомнения в том, что это работает действительно быстро у меня почти не было с самого начала.

Ранее я уже писал о растеризации/кэшировании клипов в битмапы и приводил ссылку на найденное мною решение. С тех пор, конечно, оригинальный класс растеризации клипов я сильно изменил, выкинув все лишнее, поправил баги, добавил функционал и немного оптимизировал его. Свою модификацию класса Animation я использую во всех своих играх и меня все устраивает, кроме стандартных flash.display.Sprite и flash.display.Bitmap, которые пока являются неотъемлемой основой каждого графического спрайта :( ? вот похоже настало самое время написать свой Framework для работы с графикой и красиво прикрутить к нему все те утилитные классы, которые у меня постоянно кочуют из проекта в проект. Но первым делом нужно собрать пробный ручной рендер и взвесить все за и против такой работы.

Для ручного рендера используются в основном стандартные методы от BitmapData типа draw(), copyPixels() и тому подобное. Поэтому я решил не изобретать велосипед и посмотреть устройство одного из известных движков Flixel. Его архитектура для меня оказалась простой и понятной, возможно потому что с похожими архитектурами я уже сталкивался на Delphi в каком-то 2D движке и в 3D движке GLScene (под архитектурой я понимаю иерархию классов и их взаимодействие). Для быстроты эксперимента мне нужно было сделать простую архитектуру тестового движка с возможностью создания кэша анимаций и последующим использованием анимированных спрайтов из кэша, которые движок сам бы процессил и рендерил. Тут я по неосторожности взял часть кода из Flixel и часть кода из своего Animation, и все это слепил в кучу, после чего обнаружил в коде Flixel несколько слабых мест:

  1. Отсутствие возможности регулирования скорости анимации;
  2. Все кадры одной анимации находятся в одном BitmapData, откуда поочередно копируются методом copyPixels() — что получается в 3-4 раза медленнее, чем передавать указатель на отдельный BitmapData для каждого кадра;
  3. Отладочный битмап для отрисовки рамок спрайта создается независимо от того, находится ли движок в режиме отладки, что увеличивает размер занимаемой памяти вдвое (!);
  4. Не умеет создавать/растеризировать ленту анимаций из MovieClip, с учетом смещений графики относительно нулевой координаты клипа.

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

Согласно возможностям движка Flixel недостаток номер 2 оправдан тем, что с текущим кадром могут быть произведены дополнительные трасформации. Например изменение цвета или рисование прямо на спрайте, поэтому спрайт не получает указателя на текущий кадр из общей ленты кадров, а именно копируется через copyPixels(), чтобы применяя трансформации не испортить оригинал.

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

В итоге архитектура классов для эксперимента была взята от Flixel, а кэширование и воспроизведение анимаций взято от модифицированного мною Animation.as, который изначально был написан автором touchmypixel.com.

Тестирование 1

Для честности эксперемента все флешки располагаются на отдельных веб-страницах. В своих тестированиях я использую два разных браузера Safari и Chrome оба под MacOS. В Safari установлен Flash Player v10.1, в Chrome установлен Flash Player v10.2 с аппаратной поддержкой. Оба плеера обычные, не Debug. Профайлер в демках открывается кликом правой кнопки мыши и выбором в контекстном меню пункта: «Show Profiler».

Кстати, флешки в FlashPlayer 10.2 под Chrome иногда мерцают. То есть экран становится на 0.5-1 секунду черным, а потом вновь появляется картинка. ?нтервал между мерцанием произвольный. Пока мерцание заметил только в своих флешках, в других флешках на игровых порталах такие глюки мне не встречались, правда я почти и не пользуюсь хромом. Так же в тестирование я планировал включить и FireFox, но после очередного автоматического обновления он перестал у меня запускаться.

Размер экрана первого тестового приложения 640x480 и framerate 60. Приложение имеет 200 спрайтов с активной анимацией, спрайты двигаются в произвольном направлении с произвольным отражением и с произвольным вращением + сглаживание.

Первая Флешка использует ручное кэширование: все объекты на сцене — это обычные Sprite, в который вложен Bitmap, которому в свою очередь каждый кадр присваивается новый BitmapData из заранее созданной ленты кадров согласно текущему номеру кадра. Смотрим →

Мои результаты:
Safari: 15 fps — Постоянно колеблится от 14 до 21 и со временем средний fps растет :)
Chrome: 13 fps

Вторая флешка использует так же ручное кэширование: на сцене всего один Sprite, в который вложен Bitmap размером с 640x480. Все игровые спрайты являются невизуальными классами для Flash и рендерятся в основной буффер методом draw(). Смотрим →

Мои результаты:
Safari: 19 fps
Chrome: 24 fps

Тестирование 2

Размер экрана второго тестового приложения 640x480 и framerate 60. Приложение имеет 600 спрайтов с активной анимацией, которые двигаются в произвольном направлении без отражения, вращения и сглаживания.

Первая флешка. Смотрим →

Мои результаты:
Safari: 19 fps
Chrome: 59 fps — Аппаратная поддержка делает свое дело.

Вторая флешка. Поскольку в данном примере не используется никаких трансформаций для спрайта, то все спрайты рендерятся в буффер методом copyPixels(). Смотрим →

Мои результаты:
Safari: 60 fps
Chrome: 60 fps

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

Мои результаты:
Safari: 28 fps
Chrome: 33 fps

Выводы

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

Оставался только один не понятный для меня вопрос: почему при ручном рендере приложение более чем в два раза больше занимает оперативной памяти!? Но, когда я занялся поиском ответа на свой вопрос, то обнаружил, что у меня без надобности создаются дополнительные пустые BitmapData для отрисовки отладочных рамок вокруг спрайтов. Сверив код, оказалось, что и во Flixel эти дополнительные BitmapData тоже создаются независимо от того, включен режим отрисовки рамок или нет. В общем бездумный копипаст чужого кода никогда к хорошему не приводит :) Проблема с памятью легко решилась.

Ура! Все используем ручной рендер!?

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

Я бы тоже с радостью использовал Flixel, но посмотрев на него изнутри, он оказался мало подходящим для моих нужд: невозможность кэшировать клипы — это самый существенный минус для меня. Так же мне не нужна элементарная физика, которой обладает Flixel. ? попиксельная проверка столкновений с использованием hitTest() меня тоже немного смутила. На мой взгляд Flixel имеет понятную и простую архитектуру, а так же еще много того, что мне совсем не нужно, но при этом он не имеет то, что я привык активно использовать.

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

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

?сходники эксперементов я решил не выкладывать. Про кэширование клипов в набор растровых картинок я еще обязательно подробно с примерами и исходниками расскажу в одной из следующих записях. Ну, а пример с ручным рендером — это фактически гибрид рендера от Flixel с классом анимации от touchmypixel.com.

Конфигурация компьютера, на котором я проводил тесты:
iMac"24, 2.4ГГц Intel Core Duo, 4Гб RAM, MacOSX 10.6.7

Если ваши результаты демок радикально отличаются от моих, дайте знать — интересно сравнить ;)

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

?з вектора в растр
Решение проблемы производительности во Flash

 

Спасибо, интересный пост!
Тоже пишу фреймвор.Правда он связан с физикой, но не связан с отрисовкой.
Думаю можно совмещать стандартный рендер и ручной. Стандартный для меню и прочего, ручной - для отрисовки объектов в игре.

Midnight
17 Апреля 2011
— 01:40
#

Смотреть на ДРАКОНЧ?КОВ очень завораживает)

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

Вадим М.
17 Апреля 2011
— 02:04
#

> а вот приделать кнопки, выводить тексты и вообще собрать полноценный интерфейс уже не получится привычными способами,

Антон, я все такие вещи вывожу поверх главного блиттингового битмапа обычными display object. Я не делал тестов, но каких-то мега просадок (просто отключив-включив временно все эти объекты) я не заметил. Ну и точно "по старому" можно делать неигровые экраны (меню-сеттинги).

Sergey Batishchev
17 Апреля 2011
— 02:12
#

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

don-andreas
17 Апреля 2011
— 03:44
#

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

don-andreas
17 Апреля 2011
— 04:16
#

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

don-andreas
17 Апреля 2011
— 04:19
#

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

genm
17 Апреля 2011
— 09:28
#

@don-andreas
Не обманывайте себя, Карлов и Хитри в нашем случае уникальные люди. (:

elmortem
17 Апреля 2011
— 09:30
#

@Midnight и @Sergey Batishchev да, точно! Про совмещение ручного рендера с привычными спрайтами, кнопками и текстовыми метками для реализации игровых меню я уже подумал после того как написал запись :)

Ant.Karlov
17 Апреля 2011
— 11:05
#

@don-andreas, вот тут на первое апреля один из товарищей пошутил на эту тему, рассказав про новый Tween Pro ;) К сожалению (или к счастью) такой возможности нет. Но если разобраться с Shape Tween то можно получить похожи эффект. Вот здесь есть хороший урок на эту тему...

Ant.Karlov
17 Апреля 2011
— 11:12
#

@don-andreas, на самом деле я знаю еще много человек которые выступают одновременно художниками и программистами. Чаще всего такие люди делают тру инди игры о которых мы мало чего знаем :) Конечно код и графика получается у всех на разном уровне, но это не отменяет того факта, что над игрой работает один человек.

MineCraft вроде тоже изначально один человек создал. Это сейчас только вокруг этой игры собралась небольшая команда, если я не ошибаюсь конечно :)

В общем тут главное желание, стремление и понимание некоторых граней...

Ant.Karlov
17 Апреля 2011
— 11:21
#

@genm, пока ковырялся с этими примерами и с пикселями, вспомнил как давным давно на Delphi пытался писать игры с использованием Canvas... Счастливое детство :)

Ant.Karlov
17 Апреля 2011
— 11:23
#

@elmortem, на самом деле таких как мы не один и не два, нас больше! :)

Ant.Karlov
17 Апреля 2011
— 11:25
#

Держи Антон:
http://blog.iainlobb.com/2010/11/bunnylandmark-new-flash-game-benchmark.html

Когда запустил последнюю демо, сразу понял о чем остальной пост =)

krylover
17 Апреля 2011
— 11:38
#

Отличная статья Антон. В скором времени мне понадобится тоже самое. Спасибо.

Zarkua
17 Апреля 2011
— 11:57
#

@krylover, зря не дочитал пост ;) По приведенной ссылки очень упрощенный бенчмарк. Там только copyPixels() и исключительно битмапы используются без каких либо анимаций и трансформаций.

Ant.Karlov
17 Апреля 2011
— 12:22
#

Антон, еще можно подождать когда выйдет Molehill, наверняка ты видел демки с M2D ?
Там хоть 100000 объектов на 60 fps рендерится)

neelts
17 Апреля 2011
— 13:38
#

Во флешпанке автор позаботился о проблеме с интерфейсом и предоставил стейдж: :)
FP.stage.addChild(myWindow);

Dragosha
17 Апреля 2011
— 15:48
#

@neelts, во Flash Player 10.2 тоже есть аппаратная поддержка и что-то как-то не очень результаты. Хочется, конечно верить, что новый плеер с Molehill будет полноценно использовать аппаратное ускорение, например на уровне с Unity3D. Но Molehill выйдет в бета тестирование только в 2012 году, а когда еще случится релиз и сколько времени еще пройдет прежде чем его установит большая часть пользователей на свои компьютеры!? В общем очень долго ждать его... :(

Ant.Karlov
17 Апреля 2011
— 16:44
#

@Dragosha, во Flixel тоже не сложно добавить указатель на stage в глобальное хранилище если нужно. В файле FlxG.as добавляем статическую публичную переменную:

public static var stage:Stage = null;

А в файле FlxGame.as в методе create(), сразу после строчки stage.frameRate = _framerate, добавляем строчку:

FlxG.stage = stage;

После чего в коде где угодно можно получить доступ к stage так же как и во FlashPunk ;)

FlxG.stage.addChild(myWindow);

Ant.Karlov
17 Апреля 2011
— 17:07
#

Растеризация наше все =).

А проверял как быть с анимацией большого размера? Мы натолкнулись на проблему связанную с тем, что большие размеры (раза в 2-3 больше дракона), с большим объемом анимации (порядка 200 кадров) легко выйдут за придел 1gb, чем подвесят флеш. Конечно при условии, что таких объектов еще несколько на сцене.

Пока нашли 2 решения:
а) запретить аниматорам рисовать большие анимированные объекты;
б) растеризация на-лету;

более элегантного решения пока не подвернулось :(

Hyzhak
17 Апреля 2011
— 20:15
#

@ant-karlov
Molehill уже в бете (я себе уже поставил). А в начале 2012 ожидается уже официальный релиз

Zaphod
17 Апреля 2011
— 20:23
#

@Hyzhak:
Мне кажется, проблема сильно преувеличена. В 1 гигабайт можно уместить изображение размером
32768 х 32768 пикселей в 32 битном ARGB формате. Это 2621 кадр размером 640х640 пикселей.
Естественно в реальности это изображение разбивается на несколько частей, ни одна видеокарта такую текстуру в памяти не создаст.

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

А аниматорам нужно руку жать, за желание рисовать большие анимированные объекты.

BuxomBerry
17 Апреля 2011
— 20:36
#

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

Hyzhak
17 Апреля 2011
— 20:49
#

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

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

BuxomBerry
17 Апреля 2011
— 21:12
#

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

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

Ant.Karlov
17 Апреля 2011
— 22:37
#

@Zaphod, если я не ошибаюсь, то то что вы себе поставили - это не бета версия, это Incubator версия для публичной демонстрации новых возможностей будущего плеера. Сам же Molehill api в данный момент доступен только избранным разработчикам. ? если мне не изменяет память, то Антон Волков на последнем FlashGAMM говорил про выход бетаверсии нового плеера в начале 2012 года, а релиз еще через пол года.

Ant.Karlov
17 Апреля 2011
— 22:52
#

@BuxomBerry большое спасибо за идею "решетки спрайтов", с удовольствием попробую ее воплотить =)

Hyzhak
18 Апреля 2011
— 01:59
#

На самом деле уже можно работать с Molehill - есть справочник и библиотека для разработки (playerglobal.swc)
ссылка 1
ссылка 2

Другое дело, что пока не вышел релиз плеера это не очень актуально.

Для больших битмап встречалось несколько решений. Например BigAssCanvas

Flasher
18 Апреля 2011
— 04:08
#

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

jarofed
18 Апреля 2011
— 16:41
#

@Ant.Karlov, я бы не спешил создавать фреймворк, где отрисовка всех объектов производится в буфер (ваш screen:Bitmap), а затем он выводится на экран. Мой совет - делайте его универсальным, чтобы он работал без буфера. Рано или поздно во флеше основным рендером будет рендеринг с использованием GPU. В этом случае по вашей методике флеш сначала нарисует все программно в буфер, а затем, когда его нужно будет отобразить на экране, загонит буфер в текстуру и текстуру передаст в видеопамять. Этот процесс передачи занимает кучу времени (поэтому и существуют пиксель-шейдеры) и ваш фреймворк в режиме GPU может работать в несколько раз медленнее, чем самые обычные MovieClip`ы (с cacheAsBitmap) просто добавленные на сцену, отрисовкой которых заботится сам флеш. Уже сейчас это заметно на мобильном флеш-плеере.

programmer
18 Апреля 2011
— 17:31
#

@Ant-Karlov:
А как дела обстоят с очень медленными движениями и поворотами клипов (смещения меньше чем на пиксель и поворот меньше чем на градус)? Движение рывками сохраняется? А в режиме низкого качества в плейере?

По демкам не смог определить.

BuxomBerry
18 Апреля 2011
— 17:55
#

Поддерживаю BuxomBerry:
?нтересно посмотреть на медленное движение по горизонтали

Flasher
18 Апреля 2011
— 19:59
#

@Flasher, справочника по ссылкам не нашел, может плохо искал? Есть много демок, сам плеер и несколько статей по которым можно лишь понять что и как работает. Но для глубокого изучения этого не достаточно. На мой взгляд информации по Molehill сейчас очень мало потому что API еще в глубокой разработке и API постоянно меняется.

Ant.Karlov
18 Апреля 2011
— 23:21
#

@programmer, а без буфера куда спрайты рендерить? Вариант с растеризацией клипов и использованием стандартного Sprite для рендера — я как раз представил в этой записи для сравнения производительности. Так же в этой записи для тестирования я уже использовал плеер для тестов с аппаратной поддержкой.

Если под полной аппаратной поддержкой подразумевается Flash Player 11 с Molehill, то если я не ошибаюсь, для полной аппаратной поддержки прийдется уже работать с треугольниками и полигонами, а для этого в любом случае нужно будет писать совсем другой фреймворк или использовать сторонний. Adobe не обещала нативных инструментов для работы с Molehill API.

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

Ant.Karlov
18 Апреля 2011
— 23:38
#

Во второй ссылке:
http://download.../flashplayer_inc_langref_022711.zip

Ну и сейчас практически все основные 3Д движки молехилл-реди ;)

Flasher
18 Апреля 2011
— 23:50
#

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

Код движения в примере такой:
velocity.x = Math.random();
angularVelocity = Math.random();
dragon.x += velocity.x;
dragon.angle += angularVelocity;
Поэтому жмите кнопку «Обновить» чтобы увидеть разную скорость движения ;)

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

Но при ручном рендере решается другая проблема: например у меня в Zombotron при использовании Sprite и Bitmap во время скролла карты все объекты дергаются — смещаются не синхронно. То есть смещаются они равномерно, но рендерятся по разному, как будто для одних спрайтов координаты округляются методом Math.ceil(), а для других Math.floor(). ?з-за этого получается эффект дрожания объектов :( Если смотреть внимательно ролик Зомботрона, то можно заменить как подергиваются ящики и бочки во время движения карты. Аналогичный эффект есть и в Mining Truck 2, но там он был менее заметен потому что скорость скролла карты большая, но можно видеть дрожание грузов или деталей моста если ехать медленно. ? это для меня на данный момент самая большая проблема стандартного рендера Flash которая никак не лечится стандартными средствами и именно из-за нее я начал ковырять эту тему.

Ant.Karlov
19 Апреля 2011
— 00:04
#

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

Ant.Karlov
19 Апреля 2011
— 00:08
#

Ant.Karlov@ Аналогично наблюдаю в своей новой игре асинхронное дерганье объектов. Особенно неприятно когда становиться видно составные части текстур.

bischak
19 Апреля 2011
— 01:41
#

@Ant.Karlov

Как применять MoleHill для 2D графике лучше смотреть на исходниках:

http://www.flashrealtime.com/m2d/

там есть кое какие хитрости с AGAL (Adobe Graphic Assembler Language), А в целом прав. Вывод любого спрайта осуществляется с помощью 2х треугольников.

Если заложить гибкость (к примеру шаблоном "Адаптер" http://en.wikipedia.org/wiki/Adapter_pattern) в фреймворк, то можно в любой момент переключаться между MoleHill и традиционными BitmapData.

Hyzhak
19 Апреля 2011
— 02:11
#

@Ant-Karlov:
Но ведь при отрисовке методом draw все равно используется Flash vector render и получается, что для растеризации трансформированных изображений используются те же неточные алгоритмы. Почему же проблема со скролингом решилась? Ты проверял скролинг со скоростью смещения меньше одного пикселя?

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

По идее рывки в такой ситуации могут исчезнуть только если использовать целые числа для координат. ? не применять масштабирования и поворотов ни в каком виде, кроме заранее разстеризованных в страйп или последовательность изображений. (Масштабирование в 2n раз и повороты на 90 градусов не в счет).

BuxomBerry
19 Апреля 2011
— 08:27
#

@Hyzhak, по приведенной ссылке представлен простой фремворк m2d который использует Molehill API для рендера 2D графики. Демки и исходники я правда еще не смотрел, но из описания и так все понятно :) То есть когда Molehill API станет более доступным, все равно прийдется смотреть на него под новым углом и писать отличающийся движок от того как мы сейчас работаем с битмапами.

За ссылку на паттерн «Адаптер» спасибо! :)

Ant.Karlov
19 Апреля 2011
— 11:50
#

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

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

Ant.Karlov
19 Апреля 2011
— 12:22
#

@Ant.Karlov
очень интересно Ваше мнение о nape, по тестам ведь он будет пошустрее Box2D.

nodoxi
19 Апреля 2011
— 13:25
#

@Ant.Karlov
и еще такой вопрос.
как бы утроить обсчет физики в игре платформере с 2.5D видом. (как в double dragon, герои могут ходить вниз-вверх и еще прыгать).
мне пока на ум приходит только сделать вручную разбивку на слои. но это как-то глупо наверное.

nodoxi
19 Апреля 2011
— 14:39
#

Во flixel доступ к stage, осуществляется через FlxG.stage он там сразу есть, добавлять не надо. Еще FlxState наследуюется от Sprite, можно ChildAdd использовать.
Но, честно говоря я сильно расстроился во стандартном флешевском рендере, когда попробовал сделать простенькую анимацацию большего интерфейса. Хочу выдрать flixelовский рендерер, чтобы он для простых Sprite рисовал, и я мог сделать несколько слоев.. Но это все мечты :)
Насчет обрисовки рамкой - незнал, сейчас пойду выдерну этот код из движка :). У меня весь фликсел порезаный на куски, с кучей закоментированного кода :). А еще недавно подключил Box2D и Nape ^_^. Правда, подумываю отказаться от этого, и использовать поверх буффера flixel простые векторные модельки. Может быстрее заработает.

Удачке Антоха! Надеюсь получится что нибудь типа Flixel и ты дашь в свободное пользование свой движек. Я бы, честно говоря, помог в написании

WeslomPo
19 Апреля 2011
— 16:01
#

Небольшой вопрос. При использовании copyPixels и draw неизбежен pixelSnapping, то есть копируемое изображение будет подгоняться попиксельно и сдвиг на float координаты невезможен, так? или возможно это как то побороть?

zaynyatyi
19 Апреля 2011
— 16:06
#

@nodoxi, я не пробовал Nape и не тестировал его производительность по сравнению с Box2D поэтому ничего определенного по этому поводу не могу сказать. Знаю только, что в Nape есть радикальные отличия от Box2D за счет чего ведет он себя иначе и судя по отзывам в некоторых случаях производительнее, но есть и свои недостатки.

Ant.Karlov
19 Апреля 2011
— 16:28
#

@nodoxi можно сделать сортировку по глубине, в flixel для этого есть даже команда.

WeslomPo
19 Апреля 2011
— 16:54
#

@nodoxi, для реализации такого вида на Box2D можно использовать фильтры для разделения объектов на физические группы, за приделами которых физ объекты не будут сталкиваться друг с другом. Только вот мне не доводилось менять фильтр для проверки столкновений после создания физического тела и я не уверен, что это можно сделать в runtime. Но даже если нельзя менять фильтр для проверки столкновений после создания физ.тела, то тут можно обойтись и без этого.

Ant.Karlov
19 Апреля 2011
— 17:04
#

@WeslomPo, скорее всего я просто не заметил переменной с указателем на stage во FlxG.as :)

Кстати, судя по всему ты плохо расковырял Flixel так как в нем существует возможность создавать сколько угодно слоев! Просто в классе FlxState.as используется стандартная группа объектов defaultGroup:FlxGroup но на самом деле тебе никто не мешает создать подобных групп сколько угодно в игре и процессить/рендерить их в нужной тебе последовательности ;) Например:

var _frontLayer:FlxGroup = new FlxGroup();
add(_frontLayer);

var frontSprite:FlxSprite = new FlxSprite();
// ... здесь инициализируем графику спрайта
_frontLayer.add(frontSprite);

Так же я не вижу никакой проблемы в том чтобы использовать FlxSprite для рендера физики Box2D или Nape без использования «векторных моделек» :)

Если таки доберусь до движка своего и из этого получится что-то интересное, то конечно поделюсь с общественностью :)

Ant.Karlov
19 Апреля 2011
— 17:31
#

@zaynyatyi, да в любом случае получается привязка к пикселям и если двигать спрайт со скоростью менее чем 1pix, то двигаться он будет все равно по пиксельно.

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

velocity.x = Math.random();
smoothing = true;
angle = 0.0001;
Для отрисовки используется метод draw().

Весь трик заключается в небольшом угле поворота и включенном сглаживании. Но если включить низкую настройку графики в плеере, то будет ОЙ :)

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

Ant.Karlov
19 Апреля 2011
— 17:56
#

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

BuxomBerry
19 Апреля 2011
— 19:24
#

Ant.Karlov можно урок по рисованию Зомби из Zombotron?
Очень нравится ваш стиль*

TRaY
19 Апреля 2011
— 19:26
#

@TRaY, что вас интересует в рисовании зомби? Скетч персонажа сделан на бумажке, потом отсканирован и обводка с раскраской уже делалась во Flash, в целом подход к рисованию используется точно такой же как и к рисованию грузовика/домика/дерева. Видео процесса рисования зомби я к сожалению не записывал.

Ant.Karlov
19 Апреля 2011
— 19:42
#

@BuxomBerry, польза безусловно сомнительная, просто случайно обнаружил такую вот странность :) Думаю, что идеальную плавность мы получим только с полной аппаратной поддержкой.

Ant.Karlov
19 Апреля 2011
— 19:44
#

@Ant.Karlov, да я в курсе про FlxGroup, как никак основной инструмент :) пишу на фликселе с сентября прошлого года).

Я имел в виду, чтобы можно было сделать несколько FlxState-ов, которые бы могли жить паралелльно. Сейчас, например, чтобы перейти из одного State в другой, нужно их создавать заново, и передавать управление, а хочется чтобы они были статическими, и могли быть оба активными. Это позволило бы делать переходы между уровнями и меню плавными простым изменением alpha или положения в пространстве flash, вставлять flixel в любое flash приложение как Sprite. Думаю, это нашло бы применение в каких нибудь проектах, где нужны миниигры, но сам проект приследуют другие цели (например приложение вконтакте).

WeslomPo
20 Апреля 2011
— 00:06
#

кстати Adam `Atomic` Saltsman, как раз сейчас взялся за модернизацию flixel-а, думаю его можно попросить пофиксить проблему с рамками.

Hyzhak
20 Апреля 2011
— 02:41
#

"Весь трик заключается в небольшом угле поворота и включенном сглаживании."

Если присмотреться, то видно, что картинка при перемещении немного меняется (четче, видимо, на целых пикселях)

Насколько я помню, еще можно использовать не угол, а масштабирование (scaleX = scaleY = 1.0001);

Flasher
20 Апреля 2011
— 02:48
#

@Ant.Karlov
да, для простейшей реализации 2.5d можно раскидать всё по FlxGroup/фильтрам коллизий. но собственно сколько это будет слоев. я сделал прототип с тремя слоями. но пока не очень доволен результатом.

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

nodoxi
20 Апреля 2011
— 08:02
#

Вот умеют же люди! =) http://www.newgrounds.com/portal/view/567520

Если есть
20 Апреля 2011
— 10:52
#

Ant.Karlov меня интересует раскраска!
Можете хотя бы сделать видеоурок по рисованию какого нибудь персонажа (наподобие главного героя из Zombotron) в вашем стиле?
Тоесть заснять процесс раскраски.

TRaY
20 Апреля 2011
— 11:06
#

@WeslomPo, вначале показалось что там очень просто сделать поддержку нескольких FlxState`ов одновременно. Но внимательно по размышляв на эту тему оказалось что не все так просто из-за общего буффера. ? на деле чтобы добавить такую возможность нужно хорошенько поработать над изменением архитектуры всего движка.

Кстати в базовом классе FlxState.as есть свой screen, в комментарии к которому написано, что вы якобы можете работать с ним, но я не очень понял как и зачем его использовать. Может быть его можно использовать как скрин для каждого отдельного State?

Ant.Karlov
20 Апреля 2011
— 11:26
#

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

Ant.Karlov
20 Апреля 2011
— 11:27
#

@Flasher,

> Если присмотреться, то видно, что картинка при перемещении немного меняется (четче, видимо, на целых пикселях)

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

Ant.Karlov
20 Апреля 2011
— 11:32
#

@nodoxi, по физике Flixel мне сложно что-то однозначно посоветовать. Я подумал, что для реализации игры с таким видом будет использоваться Box2D :)

Ant.Karlov
20 Апреля 2011
— 11:33
#

@Если есть, Nitrome вообще большие молодцы! У них почти все игры отличные :)

Ant.Karlov
20 Апреля 2011
— 11:34
#

@TRaY, с персонажами у меня всегда много возьни поэтому процесс их создания и анимации я не разу не записывал. Хотя в последнее время вроде все быстрее стало получатся. Подумаю еще над тем стоит ли записывать процесс создания персонажа.

Ant.Karlov
20 Апреля 2011
— 11:36
#

Ant.Karlov давай!

TRaY
20 Апреля 2011
— 14:38
#

@Ant.Karlov
так Box2D и используется )
просто выше был предложен вариант с FlxGroup. я и использовал эту терминологию.

nodoxi
20 Апреля 2011
— 14:56
#

@nodoxi
Многослойную фильтрацию можно сделать через связку categoryBits и maskBits, таким образом будет доступно 32 уровня глубины. При перемещении объекта между слоями соответствующие им биты выставляются в единицы, также при таком способе фильтрования очень просто сделать объект находящийся одновременно в нескольких слоях. При смене фильтрации на лету нужно вызывать метод Refilter у b2World (2.0.2)

Claymore
21 Апреля 2011
— 10:28
#

Антон, посмотри, какое чудо нашел на фгл:

http://www.flashgamelicense.com/view_game.php?game_id=1482 5

Не знаю, в курсе ты или нет, на всякий случай выкладываю.

Заимствованный арт, геймплей, даже левелдизайн. Чистый клоняра.

Автор некто illia dneprov.

Platon Skedow
21 Апреля 2011
— 18:36
#

Сорри, что-то линк покоробился. Правильная ссылочка:
http://www.flashgamelicense.com/view_game.php?game_id=14825

Platon Skedow
21 Апреля 2011
— 18:37
#

@Platon Skedow, чесно говоря в чем тут клон? графа другая уровни другие общего то что мужик бегает и чето двигает. Я думаю не клон это. Что теперь все игры в жанре платформер с мужиком каторый чето двигает и чето собирает клоны грибника чтоли? Тогда уж и грибник клона санты :)))

?ван
22 Апреля 2011
— 09:19
#

2 ?ван.
Ого как 0_о
Советую запустить рядом грибника и этого вот дровосека.

Platon Skedow
22 Апреля 2011
— 10:08
#

?гры действительно очень похожи механикой. Очевидно автор ориентировался на грибника. Но все же эту поделку до уровня грибника еще пилить и пилить. В ней нет жилки, которая заставит снова и снова нажимать "next level"

icewind
22 Апреля 2011
— 11:30
#

@Platon Skedow, Woodcutter я уже видел ранее. С точки зрения клонирования игры, претензий к разработчику я не имею. Графика нарисована с нуля, код тоже свой, а что до игровой механики то на её авторство я не претендую. Любая другая схожесть с Mushroomer уже на совести автора.

Ant.Karlov
22 Апреля 2011
— 13:27
#

@Ant.Karlov
а очищение экрана как у тебя реализовано? каждый фрейм заливка прозрачным цветом?

zaynyatyi
24 Апреля 2011
— 12:02
#

@zaynyatyi, прозрачным цветом бесполезно заливать ;) Варианта два:

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

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

Ant.Karlov
24 Апреля 2011
— 15:27
#

connectionBitmapData.fillRect(connectionBitmap.rect, 0x00000000); - я вот так делаю, в принципе очищает, но производительность падает, если каждый фрейм делать. Но тут я весь фон заливаю, сейчас попробую переделать только на видимую область. Что то сегодня голова не варит совсем.
А если не очищать, то углы (edges) у изображений с прозрачным фоном становятся всё более и более резкими.

zaynyatyi
24 Апреля 2011
— 20:57
#

@zaynyatyi, а какого размера рабочий буффер куда отрисвываются все игровые объекты?

По идеи, основной буффер не должен превышать размеры игрового окна. То есть, например, если в игре разрешение 640x480 то и буффер для рендера объектов должен быть таким же размером. При этом необходимо выполнять проверку на видимость спрайтов и если спрайты целиком за приделами экрана, то тогда их рендерить не нужно. Заливка 640x480 выполняется достаточно быстро. Но если в игре используются статичные/динамические бэкграунды на весь экран, то буфер вовсе можно не очищать рисуя новый кадр прямо по верх старого.

Ant.Karlov
24 Апреля 2011
— 22:10
#

@Ant.Karlov
основной буффер 2500х2000, просто изначально пошел не по тому пути (рисовал не в общем буффере, а каждый объект в своем спрайте). Сейчас оптимизирую производительность. Подумал, может упустил способ "удаления" объектов с битмап даты. Например попиксельным xor. Очистку только видимой части совсем упустил из виду (чистил весь буффер), вот и терял 5-6 фпс. Сейчас чищу только видимую область, просадки нет. Спасибо.

zaynyatyi
24 Апреля 2011
— 23:27
#

ОМГ, попиксельный XOR меня просто убил... zaynyatyi, вы чего с ума сходите, какие нафик буферы на 2500х2000?!

Берете создаете битмап на 640х480, смещаете его x,y в мировых координатах так, чтобы он всегда был виден на экране целиком и отрисовываете в него методом draw каждый объект поочередно, исключая те объекты, чьи координаты не лежат внутри прямоугольника этого битмапа в мировых координатах.

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

Про удаление объектов XOR`ом вообще для меня загадка. Но чтобы вы ни имели ввиду, забудьте про попиксельную обработку, забудьте!

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

BuxomBerry
25 Апреля 2011
— 07:58
#

Ant.Karlov снимай уже процесс рисования персонажа, неделю жду!

TRaY
25 Апреля 2011
— 14:10
#

Ободряющая статейка:) уже с НГ стал копать в сторону собственного рендеринга, но работал я с большими битмапдатами 5000+, в ходе всяческих тестирований узнал много заковырок. ? вот уже было хотел сам пошаманить с большим количеством относительно мелких объектов. ? как вовремя попал на статью ... всегда приятно осознавать что движешся в верном направлении :)
Спасибо Антон! Гуджоб!

Bazz
25 Апреля 2011
— 15:22
#

А вот как при использовании copyPixels сохранить blendmode ? ?ными словами как blending mode работал при копировании и накладывании одних пиксеей на другие?

shaman4d
19 Мая 2011
— 23:07
#

@Shaman4d:
Никак. copyPixels просто копирует данные из одного участка памяти в другой, никакой работы с цветовой информацией не осуществляется. Поэтому он и работает быстрее чем draw.

BuxomBerry
22 Мая 2011
— 09:41
#

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

flash шаблоны
27 Мая 2011
— 23:09
#

Здравствуйте Антон!
У меня остро встал вопрос об оптимизации. При не очень интенсивных боевых действиях на карте, происходит просадка FPS к 10. Профайлер показывает:
[pre-render] - 43%
[render] – 33%
[execute-queued] – 7%
а вот [enterFrameEvent] – 17%

Отсюда вывод – нужно что-то делать с 83% процентами на отрисовку объектов.
Перечитал достаточно материала, но все попытки применить на практике кэширование в растр заканчиваются провалом. Тормоза становятся еще серьезней. По-моему проблема в непонимании основ. Как в существующей архитектуре Universe применить рисование в битмапу?
1.При старте уровня, все необходимые динамические объекты прокручиваются покадрово и перегоняются в BitmapData.
2.Это должен быть спец.клас CachedMovieClip реализующий функционал MovieClip (gotoAndStop/gotoAndPlay/Play/Stop/currentFrame/totalFrame/[/b]rotation/mask[b]!!!) и содержащий в себе Vector.<BitmapData> (для всех кадра)
3.Все графические объекты TowerBase, EnemyBase, BulletBase… порождаются от него.
4.В обработчике enterFrameHandler после работы функций update требуется отобразить BitmapData полученные от enemies, towers, bullets в единственную главную Bitmap игрового поля.
Как должно быть на самом деле? Куда данные кешируются, кто за это ответственен, как накладываются маски, повороты сильно портят качество картинки, как быть с управлением глубиной отображения, перестает работать получением объектов под курсором getObjectsUnderPoint…
Я понимаю, что это очень объемная тема, но хотя бы основы архитектуры…
Спасибо.

FirstFlashGame
29 Марта 2012
— 12:03
#

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

Ant.Karlov
29 Марта 2012
— 23:59
#

конфигурация -
Intel Core i5 2.8GHz, 4 ядрышка. Озу - 4ГБ. Nvidia GeForce GT 530

тестирование 1:
1)фпс~40
2)фпс~60

тестирование 2:
1)фпс~60
2)фпс~60
3)фпс~60

Валериус
18 Октября 2012
— 03:09
#