Эта статья посвящена ActionScript и впервые была опубликована в моем личном блоге 6 лет назад.
Иногда, когда вы экспериментируете со вспышкой, вы замечаете, что все может немного подтормаживать, когда вы помещаете слишком много объектов на сцену и пытаетесь перемещать их одновременно. Список отображения — это очень быстрый и простой способ обработки того, как вещи отображаются на экране, но вам придется пожертвовать некоторой производительностью.
Так почему же частота кадров во flash падает, когда вы хотите одновременно переместить +1000 видеоклипов? Если вы пойдете и посмотрите на livedocs для класса MovieClip, вы увидите, что MovieClip не только расширяет несколько классов (Sprite › DisplayObjectContainer › InteractiveObject › DisplayObject › EventDispatcher ›Object), он также имеет множество свойств, таких как .currentFrame, и методов, таких как gotoAndPlay(). Все это очень приветствуется, когда вы хотите их использовать, но когда вы просто хотите отобразить основные вещи, это не тот путь.
Одним из решений является использование Sprite (с потерей функций временной шкалы) или даже DisplayObject, самого основного класса, который Flash может отображать в своем DisplayList. Другим решением является использование техники под названием Blitting. На Adobe Developer Connection есть отличная статья, объясняющая основы этой техники.
Короче говоря, причина, по которой этот метод работает так быстро, заключается в том, что он полностью пропускает DisplayList. Когда вы рисуете что-то на экране, используя технику блитинга, вы копируете прямоугольную часть растрового изображения прямо в другое растровое изображение. Первое растровое изображение за кадром, второе — на вашей сцене.
Например, вы указываете flash скопировать прямоугольник с шириной = 100 и высотой = 100 в позициях x = 0 и y = 0 на вашу сцену в позициях x = 20 и y = 20. Теперь это будет отображать квадрат со сторонами 100 пикселей, содержащий все, что было в верхнем левом углу вашего растрового изображения за пределами экрана, на вашей сцене в позиции (20,20). Взгляните на следующий пример, чтобы получить лучшее представление:

Блиттинг действительно эффективен, когда он реализован в играх. Когда вы анимируете персонажа, удаление анимации может легко дать вам увеличение производительности в 10 раз.
Пару лет назад я делал интро для Международного центра искусств и новых технологий, и в ранней версии текущего интро был переход между двумя логотипами с помощью предварительно нарезанных частиц. Я всегда хотел сделать это снова без необходимости нарезать логотипы и с большим количеством частиц, но я понял, что производительность будет проблемой. Несколько лет спустя я узнал о блиттинге и познакомился с BitmapData и друзьями, и в процессе родился класс Synergy.
Класс Synergy инициализируется свойством stage вашего класса Document и имеет метод loadItems(), который принимает 2 DisplayObjects — один sourceObject и один targetObject. Далее происходит то, что после вызова метода Synergy.explode() исходный объект растворяется во всех пикселях, составляющих объект, улетает, а затем объединяется в целевой объект.
А теперь давайте посмотрим, что происходит за экранами. Во время работы над этим проектом я столкнулся с несколькими проблемами и кое-чему, чему научился, так что читайте дальше, чтобы узнать о некоторых моментах, или просто погрузитесь в исходный код.
Подробнее о технике рисования
Первое, что происходит в методе loadItems(), — это разбор DisplayObjects. Под синтаксическим анализом я подразумеваю анализ каждого пикселя, проверку его значения альфа-канала (когда значение альфа-канала равно 0, вам не нужно перемещать его, потому что он прозрачный). для каждого пикселя, который стоит перемещать, создается объект из класса «Частица». Этот класс содержит некоторую важную информацию о пикселе:
- Его исходное положение ("sourceX, sourceY" ›› они понадобятся нам позже, когда мы будем выполнять блиттинг ИЗ закадрового объекта)
- Его текущее положение ("x,y" ›› указывает положение на сцене куда мы будем копировать TO.
- Его целевая позиция ("targetX,targetY" ››, которая укажет позицию, куда пиксель должен будет переместиться)
- Ссылка на DisplayObject, куда пришел пиксель из. (переменная с именем «copyBitmapData»)
Все эти частицы помещаются в список и отображаются на экране 31 раз в секунду по событию ENTER_FRAME. Давайте посмотрим на функцию, которая отрисовывает наши пиксели на экране:
private function drawStuff(e:Event):void
{
canvasData.fillRect(canvasData.rect, 0xFFFFFF) // clear the canvas before drawing
var lli : ILinkedListIterator = sourceList.iterator() as ILinkedListIterator;
var item:Particle;
while (lli.hasNext())
{
lli.next();
item = lli.current;
if(item.update()) getNewTarget(item);
copyP.x = item.x; copyP.y = item.y;
copyR.width = copyR.height = particleSize;
copyR.x = item.sourceX; copyR.y = item.sourceY;
canvasData.copyPixels(item.copyBitmapData, copyR, copyP);
}
}
«copyP» — это объект «Точка», а «copyR» — это объект «Прямоугольник». Эти два объекта являются важными параметрами метода copyPixels. прямоугольник «copyR» определяет область, которая будет скопирована на сцену (см. примеры выше), а точка «copyP» определяет позицию, которую этот прямоугольник займет на сцене. item.copyBitmapData — это ссылка на DisplayObject, из которого будут скопированы пиксели.
метод item.update() возвращает логическое значение, которое является ложным, если частица все еще беспорядочно перемещается. Однако, когда эта функция возвращает значение true, это означает, что частица остановилась и ей нужен пункт назначения. Метод getNewTarget даст этой частице место назначения для Tween. Обратите внимание, что в этом случае мы не анимируем DisplayObject, как обычно бывает, а анимируем свойства X и Y объекта Particle.
класс Pixel32
Когда вы извлекаете информацию о пикселях из растрового изображения, вы можете сделать это с помощью метода BitmapData.getPixel32(). Это возвращает целое число без знака в диапазоне от 0 (0x00000000) до 4294967295 (0xFFFFFFFF). Чтобы немного облегчить себе жизнь, я создал класс Pixel32. Вы можете создать новый объект Pixel32, создав его экземпляр со значением цвета, а затем прочитав общедоступные свойства альфа, красный, зеленый или синий, или вы можете использовать статические функции. К ним относятся:
- toString(): это вернет строку, например «OxFFOOFFOO — Альфа: FF — Красный: 0 — Зеленый: FF-Синий: 0»;
- isWhite(): это вернет логическое значение, которое говорит вам, является ли пиксель белым.
- isTransparent(): это вернет логическое значение, которое говорит вам, является ли пиксель прозрачным на 100%.
- getAlpha(), getRed(), getGreen(), getBlue()
Некоторые случайные оптимизации
- Объявляйте свои переменные вне циклов for/while. При этом вы заметите значительное увеличение производительности. Если вы посмотрите на приведенный выше код, вы увидите, что я объявляю «var item:Particle» только один раз перед циклом, а затем повторно использую эту переменную, вместо того, чтобы объявлять ее снова и снова внутри цикла while. петля.
- Использование LinkedList вместо Array — отличный способ повысить производительность, когда итерация по списку — это самое важное, а произвольный доступ к элементам в списке не нужен.
Надеюсь, вам было так же весело читать этот пост, как мне его создавать! Если у вас есть какие-либо вопросы по этому сообщению или у вас есть советы по улучшению моего кода или производительности приложения, пожалуйста, используйте раздел комментариев ниже.
Нравится этот пост? "Подпишись на меня в Твиттере !