So I’ve been pretty deep building onto my Starling TD tutorial code to make an actual TD game, and every once in a while you have to just make something small and fun. I wrote a little Bitmap builder class where you pass in an image, a layer to draw it on, and some params, and the class takes apart your image in pixel “chunks” and redraws those chunks back onto the draw layer.

Take a look at the DEMO before checking out the code. Change some settings and click the “Draw” button in the bottom right.

You can also download the project folder.

I’m going to skip the boring setup part of the code and just jump right into the actual utility.

package com.zf.utils
{
   import flash.geom.Rectangle;
	
   import starling.animation.Transitions;
   import starling.core.Starling;
   import starling.display.Image;
   import starling.display.Sprite;
   import starling.textures.SubTexture;
   import starling.textures.Texture;
	
   public class BitmapBuilder
   {
      public static const BUILD_BOTTOM_UP:String = 'bottomUp';
      public static const BUILD_TOP_DOWN:String = 'topDown';
      public static const START_BOTTOM:String = 'bottom';
      public static const START_RANDOM:String = 'random';
		
      private static var image:Image;
      private static var buildType:String;
      private static var callBack:Function;
      private static var chunkSize:int;
      private static var canvas:Sprite;
      private static var pieceTransition:String;
      private static var totalTime:Number;
      private static var startDir:String;
      private static var pieces:Array;
      private static var pieceStartAlpha:Number;
      private static var pieceStartRotation:Number;
      private static var pieceStartScaleX:Number;
      private static var pieceStartScaleY:Number;
      private static var piecesComplete:int;
		
      public function BitmapBuilder() {}
		
      /**
       * BitmapBuilder's only public function. It gets called to parse and draw the Sprite in pieces.
       * 
       * @param {Image} img The Image we're pulling apart piece by piece to redraw
       * @param {Sprite} layer The Sprite to draw things to
       * @param {Object} [params=null] The params for the starting values of the build
       */
      public static function build(img:Image, layer:Sprite, params:Object = null):void {
         image = img;
         canvas = layer;
			
         // setup individual params and their defaults if no values were passed in
         callBack = params.onComplete || null;
         buildType = params.buildType || BUILD_BOTTOM_UP;
         pieceTransition = params.transition || Transitions.LINEAR;
         totalTime = params.time || 3;
         startDir = params.startDirection || START_BOTTOM;
         chunkSize = params.chunkSize || 5;
         pieceStartAlpha = params.pieceStartAlpha || 0;
         pieceStartRotation = params.pieceStartRotation || 0;
         pieceStartScaleX = params.pieceStartScaleX || 1;
         pieceStartScaleY = params.pieceStartScaleY || 1;
			
         pieces = [];
         piecesComplete = 0;
			
         var w:int = image.width,
             h:int = image.height,
             wCt:int = 0,
             hCt:int = 0,
             rect:Rectangle,
             piece:Image,
             imgT:Texture = image.texture;
			
         while(hCt < h) 
         {
            while(wCt < w) 
            {
               rect = new Rectangle(wCt, hCt, chunkSize, chunkSize);
               piece = new Image(new SubTexture(imgT, rect));
               pieces.push(piece);
					
               wCt += chunkSize;
            }
				
            wCt = 0;
            hCt += chunkSize;
         }
         _draw();
      }
		
      /**
       * Actually does the drawing of the bitmap onto the canvas
       */
      private static function _draw():void {
         var len:int = pieces.length,
             w:int = image.width,
             h:int = image.height,
             xStart:int,
             yStart:int,
             xEnd:int,
             yEnd:int,
             rows:int = Math.ceil(h / chunkSize),
             cols:int = Math.ceil(w / chunkSize),
             row:int,
             col:int,
             pieceTime:Number = totalTime / len,
             time:Number;

         for(var i:int = 0; i < len; i++) {
            row = i % rows;
            col = int(i / cols);
				
            // get the end x/y position of the chunk based on the row/col
            xEnd = row * chunkSize;
            yEnd = col * chunkSize;
				
            // get the start position of the chunk based on the startDirection
            if(startDir == START_BOTTOM) {
               xStart = xEnd;
               yStart = rand(h - (h / 4), h + (h / 4));
            } else if(startDir == START_RANDOM) {
               xStart = rand((w / 2) - w, w + (w / 2));
               yStart = rand((h / 2) - h, h + (h / 2));
            }
				
            // add piece to draw layer
            canvas.addChild(pieces[i]);
				
            // set piece starting values
            pieces[i].x = xStart;
            pieces[i].y = yStart;
            pieces[i].scaleX = pieceStartScaleX;
            pieces[i].scaleY = pieceStartScaleY;
            pieces[i].rotation = pieceStartRotation;
            pieces[i].alpha = pieceStartAlpha;

            time = pieceTime * i;
            if(buildType == BUILD_BOTTOM_UP) {
               time = totalTime - time;
            } 
				
            Starling.juggler.tween(pieces[i], time, {
                  transition: pieceTransition,
                  x: xEnd,
                  y: yEnd,
                  scaleX: 1,
                  scaleY: 1,
                  rotation: 0,
                  alpha: 1, 
                  onComplete: onPieceTweenComplete
            });
         }
      }
		
      /**
       * Callback handler for when each individual piece completes its Tween
       */
      private static function onPieceTweenComplete():void {
         piecesComplete++;
         if(piecesComplete == pieces.length && callBack) {
            callBack()
         }
      }

      /**
       * Generates a random number between min and max
       * 
       * @param {Number} min The minimum value for the random number
       * @param {Number} min The maximum value for the random number
       */
      private static function rand(min:Number, max:Number):Number {
         return (Math.floor(Math.random() * (max - min + 1)) + min);
      }
   }
}
  • Line 48-57 - From the main Game.as class, we're setting a lot of values in the UI for how we want this tree to animate. In these lines we're setting our internal values from those user-set values (or using defaults if a user didn't selecting something, or cleared out a text input)
  • Line 48 - callback is the "onComplete" callback function from Game.as when we complete drawing the bitmap
  • Line 49 - buildType is how we're wanting to rebuild the image, from the top down, or from the bottom up
  • Line - pieceTransition is the standard Transistion type ("linear", "easeIn", "easeOut", etc) to use to animate the pieces
  • Line - totalTime is the total time the animation should take. The lower the number, the faster the pieces will animate in.
  • Line - startDir is which direction the pieces should animate in from
  • Line - chunkSize is the number of pixels we're batching together in a "chunk". If you had a 50x50 image and chunk was set to 10 (meaning 10px by 10px per "chunk") you'd have a total of 25 chunks that were going to be animated. The lower you set this, the more pieces will be animating, but the lower you set this, the more you're going to see performance issues. There's a fine line between a cool number of pieces, and poor performance
  • Line 54-57 - starting alpha, rotation, and scale for each piece
  • Line 70-83 - looping over the rows/columns and adding the new pieces to our array to draw
  • Line 105-148 - looping over the pieces to add them to the stage and animate
  • Line 106-107 - getting the proper row and column position of the piece
  • Line 138-147 - adding the piece to the Starling Juggler and tweening to the proper end position/scale/rotation/alpha

That's pretty much it! Make sure you check out the demo to see the finished product!

The tree pixel art image used comes from Mpaeva at Society6 and is used with permission by the artist. Feel free to check out his fine work there!

Categories:

0 Comments

Leave a Reply

Avatar placeholder