Flash platform2012.01.25 16:20

 Image Sprite Sheet는 게임이나 웹서비스에서 이미지를 불러올때 여러개로 나눠져 있는 이미지를 불러 오는 것 보다 하나의 이미지를 불러오는게 비교적 좋은 로딩 시간 또는 로딩 되는 파일 크기를 줄일수 있기 때문에 사용하는 방법이다. 많은 서비스에서 이미 CSS Sprite Sheet 를 이용하여 사이트에 포함 되어 있는 이미지를 하나의 이미지를 통해 불러 와서 사용함으로 로딩 타임을 줄이는 이점을 취하고 있다.


  • 이미지를 온라인에서 불러올 경우 요청 횟수를 줄일수 있다.
  • 여러 이미지를 하나의 이미지 파일로 만들기 때문에 이미지 크기를 줄일수 있다. (클수도 있다)
  • 하나의 이미지로 해당 어플리케이션의 스킨을 지정 할 수 있다. (해당 이미지만 교체 하면 스킨이 바뀐다)




Flash/Flex(이하 Flash)로 구현된 어플리케이션(이하 앱)들은 대부분의 이미지가 컴파일 타임에 임베딩 되기 때문에 로딩 시간 단축에 대한 장점은 줄어들겠지만, 메모리 관리 측면에서 보면 상당히 이득을 준다. 이 문서에서는 Flash 앱 개발시에 Image Sprite Sheet 를 만들어서 사용하는 방법에 대해 다룬다.



Image Sprite Tool


합쳐진 이미지를 사용하기에 앞서 Actionscript로 사용하기 편하게 이미지를 합쳐주는 툴이 필요 하다. 물론 수작업으로 해도 되지만, 이미 좋은 툴들이 나와 있으니 아래 나열된 툴중 무료를 사용하도록 하겠다. 툴은 어떤 것을 사용해도 상관 없지만 툴에서 만들어주는 이미지 퀄러티와 설명 파일형식을 고려 해야 한다.



Sprite Sheet Packer


Sprite Sheet Packer 는 아래 그림 처럼 이미지를 불러와서 하나의 png/jpeg/bitmap 파일로 만들어 준다. 또한 만들어진 이미지에 대한 좌표값을 가지고 있는 txt/xml 파일도 만들어 준다. 최대 단점은 윈도우에서만 된다는거;;





위와 같이 이미지를 추가한 뒤에 Image File 이름 Map File (합쳐진 이미지의 위치값에 대한 파일) 이름을 설정해 준뒤에 Build Sprite Sheet 버튼을 누르면 아래와 같이 두개의 파일이 생성 된다.



sample.png




sample.xml


sample.xml 과 sample.png 파일이 생성 되었다.




사용 방법


Sprite Sheet Packer 를 통해 이미지를 만들었다면 다음과 같은 방법으로 이미지를 사용할 수 있다.  (다른 툴을 이용했다면 해당 툴이서 출력해 주는 xml 또는 text 파일 형식에 맞게 수정해서 사용할 수 있다)


아래 Client Code 에서는 위의 툴을 이용하여 생성한 sample.png와 sample.xml 을 임베딩하여 ImageSpriteSheet.as 클래스를 사용하여 popup_bg를 불러와서 화면에 표시 하고 있다.



client code


package {
     
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.utils.ByteArray;
     
    public class ImageSprite_sprite extends Sprite {
         
        [Embed (source="/assets/sample.png")]
        private var IMAGE_SHEET:Class;
        [Embed (source="/assets/sample.xml", mimeType="application/octet-stream")]
        private var DATA_SHEET:Class;      
         
        public function ImageSprite_sprite()
        {
            super();
             
            // support autoOrients
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
             
            init();
        }          
         
        private function init():void
        {
            // embed 한 xml과 png 파일을 로드 하여 파싱한다.
                        var xmlData:ByteArray = (new DATA_SHEET()) as ByteArray;
             
            var spriteSheet:ImageSpriteSheet;
            spriteSheet = new ImageSpriteSheet();
            spriteSheet.init(new IMAGE_SHEET(), XML(xmlData.readUTFBytes(xmlData.bytesAvailable)));
             
 
            // xml 에 정의 되어 있는 activity_indicator_160 을 불러와서 추가 한다.
            addChild(spriteSheet.getImage('popup_bg'));
             
        }
         
    }  
}




 ImageSpriteSheet Class


package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.utils.Dictionary;
     
     
    public class ImageSpriteSheet {
         
        private var _sheetImage:Bitmap;
        private var _sheetData:Dictionary;
         
        public function ImageSpriteSheet(){
        }          
         
        public function init(spriteSheet:Bitmap, sheetXML:XML):void {
             
            _sheetImage = spriteSheet;
            _sheetData = new Dictionary();
            parseSheetXML(sheetXML);           
        }
         
        private function parseSheetXML(data:XML):void {
             
            var i:int 0;
            var list:XMLList = data.Asset.Item;
             
            for(; i < list.length(); ++i) {
                 
                var key:String = list[i].Key;
                var value:Rectangle = parseImageBound(list[i].Value);
                _sheetData[key] = value;               
            }          
        }
         
        private function parseImageBound(data:String):Rectangle {
             
            var properties:Array =  data.split(" ");
            var bound:Rectangle;
             
            try {
             
                bound = new Rectangle(properties[0], properties[1], properties[2], properties[3]);
                 
            }catch(error:Error) {
                 
                throw new Error("Invalid data. please confirm data.");
            }
             
            return bound;
        }
         
        public function hasImage(name:String):Boolean {
             
            return (_sheetData[name])? true false;
        }
         
        public function getImage(name:String):Bitmap {
             
            if(hasImage(name)) {
                 
                var bound:Rectangle = _sheetData[name];
                var image:BitmapData = new BitmapData(bound.width, bound.height, true);
                image.copyPixels(_sheetImage.bitmapData, bound, new Point(00));
                 
                return new Bitmap(image);
            }
            else {
                 
                throw new