User's collector

Внимание!   Данная опция будет доступна только после того, как вы авторизуетесь.
   запомнить меня 

Если кто не в курсе, то уже почти месяц как Intellij Idea 9 имеет возможность просмотра UML-диаграмм для ActionScript и Flex классов, а это значит, что у нас есть возможность анализировать код своих Flex проектов при помощи UML диаграм классов.

5 комментариев

Теги:

В одном из разделов форума Flasher.ru был задан вопрос о том, каким образом нужно правильно удалять всех детей из объекта DisplayObjectContainer.

В ходе обсуждения были предложены следующие варианты (орфография и пунктуация авторов сохранены =):

  1. Вариант пользователя smilenka.
    Actionscript:
    1. var maxChildIndex:int = graphicContext.numChildren - 1 ;
    2. for( var childIndex:int = maxChildIndex; childIndex>= 0 ; childIndex-- )
    3. {
    4.     graphicContext.removeChildAt( childIndex ) ;
    5. }

  2. Вариант пользователя __etc.
    Actionscript:
    1. var l:uint = super.numChildren;
    2. while (l--) super.removeChildAt(l);

  3. Вариант пользователя wvxvw.
    Actionscript:
    1. while (numChildren) removeChildAt(0);

Так какой же из вариантов самый оптимальный?

Внимание, правильный ответ. В плане быстродействия лидирует вариант номер три от пользователя wvxvw (761 миллисекунд против 1757, получаемых во втором варианте при 10000 итерациях):

Actionscript:
  1. var s:Sprite = new Sprite();
  2. for (var i:uint = 0; i <10000; i ++) {
  3.     var h:Sprite = new Sprite();
  4.     s.addChild(h);
  5. }
  6.            
  7. var t1:Number = getTimer();  
  8. var l:uint = s.numChildren;
  9. while (l--) s.removeChildAt(l);  
  10. var t2:Number = getTimer();  
  11. Debugger.trace(t2 - t1);//1575
  12.            
  13. //***********************************
  14.            
  15. var b:Sprite = new Sprite();
  16. for (var j:uint = 0; j <10000; j ++) {
  17.     var m:Sprite = new Sprite();
  18.     b.addChild(m);
  19. }
  20.            
  21. var t3:Number = getTimer();  
  22. while (b.numChildren) b.removeChildAt(0);      
  23. var t4:Number = getTimer();
  24.                    
  25. Debugger.trace(t4 - t3);//761

Коментарии по этому поводу от Дениса «__etc» Коляко:

«Разница в том, что код wvxvw в цикле запрашивает геттер numChildren и удаляет ребенка с нулевого индекса.

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

Как выяснилось, в модели DisplayObjectContainer элемент на нулевом индексе оказывается последним в массиве элементов, а не наоборот. Именно поэтому удаление последнего элемента в display list медленнее, чем первого. Ну а геттер numChildren оказался таким же быстрым, как и декремент локальной переменной, вероятно в силу того, что декремент выполяется в несколько действий, с конвертацией и прочим.»

Посмотрим, как метод удаления всех детей реализован во Flex Framework:

Actionscript:
  1. public function removeAllChildren():void
  2. {
  3.     while (numChildren> 0)
  4.     {
  5.         removeChildAt(0);
  6.     }
  7. }

По всей видимости, над этим фреймворком работали не такие уж и бестолковые программисты… ;-)

На этом все. Эффективного вам кода!

15 комментариев

Теги:

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

В ходе перевода некоторых классов разрабатываемого мною в том числе приложения на использование класса Vector, возник вопрос: «Каким образом можно наполнять экземпляр вектора нужными нам элементами при его создании?».

В случае использования массива это делается достаточно просто. Можно прибегнуть к конструктору массива:

Actionscript:
  1. var rows:Array = new Array(new Row(0, 25), new Row(183, 3));

либо же к литералу:

Actionscript:
  1. var rows:Array = [new Row(0, 25), new Row(183, 3)];

У конструктора класса Vector есть всего два строго фиксированных параметра: первый указывает длину создаваемого экземпляра, второй отвечает за то, возможно ли изменение длины вектора со временем. Поэтому передать нужные нам элементы в конструктор класса Vector у нас не получится. Литерала вектора не существует. Обратившись к документации по этому классу, максимум, что мы можем найти — это метод concat(...args):Vector.<T>, присоединяющий переданные аргументы к объекту. Т.е. следующий код:

Actionscript:
  1. var rows:Vector.<Row> = new Vector.<Row>();
  2. rows.push(new Row(0, 25));
  3. rows.push(new Row(183, 3));

