Hi, Habr!
I am writing for the first time, I am a little nervous, so for the fourth time I am typing the first sentence.
I am developing games. Mostly on flush. More than a year worked with HaXe NME - cool. But a lot of their own "frills", which, when compared with AS3 when targeting a flash, can be placed in a separate article.
For more than two years of experience, I identified one urgent problem for me when creating flash games.
This is the initial initialization. The problem is that while the user looks at the progress bar of the preloader, a lot of operations are performed in the background - from loading data and assets to preparing them. And, often, the number of these operations rolls over even at the very beginning of development, not to mention updates.
I watched a lot of other sources of games and saw terrible conglomerations of constructions of the form:
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 {
At the same time, they do not always use some auxiliary singleton-classes in the methods, and often a lot of loader-s appear in the methods of the loading class for each operation with all the attendant listeners. Hash is not read absolutely. He also did it once.
The taste and color, as they say, but when the number of methods for initialization grows to at least ten, it becomes difficult to read such code. And if you later suddenly need to insert an additional intermediate function, say, for additional processing of the data, it turns into a headache and a lot of time spent trying to figure out what is going on.
This problem is relevant not only for downloading data. And also, for example, sequential animation of interface elements. Someone will argue that you can animate even in the Flash IDE - no doubt. However, often when animating the appearance of a window, it is necessary not only to show the elements in order, but also to activate / deactivate them, so that no accidental closure of the window occurs by a user click until the animations are completed.
The task seems to be clear and quite trivial. I see the goal. Going to the goal.
The utility I wrote is quite small and quite simple for myself. However, she really helped me to move from the structures above to a very concise:
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); }
All methods will be called in the same sequence in which they are added.
The sequence of actions, plus a relatively easy readability queue. It would seem that more needed?
Full code of class Instruction package { 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; } } }
There are several drawbacks to this structure. First, the functions that we want to push into the queue must correspond to a certain structure, namely, the first parameter is the callback for completing the method, the remaining parameters are the arguments. A positive point is that you can stuff a callback as it loads data into the Event.COMPLETE handler, or just call it last, after performing various operations:
')
function weWantToAddToInstruction(completeHandler:Function, paramOne:Object, paramTwo:String, paramThree:Function, ...params):void {
Also, the negative point is that the error handling in the instruction being executed lies on the shoulders of the methods being executed. I have not yet come up with a solution that will make it possible to catch errors at the responsibility of the instruction itself. In the variants that I tried, I had to change the structure of functions => not conveniently. And in many situations, everything came to a standstill. That would be great.
Another side effect was that, having started using the instruction, I often started to produce anonymous functions inside the called methods, so that before calling the completeHandler some other function or setting a flag worked, which is not correct. But this is rather the result of my laziness. Since in such cases, inside the method that is called inside the instruction, you can create another instruction and arrange the code, bypassing the anonymous letter.
The positive aspect was that methods can be stuffed into the instruction, the arguments for which are absolutely arbitrary, both in content and in quantity. This is quite convenient. But only if it is firmly convinced that the arguments are precisely those that are conceived. Although, this rule applies to everything, I guess.
In general, that's all. Thank you for your attention, habrazhiteli.
Very cool, if what I wrote, will be useful to someone.