Привет, Хабр!
Пишу впервые, немного нервничаю, поэтому уже в четвертый раз набираю первое предложение.
Я занимаюсь разработкой игр. Преимущественно на флеше. Больше года работал с HaXe NME — круто. Но много своих «выкрутасов», которые, если сравнивать с AS3 при таргете во флеш, можно уместить в отдельную статью.
За более чем два года опыта выделил одну насущную для меня проблему при создании флеш игр.
Это первичная инициализация. Проблема заключается в том, что пока пользователь смотрит на прогресс-бар прелоадера, в бэкграунде выполняется множество операций — от загрузки данных и ассетов до их подготовки. И, зачастую, количество этих операций зашкаливает даже в самом начале разработки, не говоря уже об обновлениях.
Очень много смотрел чужие исходники игр и видел ужасные нагромождения конструкций вида:
/** * calling server (pseudo-code) */ private function startInitialization():void { ServerConnection.instance.call('method', params, onCallMethodComplete); } private function onCallMethodComplete():void { ServerConnection.instance.call('methodTwo', params, onCallMethodTwoComplete); } private function onCallMethodTwoComplete():void { AssetsLoader.loadAssetZipByName('assetName', onAssetLoaded); } private function onAssetLoaded():void { // etc }
При этом, не всегда пользуются какими-то вспомогательными синглтон-классами в методах, и зачастую в методах класса загрузки появляется куча loader-ов для каждой операции со всеми вытекающими слушателями. Мешанина не читаемая абсолютно. Сам так же делал когда-то.
На вкус и цвет, как говорится, но когда количество методов для инициализации вырастает хотя бы до десятка — читать такой код становится трудно. А если впоследствии внезапно понадобится вставить дополнительную промежуточную функцию, скажем, для дополнительной обработки полученных данных, — это превращается в головную боль и уйму времени, потраченного на попытки разобраться что за чем идёт.
Такая проблема актуальна не только для загрузки данных. А так же, например, последовательной анимации элементов интерфейса. Кто-то возразит, что анимировать можно и во Flash IDE — спору нет. Однако, зачастую при анимации появления окна требуется не только показывать элементы по порядку, но ещё и активировать/деактивировать их, чтобы не возникало случайных закрытий окна по пользовательскому клику до завершения анимаций.
Задача вроде бы понятная и достаточно тривиальная. Вижу цель. Иду к цели.
Утилитка, которую я написал, — весьма небольшая и вполне себе простая. Однако, она очень помогла мне перейти от конструкций, приведенных выше, к вполне лаконичному:
private function init():void { Instruction.create() .add(configureViewTree) .add(initializeManagers) .add(SocialData.instance.init, flashVars) .add(GameData.instance.init, 'http://server.ru/', 'http://static.server.ru/assets/') .add(Assets.getZip, 'music/music.zip') .add(MetaData.instance.init) .add(World.instance.create, worldContainer) .execute(startGame); }
Все методы вызовутся в той же последовательности, в которой они добавлены.
Последовательность действий плюс относительно удобная читаемость очереди. Казалось бы, что ещё нужно?!
package { /** * ... * @author Frost */ public class Instruction { public static function create():Instruction { return new Instruction(); } private var functionsQueue:Vector.<Function>; private var argumentsQueue:Vector.<Array>; public function Instruction() { functionsQueue = new Vector.<Function>(); argumentsQueue = new Vector.<Array>(); } public function add(calledFunction:Function, ...params):Instruction { functionsQueue.push(calledFunction); argumentsQueue.push(params); return this; } public function execute(completeHandler:Function = null, ...params):void { add(completeHandler, params).checkQueue(); } private function doTask():void { var calledFunctionArguments:Array = argumentsQueue.shift(); calledFunctionArguments.unshift(checkQueue); functionsQueue.shift().apply(null, calledFunctionArguments); calledFunctionArguments = null; } private function checkQueue():void { if (functionsQueue.length > 1) { doTask(); return; } completeInstruction(); } private function completeInstruction():void { if (functionsQueue[0] != null) { functionsQueue.shift().apply(argumentsQueue.shift()); } functionsQueue = null; argumentsQueue = null; } } }
Минусов у подобной структуры несколько. Во-первых, функции, которые мы хотим запихнуть в очередь, должны соответствовать определённой структуре, а именно — первым параметром идёт колбек на завершение метода, остальные параметры — это аргументы. Положительным моментом может служить то, что колбек можно запихнуть как на загрузку данных в обработчик Event.COMPLETE, так и просто поставить его вызов последним, после проведения различных операций:
function weWantToAddToInstruction(completeHandler:Function, paramOne:Object, paramTwo:String, paramThree:Function, ...params):void { // do some crazy stuff completeHandler(); }
Так же, к минусу можно отнести и то, что обработка ошибок в выполняемой инструкции лежит на плечах выполняемых методов. Я пока не придумал решение, которое позволит вынести отлов ошибок под ответственность самой инструкции. В вариантах, что я пробовал — приходилось сильно менять структуру функций => не удобно. Да и во многих ситуациях всё заходило в тупик. Так-то было бы здорово.
Ещё одним, но побочным эффектом, оказалось то, что, начав использовать инструкцию, я стал часто плодить анонимные функции внутри вызываемых методов, чтобы до вызова completeHandler сработала ещё какая-нибудь функция или выставление флага, что не правильно. Но это, скорее, результат моей лени. Т.к. в таких случаях внутри метода, который вызывается внутри инструкции, — можно создать ещё одну инструкцию и упорядочить код, обойдя стороной анонимки.
Положительным аспектом стало то, что в инструкцию можно запихивать методы, аргументы для которых — абсолютно произвольные, как по содержанию, так и по количеству. Это весьма удобно. Но лишь в том случае, если твёрдо уверен, что аргументы — именно те, которые задуманы. Хотя, это правило применимо ко всему, наверное.
В общем-то это всё. Благодарю за внимание, хабражители.
Очень здорово, если то, что я написал, будет кому-то полезно.
ссылка на оригинал статьи http://habrahabr.ru/post/207308/
Добавить комментарий