может превратиться в:

Actionscript:
  1. var rows:Vector.<Row> = new Vector.<Row>().concat(new Row(0, 25), new Row(183, 3));

Однако если взглянуть на документацию более внимательно, то мы можем найти там глобальную функцию Vector, принимающую в качестве параметра исходный массив или вектор, элементы которого будут являться элементами будущего вектора, и возвращающую этот новый вектор. Синтаксис этой функции в точности повторяет синтаксис конструктора класса Vector, однако оператор new в этом случае использовать не нужно. И наш код превращается в следующий красивый и краткий кусок кода:

Actionscript:
  1. var rows:Vector.<Row> = Vector.<Row>([new Row(0, 25), new Row(183, 3)]);

Вот такие тонкости. Удачного вам программирования!

См. также:

9 комментариев

Теги:

Возрадуйтесь начинающие флэшеры и флексеры! Вышла в свет новая книга Колина Мука на русском языке «ActionScript 3.0 для Flash. Подробное руководство» (страница книги на Books.ru).

Меня немного смущает перевод названия книги, который в оригинале звучит как «Essential ActionScript 3.0», что буквально означает «Основы ActionScript 3.0». Будем надеяться, что такой не совсем корректный перевод названия книги — это просто маркетологический ход, призванный повысить продажи, и в целом перевод основного содержимого книги будет на высоте, как и как и переводы двух прошлых книг Колина.

Лично я уже заказал эту книгу для своей коллекции. =)

26 комментариев

Теги:

Майк Чемберз опубликовал полный список нововведений во Flash Player 10 API. Кому интересно, тот может ознакомиться с этим списком на сайте Майка. Хотя меня мучают подозрения, что он не совсем полный...

Кроме того, Майк задает своим читателям вопрос, получивший просто огромное количество откликов: «Используете ли вы ActionScript 2 или 1? И если да, то почему?». Мне стало тоже интересно, кто-нибудь из читателей этого блога программирует еще на прошлых версиях ActionScript? И, в противоположность, начал ли кто-нибудь использовать в своих приложениях новшества Flash Player 10?

От себя добавлю, что мы в разрабатываемом приложении, о котором будет рассказано на ближайшем RAFPUG 11, решили заранее ориентироваться на Flash Player 10, поскольку не смогли отказаться от таких вкусностей, как FileReference.load() и FileReference.save(), от класса Vector, более продвинутого рендеринга текста, ну и, конечно, от интеграции движка Alternativa 3D с этой версией плеера.

4 комментария

Теги:

Признаюсь честно, в последнее время я писал статью о том, как разрабатывать Flex-приложения при помощи InlelliJ IDEA. Но сейчас я сомневаюсь, стоит ли мне продолжать, поскольку понимаю, что написать статью, наполненную большей любовью к этой среде разработки, чем ее написал пользователь Develar на Хабрахабре, в настоящий момент я не смогу. =)

Поэтому сейчас я отсылаю вас к статье «Разработка на Flex в IntellliJ IDEA с использованием maven». Не жалейте на знакомство со статьей свое время — IDEA восполнит его вам сторицей. Ну а сам я все же сконцентрируюсь на неосвещенных в статье вопросах: интеграции с Apache Ant, рефакторингах, автогенерации кода и горячих клавишах.

Один комментарий

Теги:

С любезного разрешения Майка Чемберза (Mike Chambers), публикую перевод на русский язык его статьи «Using Vectors in ActionScript 3 and Flash Player 10». Дальнейшее повествование идет от имени Майка.

Одной из новых возможностей, появившихся в Flash Player 10 Public Beta, является включение в эту версию плеера нового класса Vector. По существу, класс Vector является массивом, но в дополнение к функционалу массива он отслеживает тип хранимых элементов данных, и поэтому может обеспечить (иногда довольно существенный) прирост производительности по сравнению с обычным массивом.

Пользоваться классом Vector так же просто, как и пользоваться классом Array. Фактически, класс Vector содержит все те же методы, что и класс Array, и главным отличием между ними является то, как вы создаете экземпляры этих классов.

Для примера, вот так вы инстанцируете экземпляр массива:

Actionscript:
  1. var a:Array = new Array();

ну или вот так:

Actionscript:
  1. var b:Array = [];

А это пример создания экземпляра вектора, который будет хранить объекты с типом int:

