Friday, April 19, 2013

Easiest way to make animation from png spritesheet

In this tutorial I will show you how I made an aimation using just 8 png images from a sprite sheet. I suppose this is the most easiest and straigt forward way to do it. There are tools like TexturePacker but I will skip it in this tutorial and concentrate only on the mechanics how I did this animation.

I assume that you have installed FlashDevelop and have created blank AS3 project named SpriteAnimation in a folder with the same name. The project has white background, 30 fps and it is 200x200 size.

I will use this sprite sheet that I found on internet for free to be exact on this site as3gamegears



So this is a field scarecrow that has a sprite sheet of 8 postions. I used Gimp to divide this 8 postions into 8 png images which are 64x64 pixels. The mechanism to create animation from this 8 images is simple: take all images, put them in a AS3 Sprite and each frame change visibility of each image. So we go first image is visible all other are invisible, then first image is invisible second is visible, all other are invisible. Than third image is visible all other are invisible etc...
When we show the 8-th image we switch back to the first and repeat the same process again.


I created a folder in my FlashDevelop project called assets where I put all eight images.
I also created separate class named Scarecrow that will hold all images inside and keep track which image should be shown next.

So here is the code of Scarecrow.as

package 
{
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.events.Event;
    /**
     * ...
     * @author SpinnerBox
     */
    public class Scarecrow extends Sprite
    {
        [Embed(source = "/../assets/pic1.png", mimeType="image/png")]
        private var Pic1:Class;
        private var pic1:DisplayObject = new Pic1();
       
        [Embed(source = "/../assets/pic2.png", mimeType="image/png")]
        private var Pic2:Class;
        private var pic2:DisplayObject = new Pic2();
       
        [Embed(source = "/../assets/pic3.png", mimeType="image/png")]
        private var Pic3:Class;
        private var pic3:DisplayObject = new Pic3();
       
        [Embed(source = "/../assets/pic4.png", mimeType="image/png")]
        private var Pic4:Class;
        private var pic4:DisplayObject = new Pic4();
       
        [Embed(source = "/../assets/pic5.png", mimeType="image/png")]
        private var Pic5:Class;
        private var pic5:DisplayObject = new Pic5();
       
        [Embed(source = "/../assets/pic6.png", mimeType="image/png")]
        private var Pic6:Class;
        private var pic6:DisplayObject = new Pic6();
       
        [Embed(source = "/../assets/pic7.png", mimeType="image/png")]
        private var Pic7:Class;
        private var pic7:DisplayObject = new Pic7();
       
        [Embed(source = "/../assets/pic8.png", mimeType="image/png")]
        private var Pic8:Class;
        private var pic8:DisplayObject = new Pic8();
       
        private var picsObject:Object;
        private var currentPic:uint;
        private var animCounter:uint;
        private var nextPicTime:uint;
       
        public function Scarecrow()
        {
            picsObject = new Object();
            currentPic = 1;
            nextPicTime = 3;
            animCounter = 0;
           
            addChild(pic1);
            picsObject["1"] = pic1;
           
            pic2.visible = false;
            addChild(pic2);
            picsObject["2"] = pic2;
           
            pic3.visible = false;
            addChild(pic3);
            picsObject["3"] = pic3;
           
            pic4.visible = false;
            addChild(pic4);
            picsObject["4"] = pic4;
           
            pic5.visible = false;
            addChild(pic5);
            picsObject["5"] = pic5;
           
            pic6.visible = false;
            addChild(pic6);
            picsObject["6"] = pic6;
           
            pic7.visible = false;
            addChild(pic7);
            picsObject["7"] = pic7;
           
            pic8.visible = false;
            addChild(pic8);
            picsObject["8"] = pic8;
           
            addEventListener(Event.ENTER_FRAME, playAnimation);
           
        }
       
        public function setPicVisible(picNum:uint):void
        {
            for (var i:int = 1; i <= 8; i += 1)
            {
                var strPicNum:String = String(i);
                if (i == picNum)
                {
                    picsObject[strPicNum].visible = true;
                }
                else
                {
                    picsObject[strPicNum].visible = false;
                }
            }
        }
       
        public function playAnimation(e:Event = null):void
        {
            if (animCounter >= nextPicTime)
            {
                setPicVisible(currentPic);
                if (currentPic == 8)
                {
                    currentPic = 1;
                }
                else
                {
                    currentPic += 1;
                }
                animCounter = 0;
            }
            else
            {
                animCounter += 1;
            }
        }
    }

}

A little on the Scarecrow.as class:

- I created object named picsObject that will hold all image objects and therefore all image will be accessible by just typing picsObject[String(picNum)]. First I set all 7 images to be invisible but just the first is visible. 

 - Then I create a function called setPicVisible(picNum) that will show only the image that corresponds to the picNum parameter. 

- Next is the playAnimation(e:Event = null)  that works on ENTER_FRAME event and will play the animation on each frame. One thing to note that the frame rate is 30 fps which will be too much for our animation so I switch image on each third frame instead of each next frame. I do that by using this simple if (animCounter >= nextPicTime) / else  statement. 

My Main.as class looks like this

package
{
    import flash.display.Sprite;
    import flash.events.Event;
   
    /**
     * ...
     * @author SpinnerBox
     */
    public class Main extends Sprite
    {
        private var scarecrow:Scarecrow;
       
        public function Main():void
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
       
        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point

            scarecrow = new Scarecrow();
            scarecrow.x = 50;
            scarecrow.y = 50;
            addChild(scarecrow);
        }
       
    }
   
}


I just created new Scarecrow object and added it on the stage.

Well here is the result: