четверг, октября 30, 2008

Дублируем DisplayObject. AS3

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

Но в AS3 есть множество средств, которыми можно это сделать. Для начала, порыщем в Google.

Duplicate Movie Clip Action Script 3 (взято у Senocular, в котором есть этот исходник).
Этот метод уже стал классикой, судя по тому, как много на него ссылается блогов и форумов. Он также описан на Flashere. Идея такова: извлекается класс исходного дисплей-объекта var targetClass:Class = Object(target).constructor;, инстанцируется, и в него переносятся основные свойства исходного дисплей-объекта.

as3 duplicate DisplayObject. Здесь класс исходного дисплей-объекта получается аналогичным образом. Отличие лишь в том, что передаваемые свойства автоматически извлекаются посредством рефлексии - метода describeType.

Creating Class Instances from a DisplayObject in AS3. Здесь имя класса получается более хитрым способом - из строки, возвращаемой getQualifiedClassName. Затем, getDefinitionByName возвращает класс исходного дисплей-объекта.

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

Таким образом, дублировать клип вышеперечисленными способами нельзя если клип не имеет имени класса в Linkage.
Это еще полбеды. Ведь мы всегда можем просто взять, да и указать это имя.
Но.
А если мы внедряем в наше приложение некий клип при помощи [Embed] , который в дальнейшем надо распарсить и что-то в нем дублировать?
Это что-то мы никогда не сможем дублировать, т.к. никакие Linkage-классы из внедряемой библиотеки не распознаются - они не переносятся в ApplicationDomain.

Кстати, интересное замечание по второму способу с рефлексией: describeType для объектов из внедряемой библиотеки выдает тэги описания свойств как "accessor", в то время как для объектов из подгруженной библиотеки - "variable".

Итак, вывод - универсального решения нет, и выход один - всё делать "ручками". Или я ошибаюсь?

* * *

Спасение пришло с другой стороны: Включение файлов в SWF в виде байтовой последовательности. Проблема внедряемых файлов будет устранена, остальное, в принципе решаемо.

* * *

Да, интуиция поискать по запросу loadBytes не подвела. Внедрил SWF вышеуказанным способом, загрузил через Loader.loadBytes. Отлично! Даже фреймовые скрипты работают. Всё как обычный Loader.load.

Единственная оговорка: Я загружаю классы в ApplicationDomain.currentDomain. При этом, параметр checkPolicyFile необходимо установить в false:
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);

Еще раз спасибо Garbage Collector. Глядя на дату спасительного поста, я понимаю, что здорово отстаю :(.

6 комментариев:

Unknown комментирует...

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

Unknown комментирует...

Я наверное не совсем правильно выразился. Сорри. Да в принципе, я и не говорил "Шаблонный проект". Под "шаблонной структурой приложения" в данном случае следует понимать приложение, работающее с неким шаблоном, подгружаемым извне. То есть "шаблон" - это некий стандарт, которому следует структура внешней флэшки.
Ну вот смотри. Есть шаблонная флэшка. Она грузится/внедряется в приложение. Приложение после ее загрузки/получения сканирует эту флэшку, навешивает на кнопки события, вносит тексты, выставляет другие свойства, создает управляющие классы, поручая им для пользования дисплей-объекты (тобишь клипы).
Так вот, к примеру, один из управляющих классов должен генерить в свой контролируемый клип некоторое количество клипов-деталей.
Откуда ему их взять? Как ему сказать какие клипы-детали он дожен генерить? Задача состоит в том, чтобы сделать это с минимумом завязок на код. Простое и понятное решение - просто кинуть в контролируемый клип "образцы" тех клипов, которые нужно нагенерить. Управляющий класс их сосканирует и еще нагенерит сколько надо. Универсально и понятно для flash-верстальщика.

foreground комментирует...

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

public static function copy(value: Object): Object
{
var buffer: ByteArray = new ByteArray();

registerClassAlias(getClassAlias(value), value.constructor);
buffer.writeObject(value);
buffer.position = 0;
return buffer.readObject();
}

Такая конструкция требует flash.net.registerClassAlias для всех подклассов, на сколько я понял.
Да с загрузкой swf-а не Loader-ом меня тоже сильно ущимило, как пользователя AS3. Я был просто в недоумении и пребываю в нём до сих пор ). Почему адобовцы пренебрегли в данном случае простыми принципами ООП разделения полномочий. Вобщем конечно косяков хватает. И шарп во многом выглядит привлекательнее, хотябы гарбэджем. Но я делаю скидку на то что первый блин все-таки комом.
Напоследок в тему.. )) пользуйся интерфейсами для подгрузки заранее известного по функциональности мувика (совет я читал от __etc).

Unknown комментирует...

Спасибо, foreground. Есть заметки:
> сериализация
Любопытно, код в точности из mx.utils.ObjectUtil.copy() с добавлением registerClassAlias(getClassAlias(value), value.constructor);. Я как раз начал эксперементы с сериализации через ObjectUtil.copy, но безуспешно. Через сериализацию нельзя скопировать объект клипа полностью, т.к. она не предусматривает копирование объектов, на которые ссылаются свойства копируемого объекта. Скопируются просто ссылки. Да, собственно, в хелпе для ObjectUtil.copy написано - предназначен для копирования объектов данных, типа элементов коллекций, а UIComponent так не копируется.
Чтобы копировать сложный класс, нужно рекурсивно пройтись по всем ссылкам и сохранить все вложенные объекты, а затем их опять восстановить. Кроме того, встает вопрос - а если не все ссылки надо копировать? Типа ссылки на root, stage и т.п.
Поэтому, общего решения быть не может.
А Адоби не предусмотрел, а ведь мог бы сделать метод DisplayObject.clone();

>Да с загрузкой swf-а не Loader-ом ...
Ембед с параметром
mimeType="application/octet-stream" - работает превосходно - никакой видимой разницы - зашита SWFка или загружается.

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

foreground комментирует...

2 racer
сори если что )

"Ембед с параметром
mimeType="application/octet-stream" - работает превосходно - никакой видимой разницы - зашита SWFка или загружается."

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

foreground комментирует...

аа. ..да для BitmapData вроде не пашет.