Actionscript:
  1. // var ИМЯ_ПЕРЕМЕННОЙ:Vector.<ТИП_ХРАНИМЫХ_ЭЛЕМЕНТОВ> = new Vector<ТИП_ХРАНИМЫХ_ЭЛЕМЕНТОВ>();
  2. var vector:Vector.<int> = new Vector.<int>();

Точно так же, как и в случае использования массива, вы можете инстанцировать экземпляры вектора конкретного размера, передавая длину в качестве первого необязательного параметра конструктора:

Actionscript:
  1. var size:int = 7;
  2. var vector:Vector.<int> = new Vector.<int>(size);

Кроме того, конструктор вектора имеет второй необязательный аргумент в виде булева флага, определяющего должен ли текущий экземпляр Vector быть фиксированного размера (true), либо же его длина в дальнейшем может изменяться (false). По умолчанию этот параметр имеет значение false. В дальнейшем его можно изменить обратившись к свойству fixed экземпляра класса Vector:

Actionscript:
  1. var size:int = 7;
  2. var fixed:Boolean = true
  3. var vector:Vector.<int> = new Vector.<int>(size, fixed);
  4. vector.fixed = !fixed;

Имейте в виду, что если свойство fixed установлено в true, то далее вы уже не можете вызывать методы изменяющие длину массива, например, методы pop(), push(), shift() и т.д.

Кроме того, Vector следит и за соблюдением единообразия типа хранимых в нем переменных, в то время как в массиве можно хранить элементы разных типов:

Actionscript:
  1. var s:String = "I am a string";
  2. var d:Date = new Date();
  3. var n:Number = 1138;
  4. var a:Array = new Array();
  5. a[0] = s;
  6. a[1] = d;
  7. a[2] = n;
  8.  
  9. trace(a[1] is Date); // true

Однако в этом случае у вас возникнут ошибки на этапе компиляции:

Actionscript:
  1. var s:String = "I am a string";
  2. var d:Date = new Date();
  3. var n:Number = 1138;
  4. var v:Vector.<String> = new Vector.<String>();
  5. v[0] = s;
  6. v[1] = d;
  7. v[2] = n;
  8.  
  9. trace(v[1] is Date); // false
  10.  
  11. // Ошибки этапа компиляции:
  12. // Implicit coercion of a value of type Date to an unrelated type String.       
  13. // Implicit coercion of a value of type Number to an unrelated type String.

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

Actionscript:
  1. var vector:Vector.<int> = new Vector.<int>();
  2.  
  3. var rand:Number;
  4. for(var i:int = 0; i <1000000; i++)
  5. {
  6.         rand = (Math.floor(Math.random() * 1000000) as int);
  7.         vector.push(rand);
  8. }
  9. trace(vector[7]);

И последнее, что нужно иметь в виду при работе с векторами, заключается в том, что Vector представляет собой вариант «уплотненного» массива. Фактически, это означает, что все элементы, содержащиеся в экземпляре класса Vector должны иметь какое-либо значение или быть равными null. Например, при использовании массива вы можете свободно сделать следующее:

Actionscript:
  1. var a:Array = new Array();
  2. a[0] = "foo";
  3. a[6] = "bar";

Однако, если вы попробуете проделать то же самое с экземпляром Vector, то вы получите ошибку RangeError на этапе исполнения:

Actionscript:
  1. var v:Vector.<String> = new Vector.<String>();
  2. v[0] = "foo";
  3. v[6] = "bar";

Чтобы исправить это, зададим вектору длину при его создании:

Actionscript:
  1. var v:Vector.<String> = new Vector.<String>(7);
  2. v[0] = "foo";
  3. v[6] = "bar";

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

