В последнее время я сильно погряз в болоте работы, сроки сдачи которой давно проебал. Собственно, в болоте этом я сижу все время, но обычно не больше чем по колено, а тут накрывает с головой. Тем не менее, я отчаянно барахтаюсь в попытке выбраться из отвратительной трясины лени и безответственности.
В скором времени начну выкладывать некоторые наработки, эксперименты, идеи.
Check out cool app description...

Adobe Dreamweaver Widget Browser
download link
Приветствую, дамы и господа разработчики!
Обращаюсь к тем членам нашего комьюнити, ощущающих острую нехватку "огонька" и денюшек, - экстрима в общем.
Все желающие участвовать в интересных и не очень, но чрезвычайно горячих проектах, в качестве скорой программерской помощи - милости прошу (мыло в конце поста).
Меня в первую очередь интересуют реально крутые монстры своего ремесла с правдивым опытом работы от трёх лет и могущих мономоментно сорваться с места и полностью погрузиться в разруливание сложных по реализации или по срокам задач.
Новички, желающие получить себе в резюме пару красивых строчек, в бумажник положить пару красивых бумажек, поработать с великими мира сего - вы нужны.
Работает это все так:
-вы присылаете мне письмо, в котором пишете:
--ФИО (+ в теме письма)
--Опыт работы (срок службы в рядах флэш-разработчиков)
--Список компаний, с которыми вы лично сотрудничали
--Специализация (что лучше и быстрее всего получается)
--Контакты, по которым вас можно выцепить где угодно (в разумное время суток естественно)
--Способы расчета с вами
--другая информация, в т.ч. ссылки на работы, резюме и т.п.
- и всё.
А потом ВНЕЗАПНО получаете что-то вроде "За две недели надо сделать такую игрушку вот по этому ТЗ. Денег столько, сроки такие, дизайн есть. Берешься?" и понеслась..
Электропочта: EmergencyFlashPlatformWorldHelp@ Kozlovskij.ru или FlashImp.ru
На заграничных прилавках появилась первая книжка про Flex 4: "Hello! Flex 4". Автор: Peter Armstrong.
PDF ebook стоит $22.50.
Serge Jespers опубликовал Package Assistant, о котором писал ранее.
Текущая версия: "public alpha".
Скачать можно тут (.dmg).
Это AIR2-приложение представляет из себя некоего "помощника" для сборки приложений с нативными (встроенными) инсталяторами. Эти самые инсталяторы - есть одна из фич AIR2.
Так вот, чтобы воспользоваться этой вкусняжкой, приходилось иметь дело с ADT (command line compiler) , что не очень удобно. А Package Assistant дает нам UI к компилятору.
Вот, что пишет сам автор:
The wizard style application allows you to easily identify the files it needs and after just 4 steps you simply
hit the compile button. A lot easier than fiddling around with the command line
Можно посмотреть скринкаст (недельной давности).
Позитивная новость для геймдива:
В полку сервисов всеми любимой Adobe прибыло. Точнее, прибудет.
Adobe анонсировала открытие Flash Platform Game Technology Center - ресурса для разработчиков игр на Flash Platform.
Украдено у Алексея Яковенко.
UPD: Только что на девнете была опубликована статья Danielle Deibler Introducing the Adobe Flash Platform Gaming.
Для начала несколько слов прелюдии.
Примерно полтора года назад я накодил простенький передзагрузчик (аналог), стартующий до инициализации главного класса приложения, и периодически его дополнял и частично переписывал. Таким образом один класс вырос в целую систему, поддерживающую модульную архитектуру, полиморфизм, и предзагрузку необходимых ассетов.
Эту разработку я частенько сам использую в больших многомодульных проектах, где есть одна или несколько библиотек (swc) и огромное количество модулей, эти библиотеки использующие; Предзагрузчик у всех модулей - один, следовательно вынесен в библиотеку, а не клонируется по всей команде разработчиков от проекта к проекту. При этом (частая ситуация) хорошо было бы, что бы все модули приложения могли работать как самостоятельно, так и внутри любого другого модуля, - т.е. полиморфизм. Вот эту ситуацию и возьму за основу.
По-сути, мое решение представляет из себя неслабое универсализированное расширение уже давно описанных метод: тут (по-русски), тут и тут - настоятельно рекомендую ознакомиться перед продолжением.
Теория.
Добавляя в Главный Класс метадату:
[Frame(factoryClass="ru.kozlovskij.preload.Preloader")]
мы получаем вот такой результат:
[Схема с нашим классом]
Но все было бы чудесно, если б не одна проблема: Наш предзагрузчик понятия не имеет об имени главного класса приложения, который ему надо инстанциировать, а заниматься жестким хардкодом в большом проекте - непозволительная роскошь. Первая мысль была такова:
В каждом проекте-модуле использовать в качестве предзагрузчика субкласс того основного, что находится в библиотеке и остается неизменным. В этом субклассе мы просто переопределяем значение protected переменной MAINCLASS_NAME, ранее заведенной в основном классе и далее используем ее при инстанциировании нужного нам главного класса приложения. Но это мне вскоре надоело (неудобно) и было придумано более интересное решение:
Предзагрузчик наш остается в библиотеке, не имеет никаких субклассов (можно даже сделать его final). После загрузки всей свифки он сам найдет нужное ему имя класса в метаданных, а в голове главного класса мы просто впишем свой кастомный метатэг:
[App(className='ИмяГлавногоКласса')]
Что бы компиллер "увидел" наш метатэг, в параметры компилятора (в свойствах проекта) впишем:
-load-config cfg.xml
(про параметры компилятора подробности тут)
А в конфиге мы явно укажем, что на наш метатэг стоит обратить внимание:
cfg.xml:
<flex-config>
<compiler>
<keep-as3-metadata>
<name>App</name>
</keep-as3-metadata>
</compiler>
</flex-config>
Main.as - главный класс нашего приложения:
package {
import flash.display.FrameLabel;
import flash.display.MovieClip;
import flash.events.Event;
import flash.utils.*;
import ru.kozlovskij.core.Application;
import ru.kozlovskij.preload.Preloader;
/**
* SWF metadata
*/
[SWF(backgroundColor='0xFFCC00', width='500', height='500')]
/**
* The frameworks must be initialized by SystemManager.
* This factoryClass will be automatically subclassed by any
* MXML applications that don't explicitly specify a different
* factoryClass.
*
* NOTE: Minimize the non-Flash classes you import here.
* Any dependencies of SystemManager have to load in frame 1,
* before the preloader, or anything else, can be displayed.
*/
[Frame(factoryClass='ru.kozlovskij.preload.Preloader')]
//[Frame(factoryClass='exampleClasses.MyPreloader')]
/**
* Custom metadata tags
*/
[App(type='module', className='Main')]
[copyrights(firstName='Aleksandr', lastName='Kozlovskij', url='http://Kozlovskij.ru', blog='http://FlashImp.ru')]
public class Main extends Application
{
public function Main()
{
super();
addEventListener(Preloader.PRELOADER_DONE, preloaderDoneHandler);
}
private function preloaderDoneHandler(e:Event):void
{
//initialize...
for(var i:int; i < (parent as MovieClip).currentLabels.length; ++i)
trace('FrameLabel:', '[', FrameLabel((parent as MovieClip).currentLabels[i]).name,
',', FrameLabel((parent as MovieClip).currentLabels[i]).frame, ']');
(parent as MovieClip).nextFrame();
parent.addChild(new (flash.utils.getDefinitionByName('exampleClasses.CustomFrame') as Class)());
}
}
}
Метод preloaderDoneHandler вызывается, когда от предзагрузчика мы получаем событие об окончании загрузки всего необходимого (+ассеты?). В preloaderDoneHandler мы, удовлетворения ради, выводим в трэйс все, имеющиеся в нашем распоряжении (в родителе-предзагрузчике) фрэймы. В последних двух строках мы переходим в третий кадр и инстанциируем класс, который был загружен последним - об этом чуток ниже.
Во многих моих реализациях данного подхода я либо создавал в Main классе метод, возвращающий массив урлов к тому, что еще необходимо загрузить, что было несколько криво и не гибко, либо записывал эту информацию в метаданные, что позволяло предзагрузчику все необходимое скачать еще до инициализации главного класса, а он в свою очередь рождался "на всем готовом". Нюансы реализацию такой инициализации приложения я покажу позже, а сейчас - простой пример того самого предзагрузчика:
Preloader.as - собственно, сам прелоудер:
package ru.kozlovskij.preload
{
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.system.ApplicationDomain;
import flash.utils.ByteArray;
import ru.etcs.utils.ClassExplorer;
[Event(name="preloaderDone", type="ru.kozlovskij.preload.Preloader")]
public class Preloader extends MovieClip
{
public static const PRELOADER_DONE:String = 'preloaderDone';
protected var MAINCLASS_NAME:String;
protected var preloaderGraphics:IPreloaderGraphics;
private var Main:Class;
private var main:DisplayObject;
protected var _standalone:Boolean = true;
protected function get standalone():Boolean
{return _standalone;}
public function Preloader(preloaderGraphics:IPreloaderGraphics = null)
{
stop();
this.preloaderGraphics = preloaderGraphics ? preloaderGraphics : new PreloaderGraphics();
if(stage)
stage.scaleMode = StageScaleMode.NO_SCALE,
stage.align = StageAlign.TOP_LEFT,
stage.frameRate = 31,
createGraphics(),
stage.addEventListener(Event.RESIZE, resizeHandler);
else
_standalone = false;
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
resizeHandler();
}
protected function createGraphics():void
{
addChild(preloaderGraphics as DisplayObject);
}
private function enterFrameHandler(e:Event):void
{
//percent = current / total
var percent:Number = root.loaderInfo.bytesLoaded / root.loaderInfo.bytesTotal;
preloaderGraphics.percent = percent * 100;
//trace(Math.round(100 * percent) + '%');
if(framesLoaded == totalFrames)
{
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
//Move to the next frame when we're done
//Moving to frame 1(!)
nextFrame();
preinit();
}
}
protected function resizeHandler(e:Event = null):void
{
preloaderGraphics.x = stage ? stage.stageWidth / 2 : root.width / 2,
preloaderGraphics.y = stage ? stage.stageHeight / 2 : root.height / 2;
}
private function preinit(mainclass_name_from_metadata:String = null):void
{
var needThrow:Boolean = true;
MAINCLASS_NAME = mainclass_name_from_metadata ? mainclass_name_from_metadata :
MAINCLASS_NAME ? MAINCLASS_NAME : null;
MAINCLASS_NAME ? null : (needThrow = false, getMETAMainClassName());
if(needThrow && !MAINCLASS_NAME)
{
clear();
throw new Error(this.toString() + ': Main Class Definition eq null');
}
var has:Boolean = ApplicationDomain.currentDomain.hasDefinition(MAINCLASS_NAME);
if(has)//if main app
Main = ApplicationDomain.currentDomain.getDefinition(MAINCLASS_NAME) as Class;
else
{
has = loaderInfo.applicationDomain.hasDefinition(MAINCLASS_NAME);
if(has)//if module app
Main = loaderInfo.applicationDomain.getDefinition(MAINCLASS_NAME) as Class;
}
if(has)
{
//and instantiate our Main class
main = new Main();
//get urls, whot need to additional preloading before starting application (in next post..)
init();
clear();
}
else
if(needThrow)
{
clear();
throw new Error(this.toString() + ': Main Class Definition NOT FOUND');
}
}
private function init():void
{
//finally we add main class to the DisplayList and dispatching event
addChild(main as DisplayObject).dispatchEvent(new Event(PRELOADER_DONE));
//stage.addChild(main as DisplayObject) - only if we make a standalone app.
}
public function clear():void
{
//killing & clearing preloaderGraphics :)
IPreloaderGraphics(removeChild(preloaderGraphics as DisplayObject)).clear();
standalone ? (stage.removeEventListener(Event.RESIZE, resizeHandler), stage.removeChild(this)) : null;
}
//-----------------------------------------------------//
private function getMETAMainClassName():void
{
var bytes:ByteArray = new ByteArray();
bytes.objectEncoding = 3;
bytes.writeBytes(loaderInfo.bytes);
bytes.position = 0;
var explorer:ClassExplorer = new ClassExplorer();
explorer.loadBytes(bytes);
explorer.addEventListener(Event.COMPLETE, explorerWorkCompleteHandler);
}
private function explorerWorkCompleteHandler(e:Event):void
{
var explorer:ClassExplorer = e.target as ClassExplorer;
var xml:XML = explorer.getDefinitionInfo();
//trace(xml.toXMLString());
var className:String;
className = xml.definition/*.(@name == 'frame2')*/.script.classTrait.metadata.(@name == 'App').arg.(@key == 'className').@value.toString();
preinit(className);
}
}
}
Как видно, в данной реализации я оставил возможность наследовать этот класс и переопределять значение переменной MAINCLASS_NAME. Так же в конструктор можно передать экземпляр визуализатора процесса загрузки, реализующий интерфейс IPreloaderGraphics. По умолчанию создается инстанс PreloaderGraphics - простейший субкласс DisplayObject'a, содержащий в себе текстовое поле для отображения процентов - это я тоже упростил:)
ru.kozlovskij.preload.PreloaderGraphics:
package ru.kozlovskij.preload
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
public class PreloaderGraphics extends Sprite implements IPreloaderGraphics
{
private var text:TextField = new TextField();
public function PreloaderGraphics()
{
super();
addChild(text);
text.defaultTextFormat = new TextFormat('Tahoma');
text.defaultTextFormat.align = TextFormatAlign.CENTER,
text.defaultTextFormat.size = 13,
text.defaultTextFormat.letterSpacing = 1,
text.defaultTextFormat.bold = true;
text.borderColor = text.textColor = 0x0,
text.selectable = !(text.border = true),
text.width = 100,
text.height = 50;
percent = 0;
}
private var _percent:Number = 0;
public function set percent(value:Number):void
{
_percent = value;
text.text = 'Loading...\n' + uint(value) + '%';
}
public function get percent():Number
{
return _percent;
}
public function clear():void
{
removeChild(text);
}
}
}
Вернемся к ru.kozlovskij.preload.Preloader. Как это все работает?
Запускается конструктор, подписываемся на событие Event.ENTER_FRAME и постоянно проверяем, не завершилась ли загрузка; Загрузка завершена, находим в метаданные и в них - имя главного класса (preinit->getMETAMainClassName->explorerWorkCompleteHandler->preinit), инициализируем главный класс, не забыв перед этим перескочить на второй кадр, - вот такая последовательность.
В демонстрируемой реализации в качестве "поисковика метадаты" я использовал ClassExplorer, за что отдельное спасибо автору.
Для наших целей пришлось немного его подправить. Не помню уже где и что исправлял - интересующимся поможет любой мержер, например, Araxis Merge, который я считаю очень достойным.
Единственный минус - это то, что при каждом добавлении нового метатэга, необходимо прописывать его в конфиге компиляции.
Теперь от третьим кадре:
Для того, чтобы в нашем рутовом классе (рутовый он только в случае, если приложение самостоятельное, не модуль), являющимся по-совместительству предзагрузчиком, при компиляции создался третий кадр, конфигурационный файл необходимо дополнить следующим образом:
cfg.xml:
<flex-config>
<compiler>
<keep-as3-metadata>
<name>App</name>
</keep-as3-metadata>
</compiler>
<frames>
<frame>
<label>newFrameName</label>
<classname>exampleClasses.CustomFrame</classname>
</frame>
</frames>
</flex-config>
Класс exampleClasses.CustomFrame будет экспортирован во фрэйм "newFrameName". Третьим этот фрэйм будет потому, что два уже есть.
По непонятной мне причине, добавлять ноды для четвертого, пятого и т.д. фрэймов - бесполезно.
Теперь смотрим в Main.as на метод preloaderDoneHandler. Последние два строки - по-сути, тоже самое, что и в ru.kozlovskij.preload::Preloader, т.е. переход на следующий кадр и инициализация соответствующего класса.
Уф.. Вот и все. Наконец, исходный код (экспорт проекта).
П.С.: Совет: Не подписывайтесь на ProgressEvent.PROGRESS к собственному же loaderInfo!
Написал VBS скриптец для пакетной компиляции .pbk файлов. Работает только под окнами.
Как работает: Принимает пачку входящих параметров - [fullUrlFile.pbk ...], выбрасывает все, что не .pbk, для каждого вызывает компилятор.
Принимает необязательные параметры:
- -pb-C:\url\to\pbutil.exe
- -bin-C:\url\to\
Написано кривовато, ибо в VBS я на момент написания абсолютно ничего не смыслел.
Применять эту штуку удобно в случае, если мы пишем и отлаживаем код в Pixel Bender Toolkit, а самих кернелов (.pbk) в проекте много. Приходится либо вызывать компиляцию для каждого обновленного ручками из Pixel Bender Toolkit-а, либо, если пользуемся плагином для Eclipse под названием PBDT, то сохранять каждый pbk, не, что тоже долго и неприятно.
Решение для Eclipse/FB: Просто подключаем этот vbs-скрипт как екстернал-компилятор и указываем в параметрах папку, куда следует складывать готовые .pbj. И все обновившиеся кернелы скомпилятся при следующем билде.
Конкретика:
- Создаем в Project > Properties > Builders > .. новый билдер, называем его PBBCR;
- Заменяем свой .externalToolBuilders\PBBCR.launch на аналог из скачанного архива;
- Снова открываем Project > Properties > Builders > PBBCR > в поле Arguments первым параметром пишем/правим путь к PBBCR.vbs;
- (опционально) Там же в поле Arguments пишем/правим путь к pbutil.exe (он есть в папке Pixel Bender-a начиная с 1.5.1 версии) и путь к папке, куда складывать бинарники .pbj.
- Не забываем в списке билдеров Project > Properties > Builders > поставить наш PBBCR на самый верх, дабы он срабатывал первый.
Сам скрипт, пример .project, пример .launch в архиве.
Единственный нюанс: Если сами указываем output-папку и она реально не существует, ее необходимо создать самостоятельно - скрипт сам не умеет творить такие чудеса.