The Asynchronous Texture Atlas Generator is a tool which you can run before your Game loads to convert the Vector Assets (symbols) in your Flash Professional library into TextureAtlases and Starling MovieClips and Images you can use in your Game.
Emibap has already created a dynamic textureAtlas generator here, (https://github.com/emibap/Dynamic-Texture-Atlas-Generator. But I havent been reinventing the wheel, my Dynamic Texture Atlas Generator improves upon his on 4 features, namely
That means that it can create the textureAtlases in the background while your Game runs its frames. With Emibap's generator , textureAtlases are created at once, and then only the rest of the Game code can be run, and the next frame rendered. If you have a large number of Vector assets to turn into a textureAtlas, your Game might appear to seize, or even TimeOut (15s!) while the texture is being created. Mine on the other hand, splits all the MovieClips to be rendered into TextureAtlases into small batches which are processed frame by frame, hence eliminating lag.
I have analysed Emibap's code, and it seems that he draws the Vector content into textures using a for { gotoAndPlay() ;draw ()}; loop. That means, that your movieClips are rendered into textures before any code in your MovieClip gets a chance to run.Lets say that you create a listener function to enlarge a circle in your movieCLip every frame
//code on the first frame of your circle_mc this.graphics.lineStyle(1,0x000000); this.graphics.beginFill(0xFF0000); this.graphics.drawCircle(0,0,50); this.graphics.endFill(); //code on your listener function enlarge(evt:Event){ this.circle_mc.scaleX+=0.1; this.circle_mc.scaleY+=0.1; } this.addEventListener(Event.ENTER_FRAME, enlarge);
WIth Emibap's generator, the circle does not get drawn at all. On top of that, even if you create the circle in the authoring environment, it does not get enlarged. But mine analyses the movieClip after the FRAME_CONSTRUCTED event, which means all code is run and all graphics are created (or altered) before the frame is drawn into a texture. That means that you can control your vector movieClips using ActionScript and have them rendered into textures, instead of having to manually alter them in the library all the time.
Using just one method, you can take the textureAtlases that the generator creates, extract the frames you want and turn them into a movieClip. Properties such as labels and pivot points are also captured as well. Have a flash MovieClip with the frame labels (“walking”) and (“running”), and whose registration point is at the center of the movieClip? No problem! These are also reflected in the FlashMovieClip (subclass of starling's MovieClip) which is produced!
If you enable debugMode, the individual textures which are being rendered by the generator appear on your stage. That way,if something goes wrong, you can spot them immediately and make changes. You can also export a preview bitmap of the textureAtlases you create, showing each individual frame and its name.
Due to time constraints, this doesnt support nitty gritty details such as transformations, filters etc like emibap's does, but why bother? You can add then yourself using ActionScript to your movieClips.
After you have downloaded the files, import the generator by calling
import drawer.*
Store the functionData and flashMovieCLip as3 files into your Game directory so that you can access then directly.
If you haven't done so, download AS3corelib by http://github.com/mikechambers/as3corelib, and import it so that the PNGencoder used by the generator will be compiled.
Once you have your symbols created in your library, create a linkage className for each of the symbols you want to export by right-clicking the symbol on the list, → Properties and then checking “export for ActionScript”. Type the className into the blank.
Create an array and populate it with the linkage names of your symbols. You can also add references of the instances already on your stage. E.g
var drawArray:Array = ["dog_mc","cat_mc","fish_mc", Object(root).fish1];
If your symbol class constructor contains parameters, you can create a functionData object like so:
var humanFunctionData:FunctionData = new FunctionData(null,"human_mc","male","138cm","140kg"); // ( null, nameOfYourClass, your parameters, ...);
And store the functionData into your array:
var drawArray:Array = ["dog_mc","cat_mc","fish_mc", Object(root).fish1,humanFunctionData];
Now before you do anything, comment out the gotoAndStops and GotoAndPlays and Stops() in your movieClip. this prevents the generator from having its current frame redirected and messing up the frame sequence.
Call the beginBatchDraw method
drawData.batchLimit = 5; //set the batchLimit of the texture renderer. The batch limit is the number of movieClips you want to process at once. The higher the number, the faster the render will complete, but your game may begin to lag. drawData.debugMode= true; //set debug mode to true or false. //If true, the individual frames of the movieCLip that is currently being rendered are tiled onto the screen. drawData.beginBatchDraw(null, yourArray, theStage); //where yourArray is the drawArray you have created and //theStage refers to the document stage. //The first parameter is an event which allows the batchDraw to be called as a listener function. But we don't need it here. So its null.
The renderer will begin drawing each individual frame of your movieClips. If debugMode is on, you see them being tiled on your screen, as well as an animation of the movieClip being played. Wait a while for the whole process to complete.
Now, I haven't have time to add a progress bar, so that will replace the tiling animation if debugMode is off. Stay tuned for an update.
Once everything has been rendered, the rendered images will be stored in drawData.completedDrawingList. You cant access the images directly from there, so you'll have to generate textureAtlases using the drawData.createTextureAtlas function.
//create an event listener on your stage to listen for when the entire rendering //operation has been completed. A drawEvent.BATCH_COMPLETE event will be dispatched //by the stage (as specified by theStage parameter in beginBatchDraw) once the //rendering has been completed stage.addEventListener(drawEvent.BATCH_COMPLETE, onDrawComplete); //create the listener function function onDrawComplete (evt:drawEvent):void{ ... }
The Listener function should do 2 things, One, create textureAtlases from the images of the movieClips that have been rendered Two, store them in an Assets object somewhere in your Game so that you can access them to create starling movieClips.
To create your textureAtlases, call the drawData.createTextureAtlas method for each texture atlas you want to create. If you have many graphic assets, you may want to create multiple atlases to group similar graphics.
var atlas:labelledTextureAtlas = drawData.createTextureAtlas(movieClipNamesArray, theStage, debugMode);
Where movieCLipNamesArray is an array containing the linkage names of the movieClips you passed to beginBatchDraw. For exaample, if you wanted to draw only dog_mc and cat_mc from the example drawList mentioned above, pass [“dog_mc”,“cat_mc”]. If you've included an instance of a movieCLip on the stage, pass its name into the array. For example, if root.fish1's name is fish1, pass “fish1” into the array. If you passed a functionData into beginBatchDraw, include the className into the array, e.g “human_mc”.
theStage is a reference to your stage.
Set debugMode to true if you want a preview of the textureAtlas to be saved to a file. If you've set this to true, a dialogue box asking you where you want to save the preview will show after the textureAtlas is generated.
The atlas that comes out of createTextureAtlas is a special atlas containing labels and pivot point properties of the clips. Thats why its a labelledTextureAtlas.
Now that you've created the atlas, you might want to store it on an Assets object so that you can manage your textures.
Simply call the drawData.cacheMovieClipData method. This method stores the frames , labels and pivot point of a movieClip in an object that you define, so that you can retrieve them later when instantiating your starling movieClips.
drawData.cacheMovieClipData(object, atlas, clipName,labels,cacheName);
Object is the place where you want your data to be stored. It should be your assets object. Instances of your assets object should be dynamic so you dont have to define a var for every clip you cache.
atlas is the atlas from where the frames of your movieClip can be found. ClipName is the name of the movieCLip as you passed in beginBatchDraw
Pass the frame labels in order you want them to be cached. For example if your human_mc has labels “stand”,“walk”,“run”, then pass them as an array into this parameter. Leave it null and all the labels will be passed in the order that you created them in the timeline.
cacheName is the string you want to reference your stored data from your assets object. more on that in the next section.
Ideally, you should create a class file for every type of starling MovieClip you have in your game. Your classes should subclass FlashMovieClip so that the labels work properly. The flashMovieCLip is a subclass of starling's movieclip which contains gotoAndPlay functions as well as labels.
For example, if you want to create a starling representation of your vector human_mc,
public class human_starling_mc extends FlashMovieClip { public function human_starling_mc ():void{ var vector:Vector.<Texture> = Assets["human_mc"+"vector"]; // where Assets is the place where you cached your human_mc clip data earlier. //you retrieve the textures which make up your movieClip from the variable [yourCacheName+"vector"]. //For example, if you cached your human_mc data as "human_mc" when you call cacheMovieClipData(), then access your textures through ["human_mcvector"]; var labels:Array = Assets["human_mc"+"labels"]; //again, same as above, but retrieve the labels from [yourCacheName+"labels"] instead. var pivot:Point = Assets["human_mc"+"pivotPoint"] //again, same as above, but retrieve the pivotPoint from [yourCacheName+"pivotPoint"] instead.
Now, call the superclass (flashMovieClip) constructor, and set the frameLabels, pivotX and pivotY properties from the values you've retrieved.
super(vector, frameRate); //where frameRate is the FPS of your stage this.frameLabels= labels; this.pivotX= pivot.x; this.pivotY= pivot.y;
In addition , you can create generic flashMovieClips from textureAtlases by calling FlashMovieClip.fromTextureAtlas () method.
var yourFlashMovieClip = FlashMovieClip.fromTextureAtlas(atlas,frameRate, clipName, labels);
atlas is a reference to the labelledTextureAtlas which contains the frames of your movieCLip
frameRate is your stage FPS
clipName is the name of the clip as passed in your beginBatchDraw array.
labels is an array containing the framelabels in order , which you want to be included in your flashMovieClip. Leave null and all the labels are included in the order which you created them in the IDE.
For your movieClips to play properly, you need to first call FlashMovieClip.init() and pass the starling stage as a parameter.
The flashMovieClip class is a subclass of starling's movieClip that animates like flash Movie Clips.
They contain the following properties: addLabel(frameNum, label) Causes a label to be associated with a particular frame. Overwrites any existing labels if they exist on the frame.
AddFrameScript (frame, script) Adds a frame script to a frame label or frame number. the script can be a reference to a function or a functionData object containing the function and the parameters:
functionData (thisObject, function, arguments,....);
Set thisObject to the flashMovieClip itself, and function to the function you want to execute, for example, this.gotoAndPlay . Add your argument to the function to the rest of the parameters, e.g “running”.
The frameScript will run once that frame or label is reached.
gotoAndPlay() and gotoAndStop() Works like their lookalikes in the flash.display.MovieClip. You can pass a frameNumber or label as the parameter.
Loop(frameLabel) Causes the movieClip to loop over a specific frame label infinitely. For example, if your “running” label spans 12 frames which depict your movieClip running, the run animation will play until you call other function which affect the playing of your movieCLip, such as gotoAndPLay.
Repeat (frameLabel, times) Causes the movieCLip to repeat over a frameLabel for the number of times you specify. Once the repeats are complete, the clip stops at the first frame of the repeat.
PlayOnce (frameLabel) Causes the movieCLip to goto a frameLabel and play until it reaches the next label. It will stop at the frame directly before the next label.
currentLabel, currentFrameLabel Just like in flash, refers The label of the frame the movieCLip is currently playing (currentFrameLabel), or the previous label closest to it (currentLabel).
Source code is stored in the linked github repository.
All those “dynamic generators are drawing one by one each hierarchy childs and then place them in a sprite and then draw it again”.. to many BitmapData.draw calls….better way is to parse flash movieclip hierarchy and palce all objects in the way that they fit an texture atlas. After just one BitmapData.draw call to get all texture atlas drawn. :) Simple
It would be very nice to have a complete working example, I'm having dificulties to get it working.
Actually I can't get it to work
var mc:SheetMC = new SheetMC(); var drawArray:Array = [Object(mc).boton01, null]; drawData.batchLimit = 5; drawData.debugMode= true; var nativeMC:flash.display.MovieClip = Starling.current.nativeStage.addChild(new flash.display.MovieClip()) as flash.display.MovieClip; drawData.beginBatchDraw(null, drawArray, nativeMC);
Produces the following error
TypeError: Error #1034: Type Coercion failed: cannot convert flash.display::MovieClip@599ac11 to dynamictexture.FunctionData. at dynamictexture.drawer::drawData$/addDrawElement()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\dynamictexture\drawer\drawData.as:321] at dynamictexture.drawer::drawData$/beginBatchDraw()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\dynamictexture\drawer\drawData.as:289] at HelloFeathers/addedToStageHandler()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\HelloFeathers.as:35] at starling.events::EventDispatcher/invokeEvent()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\events\EventDispatcher.as:137] at starling.events::EventDispatcher/dispatchEvent()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\events\EventDispatcher.as:109] at starling.display::DisplayObjectContainer/broadcastEvent()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\display\DisplayObjectContainer.as:363] at starling.display::DisplayObjectContainer/broadcastEventWith()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\display\DisplayObjectContainer.as:373] at starling.display::DisplayObjectContainer/addChildAt()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\display\DisplayObjectContainer.as:127] at starling.core::Starling/initializeRoot()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\core\Starling.as:340] at starling.core::Starling/initialize()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\core\Starling.as:314] at starling.core::Starling/onContextCreated()[E:\workspace\LG_AireBueno_Retail\proyectoAndroid\src\starling\core\Starling.as:519]
I cant get this to work… And i tried…
I can't find a part in the code where the pivot points are set to atlas.mPivotPoints[clipName]. Where can I find the fully working code that handles pivot points correctly? Thank you.
Feel free to edit this part of the page!