Actionscript:
  1. package
  2. {
  3.         import flash.display.Sprite;
  4.  
  5.         public class VectorTest extends Sprite
  6.         {
  7.                 private static const NUM_LOOPS:int = 5;
  8.                 public function VectorTest()
  9.                 {       
  10.  
  11.                         var vector:Vector.<int> = new Vector.<int>();
  12.                         var array:Array = new Array();
  13.  
  14.                         // Наполняем данными.
  15.                         var rand:Number;
  16.                         for(var i:int = 0; i <1000000; i++)
  17.                         {
  18.                                 rand = (Math.floor(Math.random() * 1000000) as int);
  19.                                 vector.push(rand);
  20.                                 array.push(rand);
  21.                         }
  22.  
  23.                         var sTime:Number = getMilliseconds();
  24.                         loopArray(array);
  25.                         trace("Loop Array Avg (5) : " + ((getMilliseconds() - sTime)/NUM_LOOPS));
  26.  
  27.                         sTime = getMilliseconds();
  28.                         loopVector(vector);
  29.                         trace("Loop Vector Avg (5) : " + ((getMilliseconds() - sTime)/NUM_LOOPS));
  30.  
  31.                 }
  32.  
  33.                 private function getMilliseconds():Number
  34.                 {
  35.                         return (new Date()).getTime();
  36.                 }
  37.  
  38.                 private function loopArray(a:Array):void
  39.                 {
  40.                         var len:Number = a.length;
  41.  
  42.                         var n:int;
  43.                         for(var i:int = 0; i <NUM_LOOPS; i++)
  44.                         {
  45.                                 for(var k:int = 0; k <len; k++)
  46.                                 {
  47.                                         n = a[k];
  48.                                 }
  49.                         }
  50.                 }
  51.  
  52.                 private function loopVector(v:Vector.<int>):void
  53.                 {
  54.                         var len:Number = v.length;
  55.  
  56.                         var n:int;
  57.                         for(var i:int = 0; i <NUM_LOOPS; i++)
  58.                         {
  59.                                 for(var k:int = 0; k <len; k++)
  60.                                 {
  61.                                         n = v[k];
  62.                                 }
  63.                         }
  64.                 }
  65.         }
  66.  
  67. }

На моей машине вывелись следующие значения:

CODE:
  1. Loop Array Avg (5) : 115.8
  2. Loop Vector Avg (5) : 108.8

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

Вы можете найти больше информации о классе Vector, обратившись к документации по Flash Player 10 Beta, а так же к информации о Flash Player 10 на Adobe Labs.

См. также:

4 комментария

Теги:

С любезного разрешения Колина Мука (Colin Moock) публикую свой вольный перевод его статьи «Things you must do before unloading a SWF file». Дальнейшее повествование пойдет от имени Колина.

Если вы загрузили SWF-файл, содержащий ActionScript 3, во Flash Player 9 и теперь хотите удалить его из памяти, то вы должны перед этим его деактивировать. Иначе этот файл так и будет продолжать занимать ресурсы, и в некоторых случаях не подвергнется удалению Garbage Collector-ом.

Ниже представлен неофициальный список действий, проведение которых необходимо для деактивации SWF файла:

  • Удалите ссылки на используемые внутренние ресурсы файла.
  • Остановите все проигрываемые звуки.
  • Остановите главный таймлайн ролика, если он проигрывается в настоящий момент.
  • Остановите все проигрываемые в настоящий момент внутренние мувиклипы файла.
  • Закройте соединения всех сетевых объектов, вроде экземпляров Loader, URLLoader, Socket, XMLSocket, LocalConnection, NetConnection и NetStream.
  • Обнулите все ссылки во внутренних объектах ролика на камеры и микрофоны.
  • Отпишите все функции-слушатели в SWF-файле от получения событий (особенно от Event.ENTER_FRAME и событий клавиатуры).
  • Остановите все запущенные в настоящий момент интервалы при помощи функции clearInterval().
  • Остановите все объекты Timer вызовом метода Timer.stop().

Помните, что представленный список по определению не является полным, так как он не был официально утвержден Adobe и, следовательно, не может являться исчерпывающим. Если вы знаете еще действия, которыми можно пополнить этот список, то присылайте их мне не почту (пользователь colin, домен moock.org).

Что касается Flash Player 10, то там вышеперечисленные действия можно выполнить автоматически вызовом метода unloadAndStop() класса Loader.

Для дальнейшего ознакомления с этой темой, смотрите вторую главу моей статьи «The Charges Against ActionScript 3.0», опубликованную на сайте Inside RIA и статью Гранта Скиннера «Additional Information on Loader.unloadAndStop()».

3 комментария

Теги:

Не секрет, что ActionScript 3 не поддерживает перегрузки методов. Иногда это может привести к затруднительным ситуациям. Например, у нас есть два интрефейса, в которых объявлены одноименные методы, но с различной сигнатурой, и требуется создать класс, который должен реализовывать оба этих интерфейса. При попытке реализовать классом оба таких интерфейса мы совершенно законно столкнемся с ошибкой. Чтобы быть более конкретным приведу пример.

У нас есть два интерфейса IHuman и IMonster, в которых объявлен метод kill, только в первом случае метод в качестве аргумента принимает IMonster-жертву, а во втором — в роли жертвы должен выступать IHuman. При попытке создать класс Werewolf, который должен являться как и IHuman, так и IMonster мы окажемся в затруднительной ситуации, поскольку как я уже сказал ранее ActionScript 3 не поддерживает перегрузки методов.

В сложившейся ситуации у нас есть два варианта наших действий. Первый вариант предполагает изменение условий задачи, проще говоря мы можем переименовать методы, например killMonster для IHuman и killHuman для IMonster. Если у вас есть возможность пойти по этому пути, то можете не колебаться и смело выбирать его. Второй вариант, исходит из того, что вы не можете изменить имена методов. В этом случае, можно создать два отдельных класса, каждый из которых реализует необходимый интерфейс и использовать их вместе в составе третьего класса Werewolf. Здесь есть маленькая тонкость. Как между двумя этими классами расшарить внутреннюю логику Werewolf. Например, в методе kill этих классов вызвать приватный метод prepareToKill класса Werewolf. В этой ситуации я для себя нашел небольшой workaround — создание класса оборотня.

Для начала нам потребуются выше обзначенные интерфейсы.

Actionscript:
  1. package
  2. {
  3.     public interface IHuman
  4.     {
  5.         function kill (victim:IMonster):void;
  6.     }
  7. }

Actionscript:
  1. package
  2. {
  3.     public interface IMonster
  4.     {
  5.         function kill (victim:IHuman):void;
  6.     }
  7. }

Переходим к созданию класса Werewolf, содержащий в себе как класс реализующий IHuman, так и класс реализующий IMonster. Оба эти классы возможны только как составляющие Werewolf, соответственно их можно смело сделать вложенными, то есть объявить за скобками пакета в том же файле, что описывает собственно сам класс Werewolf.

Actionscript:
  1. package
  2. {
  3.     public class Werewolf
  4.     {
  5.         private var _monster:IMonster = null;
  6.         private var _human:IHuman = null;
  7.        
  8.         public function Werewolf()
  9.         {
  10.             _monster = new Monster();
  11.             _human = new Human();
  12.         }
  13.        
  14.         public function asHuman ():IHuman
  15.         {
  16.             return _human;
  17.         }
  18.        
  19.         public function asMonster ():IMonster
  20.         {
  21.             return _monster;
  22.         }
  23.     }
  24. }
  25.  
  26.  
  27. internal class Monster implements IMonster
  28. {   
  29.     public function Monster ()
  30.     {
  31.     }
  32.    
  33.     public function kill (victim:IHuman):void
  34.     {
  35.         trace("human killed");
  36.     }
  37. }
  38.  
  39. internal class Human implements IHuman
  40. {
  41.     public function Human ()
  42.     {
  43.     }
  44.    
  45.     public function kill (victim:IMonster):void
  46.     {
  47.         trace("monster killed");
  48.     }
  49. }

И так мы имеем класс Werewolf, который может быть использован как IMonster, так и IHuman, для этого необходимо вызвать соответсвующий метод. Теперь вернемся к условиям, а именно, кроме реализации классов Monster и Human, нам требуется расшарить для них внутреннюю логику Werewolf. Так как эти классы могут существовать только в составе Werewolf мы могли бы смело дать им возможность вызывать приватные методы Werewolf. Однако, как мы знаем приватный метод, он на то и приватный, чтобы быть доступным только в рамках своего класса. Но нам никто не мешает создать собственную область видимости. А помогут нам в этом пространтсва имен. Чтобы создать неймспейс, видимый только в пределах наших классов, объявим его как и классы Monster и Human вне пакета. Таким образом методы, объявленные в этом неймспейсе, могут быть вызваны, любым из наших трех классов.

Actionscript:
  1. package
  2. {
  3.     public class Werewolf implements IWerewolf
  4.     {
  5.         private var _monster:IMonster = null;
  6.         private var _human:IHuman = null;
  7.        
  8.         public function Werewolf()
  9.         {
  10.             super();
  11.             _monster = new Monster(this);
  12.             _human = new Human(this);
  13.         }
  14.        
  15.         werewolf_private function prepareToKill ():void
  16.         {
  17.             trace("prepared for kill");
  18.         }
  19.        
  20.         public function asHuman ():IHuman
  21.         {
  22.             return _human;
  23.         }
  24.        
  25.         public function asMonster ():IMonster
  26.         {
  27.             return _monster;
  28.         }
  29.     }
  30. }
  31.  
  32. internal namespace werewolf_private;
  33.  
  34. internal class Monster implements IMonster
  35. {
  36.     private var _owner:Werewolf = null;
  37.    
  38.     public function Monster (owner:Werewolf)
  39.     {
  40.         _owner = owner;
  41.     }
  42.    
  43.     public function kill (victim:IHuman):void
  44.     {
  45.         _owner.werewolf_private::prepareToKill();
  46.         trace("human killed");
  47.     }
  48. }
  49.  
  50. internal class Human implements IHuman
  51. {
  52.     private var _owner:Werewolf = null;
  53.    
  54.     public function Human (owner:Werewolf)
  55.     {
  56.         _owner = owner;
  57.     }
  58.    
  59.     public function kill (victim:IMonster):void
  60.     {
  61.         _owner.werewolf_private::prepareToKill();
  62.         trace("monster killed");
  63.     }
  64. }

Таким образом, мы получили метод prepareToKill видимый только в пределах наших классов. Аналогичным образом можно расшарить и прочую логику. На этом можно было бы и остановиться, но есть один момент, каждая из составляющих как Human, так и Monster не являются Werewolf-ом. Решается это достаточно просто. Введем интерфейс IWerewolf, где и обозначим соответствующий API.

Actionscript:
  1. package
  2. {
  3.     public interface IWerewolf
  4.     {
  5.         function asHuman ():IHuman;
  6.        
  7.         function asMonster ():IMonster;
  8.     }
  9. }

Теперь остается только реализовать этот интерфейс всеми тремя классами.

Actionscript:
  1. package
  2. {
  3.     public class Werewolf implements IWerewolf
  4.     {
  5.         private var _monster:IMonster = null;
  6.         private var _human:IHuman = null;
  7.        
  8.         public function Werewolf()
  9.         {
  10.             super();
  11.             _monster = new Monster(this);
  12.             _human = new Human(this);
  13.         }
  14.        
  15.         werewolf_private function prepareToKill ():void
  16.         {
  17.             trace("prepared for kill");
  18.         }
  19.        
  20.         public function asHuman ():IHuman
  21.         {
  22.             return _human;
  23.         }
  24.        
  25.         public function asMonster ():IMonster
  26.         {
  27.             return _monster;
  28.         }
  29.     }
  30. }
  31.  
  32. internal namespace werewolf_private;
  33.  
  34. internal class Monster implements IMonster, IWerewolf
  35. {
  36.     private var _owner:Werewolf = null;
  37.    
  38.     public function Monster (owner:Werewolf)
  39.     {
  40.         _owner = owner;
  41.     }
  42.    
  43.     public function kill (victim:IHuman):void
  44.     {
  45.         _owner.werewolf_private::prepareToKill();
  46.         trace("human killed");
  47.     }
  48.    
  49.     public function asHuman ():IHuman
  50.     {
  51.         return _owner.asHuman();
  52.     }
  53.    
  54.     public function asMonster ():IMonster
  55.     {
  56.         return this;
  57.     }
  58. }
  59.  
  60. internal class Human implements IHuman, IWerewolf
  61. {
  62.     private var _owner:Werewolf = null;
  63.    
  64.     public function Human (owner:Werewolf)
  65.     {
  66.         _owner = owner;
  67.     }
  68.    
  69.     public function kill (victim:IMonster):void
  70.     {
  71.         _owner.werewolf_private::prepareToKill();
  72.         trace("monster killed");
  73.     }
  74.    
  75.     public function asHuman ():IHuman
  76.     {
  77.         return this;
  78.     }
  79.    
  80.     public function asMonster ():IMonster
  81.     {
  82.         return _owner.asMonster();
  83.     }
  84. }

Проверим что у нас получилось.

Actionscript:
  1. package
  2. {
  3.     import flash.display.Sprite;
  4.  
  5.     public class World extends Sprite
  6.     {
  7.         public function World()
  8.         {
  9.             var werewolf:Werewolf = new Werewolf();
  10.            
  11.             // "Кастим" werewolf как IMonster и как IHuman
  12.             trace(werewolf.asMonster() is IMonster);
  13.             trace(werewolf.asHuman() is IHuman);
  14.            
  15.             // Проверяем является ли каждая составляющая IWerewolf
  16.             trace(werewolf.asMonster() is IWerewolf);
  17.             trace(werewolf.asHuman() is IWerewolf);
  18.            
  19.             // Ну а здесь уже мистика :)
  20.             trace((werewolf.asMonster() as IWerewolf).asHuman() is IHuman);
  21.             trace((werewolf.asHuman() as IWerewolf).asMonster() is IMonster);
  22.            
  23.             // Попробуем убить нашим оборотнем как человека так и монстра
  24.             var knight:Knight = new Knight();
  25.             var zombie:Zombie = new Zombie();
  26.            
  27.             werewolf.asMonster().kill(knight);
  28.             werewolf.asHuman().kill(zombie);
  29.         }
  30.     }
  31. }

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

15 комментариев

Теги:

О выходе Flash Player 10 beta aka «Astro» не писал только ленивый и «Garbage Collector». Однако последний, из этих двух, все же решил исправиться.

Одной из «фишек» нового плеера стала возможность создавать свои собственные графические фильтры. Вот, собственно, об этом сегодня и пойдет речь.

Создание собственных фильтров стало возможно благодаря новому продукту от Adobe под названием Pixel Bender Toolkit (раннее известного как Adobe Image Foundation Toolkit). Pixel Bender Toolkit использует собственный C-подобный язык описания алгоритмов преобразования изображений. Что ж, давайте попробуем создать собственный фильтр и применить его в нашем ActionScript 3 приложении.

Шаг 1: Подготовка

Прежде чем приступить к нашему знакомству с обозначенной темой, нам придется немного подготовиться. Во-первых, нам потребуется сам Pixel Bender Toolkit для написания фильтра и его компиляции. А во-вторых, нам потребуется настроить свою среду разработки. Приступим.

Инструкцию по скачиванию и установке Pixel Bender Toolkit можно найти на Adobe Labs. О том, как настроить свою среду разработки, можно почитать следующие статьи:

Шаг 2: Написание и компиляция собственного эффекта

Для первого знакомства, я думаю, стоит взять простой эффект — сепия. Так как пока мои навыки написания собственных фильтров на Pixel Bender не велики, я решил воспользоваться готовым решением (правда код подвергся небольшой корректировке в свете некоторых изменений в языке).

<languageversion : 1.0;>

kernel Sepia
<   namespace:      "popforge::ImageProcessing";
    vendor:         "Joa Ebert";
    version:        1;
    description:    "A good looking sepia filter using Y transform";
>
{
    input image3 source;
    output pixel3 result;

    void evaluatePixel()
    {
        pixel3 color = sampleLinear(source, outCoord());

        float y = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b;

        float r = min(1.0, y + 0.19);
        float g = max(0.0, y - 0.055);
        float b = max(0.0, y - 0.22);

        result = pixel3(r, g, b);
    }
}

И так у нас есть исходный код фильтра. Скомпилируем его.

Запускаем Pixel Bender Toolkit и создаем новый фильтр (File → New Pixel Bender Kernel Filter). Теперь в окно редактора вставляем исходный код и компилируем его (File → Export Pixel Bender Byte Code for Flash). Полученный файл с расширение .pbj мы и будем использовать в нашем приложении.

Шаг 3: Написание приложения

Для демонстрации применения фильтра мы напишем простое приложение. В его задачи будет входить загрузка изображения, его отображение, загрузка фильтра и показ этого же изображения, но уже с применением фильтра. Дальше подробно расписывать не буду, ибо вся суть в коде. Остановлюсь лишь на некоторых моментах. Для применения фильтра нам потребуется новоиспеченный класс flash.display.Shader, который должен получить фильтр в бинарном виде. Результат применения фильтра мы отрисовываем в экземпляре Shape с помощью метода graphics.beginShaderFill.

Actionscript:
  1. package
  2. {
  3.     import flash.display.Bitmap;
  4.     import flash.display.Loader;
  5.     import flash.display.Shader;
  6.     import flash.display.Shape;
  7.     import flash.display.Sprite;
  8.     import flash.display.StageScaleMode;
  9.     import flash.events.Event;
  10.     import flash.net.URLLoader;
  11.     import flash.net.URLLoaderDataFormat;
  12.     import flash.net.URLRequest;
  13.    
  14.     [SWF(width='500', height='510', frameRate='31', backgroundColor='0x000000')]
  15.     public class RuntimePixelBenderExample extends Sprite
  16.     {
  17.         private var _image:Bitmap = null;
  18.         private var _shape:Shape = null;
  19.         private var _imageLoader:Loader = null;
  20.         private var _filterLoader:URLLoader = null;
  21.         private var _sepiaShader:Shader = null;
  22.        
  23.         public function RuntimePixelBenderExample()
  24.         {
  25.             stage.scaleMode = StageScaleMode.NO_SCALE;
  26.             loadImage();
  27.         }
  28.        
  29.         private function loadImage ():void
  30.         {
  31.             _imageLoader = new Loader();
  32.             _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
  33.             _imageLoader.load(new URLRequest("assets/image/bender.jpg"));
  34.         }
  35.        
  36.         private function loadFilter ():void
  37.         {
  38.             _filterLoader = new URLLoader();
  39.             _filterLoader.dataFormat = URLLoaderDataFormat.BINARY;
  40.             _filterLoader.addEventListener(Event.COMPLETE, onFilterLoaded);
  41.             _filterLoader.load(new URLRequest("assets/filter/Sepia.pbj"));
  42.         }
  43.        
  44.         private function applyFilter ():void
  45.         {
  46.             _sepiaShader.data.source.input = _image.bitmapData;
  47.            
  48.             _shape = new Shape();
  49.             _shape.y = 255;
  50.            
  51.             _shape.graphics.beginShaderFill(_sepiaShader);
  52.             _shape.graphics.drawRect(0, 0, 500, 255);
  53.             _shape.graphics.endFill();
  54.            
  55.             addChild(_shape);
  56.         }
  57.        
  58.         private function onImageLoaded (event:Event):void
  59.         {
  60.             _image = _imageLoader.content as Bitmap;
  61.             addChild(_image);
  62.             loadFilter();
  63.         }
  64.        
  65.         private function onFilterLoaded (event:Event):void
  66.         {
  67.             _sepiaShader = new Shader(_filterLoader.data);
  68.             applyFilter();
  69.         }
  70.     }
  71. }

Необходимость в загрузке фильтра во время исполнения иногда может вызвать некоторые трудности. Но мы можем это легко обойти, включив байткод фильтра в приложение на этапе компиляции (как это делается, можно узнать из статьи «Включение файлов в SWF в виде байтовой последовательности»). В этом случае наше приложении будет выглядеть так:

Actionscript:
  1. package
  2. {
  3.     import flash.display.Bitmap;
  4.     import flash.display.Loader;
  5.     import flash.display.Shader;
  6.     import flash.display.Shape;
  7.     import flash.display.Sprite;
  8.     import flash.display.StageScaleMode;
  9.     import flash.events.Event;
  10.     import flash.net.URLRequest;
  11.     import flash.utils.ByteArray;
  12.    
  13.     [SWF(width='500', height='510', frameRate='31', backgroundColor='0x000000')]
  14.     public class CompiledPixelBenderExample extends Sprite
  15.     {
  16.         private var _image:Bitmap = null;
  17.         private var _shape:Shape = null;
  18.         private var _imageLoader:Loader = null;
  19.        
  20.         [Embed(source="assets/filter/Sepia.pbj", mimeType="application/octet-stream")]
  21.         private var SepiaFilter:Class;
  22.        
  23.         private var _sepiaShader:Shader = null;
  24.        
  25.         public function CompiledPixelBenderExample ()
  26.         {
  27.             stage.scaleMode = StageScaleMode.NO_SCALE;
  28.             loadImage();
  29.         }
  30.        
  31.         private function loadImage ():void
  32.         {
  33.             _imageLoader = new Loader();
  34.             _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
  35.             _imageLoader.load(new URLRequest("assets/image/bender.jpg"));
  36.         }
  37.        
  38.         private function applyFilter ():void
  39.         {
  40.             _sepiaShader = new Shader(new SepiaFilter());
  41.             _sepiaShader.data.source.input = _image.bitmapData;
  42.            
  43.             _shape = new Shape();
  44.             _shape.y = 255;
  45.            
  46.             _shape.graphics.beginShaderFill(_sepiaShader);
  47.             _shape.graphics.drawRect(0, 0, 500, 255);
  48.             _shape.graphics.endFill();
  49.            
  50.             addChild(_shape);
  51.         }
  52.        
  53.         private function onImageLoaded (event:Event):void
  54.         {
  55.              _image = _imageLoader.content as Bitmap;
  56.              addChild(_image);
  57.              applyFilter();
  58.         }
  59.     }
  60. }

Исходный код проекта можно скачать здесь.

Послесловие

Новые возможности Flash Player 10 вскоре сильно изменят внешний вид будущих flash-приложений. И с одной из таких возможностей мы уже познакомились. Замечу, мы рассмотрели пример применения простого фильтра, не требующего параметризации. В остальных случаях читайте документацию (она идет в комплекте с Pixel Bender Toolkit), пишите фильтры и делайте мир лучше (жду русскоязычный блог про Pixel Bender). Что ж, на этом на сегодня все. Надеюсь вам было интересно.

Для тех, кто все еще здесь

Напоследок, несколько полезных ссылок:

7 комментариев

Теги: