AS3로 SystemManager 의 Preloading을 흉내내기

* 이 글은 앞서 쓴 http://www.diebuster.com/?p=321 와 관련이 있으므로 미리 읽어보시면 도움이 됩니다.

Flex 프레임웍에서 SystemManager가 하는 일은 뭐냐? 라고 물어보면 한마디로 시스템 초기화라고 할 수 있습니다. 최초 이것저것 기본적인 실행 환경을 만들어 준 뒤, 진짜 주인공인 Application 객체를 호출하고 바톤을 넘기게 됩니다.

SystemManager의 초반 작업 중 중요한 부분은 preloading 입니다.

SystemManager가 Preloading을 구현하는 비밀

공식문서를 뒤져도 나오지 않는 메타데이터 태그 중 Main.as(제네레이트 된)의 소스를 보면 나오는 코드가 있습니다. 바로 아래 한 줄인데,

[Frame(factoryClass="SystemManager")]

이 [Frame] 메타데이터 태그에 대한 공식 문서는 없는 건지 못 찾은 건지 모르겠으나 여하간 mxmlc가 인식하는 건 분명합니다.

이 메타데이터 태그의 용도는 어플리케이션 클래스가 자동으로 stage의 자식이 되는 것을 막고 factoryClass를 stage의 0번째 자식으로 추가해주는 기능을 합니다.

따라서 이 팩토리 클래스를 무비클립으로 만들어 고전적인 if( framesLoaded === totalFrames ) 조건을 이용해 본체의 로딩 완료여부를 체크한 후 본체를 생성(팩토링)하여 사용하게 해주는 것입니다. 전체 swf를 하나의 무비클립으로 본다면 0번 프레임과 그 외로 나눠주는 기능인 셈입니다.

이러한 팩토리 클래스는 다음과 같은 흐름과 조건을 갖습니다.

  1. 무비클립을 상속받은 팩토리 클래스를 제작하고 이를 어플리케이션 클래스의 [Frame(factoryClass=””)] 항목에 지정해준다.
  2. 팩토리 클래스는 onEnterFrame 이벤트를 통해 framesLoaded === totalFrames 조건을 감시한다.
  3. 로딩되는 상태를 모니터링 하기 위해 loaderInfo.bytesLoaded 및 loaderInfo.bytesTotal 속성을 사용한다.
  4. 프레임로딩 완료 후 투영을 통해 Main클래스를 생성하여 addChild 한다.

이 과정은 단순해 보이지만 2가지 숨겨진 테크닉이 요구됩니다.

  1. Main클래스의 이름을 알아내야 한다. 만약 클래스 투영 시 main 이라고 기술한다면 반드시 어플리케이션 클래스의 이름은 main이 되어야 하는데, 보통 자유롭게 이름을 사용하므로 좋지 않다.
  2. 프리로딩 절차가 끝난 후 Main 클래스가 실행되는 시점에서 stage 상에 팩토리 클래스는 완전히 제거 되어 있어야 한다.

어플리케이션 클래스의 이름을 알아내기

어플리케이션 클래스의 이름을 알아내는 힌트는 loadInfo 객체에 있습니다. loadInfo는 현재 로딩된 swf 의 이름을 알아내는 loaderURL 이란 속성을 제공합니다. 개발자가 어플리케이션 클래스 이름과 swf의 이름을 다르게 가져가면 문제가 되지만 딱히 이것 외엔 방법이 없어 보이므로 loaderURL 기반으로 객체 이름을 투영해보겠습니다.

var main:Class;
main = getDefinitionByName(
	loaderInfo.loaderURL.substring(  //원래 문자열은 c:/…/main.swf 와 같은 완전한 swf의 경로
		loaderInfo.loaderURL.lastIndexOf('/') + 1, // 마지막 슬래시 부터
		loaderInfo.loaderURL.lastIndexOf('.')  //확장자의 점까지를 잘라낸다.
	) //이 문자열 작업의 결과로 main 을 얻게 된다.
) as Class; //main이름 기준으로 투영!

머 간단하죠 ^^; 이제 팩토리를 안전하게 제거하고 main만 스테이지에 남겨보겠습니다.

stage.addChild(new main);
stage.removeChildAt(0);

이로서 main입장에선 깨끗하게 자신만 stage에 들어간 상태로 시작할 수 있게 됩니다.

Factory Class 샘플

간단히 제작한 Fatory Class의 샘플을 하나 보겠습니다.

package{

	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;

	public class Cfactory extends MovieClip{

		public function Cfactory(){
			stop();
			addEventListener(Event.ADDED_TO_STAGE,ADDED_TO_STAGE);
		}

		private function ADDED_TO_STAGE( $e:Event ):void {
			removeEventListener(Event.ADDED_TO_STAGE,ADDED_TO_STAGE);
			addEventListener( Event.ENTER_FRAME, hnProgress);
		}

		private function hnProgress( $e:Event ):void {
			graphics.clear();
			if( framesLoaded === totalFrames ){ //로딩완료시
				removeEventListener(Event.ENTER_FRAME, hnProgress);
				nextFrame();
				hnLoaded();
			}else{ //로딩중 게이지 갱신
				graphics.lineStyle( 1, 0 );
				graphics.beginFill(0xffffff);
				graphics.drawRoundRect( stage.stageWidth-50, stage.stageHeight-5, 100, 10, 5 );
				graphics.endFill();
				graphics.beginFill(0x9a9a9a);
				graphics.lineStyle( 0, 0 );
				graphics.drawRoundRect( stage.stageWidth-49, stage.stageHeight-4, 98 * loaderInfo.bytesLoaded / loaderInfo.bytesTotal, 8, 5 );
				graphics.endFill();
			}
		}

		private function hnLoaded():void{
			var main:Class;
			main = getDefinitionByName(
				loaderInfo.loaderURL.substring(
					loaderInfo.loaderURL.lastIndexOf('/') + 1,
					loaderInfo.loaderURL.lastIndexOf('.')
				)
			) as Class;
			stage.addChild(new main);
			stage.removeChildAt(0);
		}
	}
}

이 코드는 제가 만들었으므로 자유롭게 사용하셔도 됩니다.

Main Class

실제 위의 팩토리 클래스를 사용하는 어플리케이션 클래스도 보겠습니다.

package {

	import flash.display.*;
	import flash.events.*;

	[Frame(factoryClass="Cfactory")]

	public class test extends Sprite {

		[Embed(source="menu1_0.jpg")]
		public var picture1:Class;
		[Embed(source="menu1_1.jpg")]
		public var picture2:Class;

		public function test() {
			addEventListener(Event.ADDED_TO_STAGE,ADDED_TO_STAGE);
		}
		private function ADDED_TO_STAGE( $e:Event ):void {
			removeEventListener(Event.ADDED_TO_STAGE,ADDED_TO_STAGE);
			trace(stage.numChildren); // 1을 반환
			trace(stage.getChildAt(0)===this); //true 반환
		}
	}
}

부록 * root란 무엇인가?

root는 경우에 따라 다르게 해석되기 때문에 난해합니다. root는 아래와 같은 경우로 나눠서 생각해볼 수 있습니다.

  1. Loader.root : 로더의 root는 언제나 loader.content.getChildAt(0) 와 같은 의미다.
  2. Bitmap.root : 비트맵의 root는 언제나 자기 자신이다. Bitmap.root == Bitmap
  3. Stage.root : 스테이지의 root도 언제나 자기 자신이다. Stage.root == Stage
  4. displayObject : DisplayObject의 root는 DisplayObject의 포함관계 트리 상의 최상위 객체를 나타낸다.
  5. 로드 된 SWF 소속의 displayObject : 해당 SWF내에서의 포함관계 트리 상 최상위 객체를 나타낸다.

간단히 정리하면 일반적으로 stage에 등록된 첫번째 displayObjectContainer가 바로 root입니다. 따라서 그 클래스가 그 시점에서 stage의 첫번째 자식인 경우는 root 자기 자신이므로 쓰나 안쓰나 똑같습니다.

위에 설명한 팩토리 클래스의 경우 최초 stage에 로딩되는 클래스 이므로 당연히 this.root 와 this는 같은 의미가 됩니다. 여러 예제 등에서 어플리케이션 클래스에서 root를 사용하는 경우가 있는데, 구지 사용할 필요는 없는 것이죠.



관련된 글:

  1. Flex에서 순수 AS3로 Preloader 구현하기
  2. PowerFl Compile Process Presentation
  3. particle system
  4. PowerFl Kickoff Presentation
  5. 시작점 클래스에 대해

11 Comments

  1. 지돌스타 says:

    정말 좋은 팁입니다.
    AS3 프로젝트를 가지고 장난칠때 잘 사용해서 사용자들에게 기다리는 일이 없도록 해야겠네요.

    그리고 Flash Builder에서 AS3 로 프로젝트를 만들때 위의 내용만 가지고 하면 “default css file not found” 이라는 경고가 뜨더라구요. 아무래도 이 과정이 Flex와 연결되는 부분으로 컴파일러가 처리하는 것 같은데 저 경고문구 없애려고 빈 null.css를 만들어 컴파일 옵션에 -defaults-css-url null.css 을 주었습니다.
    컴파일러 단계에서 처리하는 거라 어쩔 수 없다고 생각이 들긴 하지만 다른 방법이 없을까 궁금하기도 하네요.

    • admin says:

      아직 정확하게 확인을 하지는 않았는데 .actionscript 프로젝트 설정 파일에 조정 옵션이 있었던 듯 합니다. 확인해보고 다른 포스트로 올릴께요. 근데 위에 코멘트 하신걸로 벌써 블로깅을 하나 하셨군요 ^^

  2. ActionScript 3.0에서 Preloader 구현 및 Default css file not found 경고 메시지 없애기…

    Flex의 SystemManager는 Flex가 구동될 때 Application이 동작하기전 각종 설정을 하면서 사용자들에게 충분히 그 시간을 기다릴 수 있도록 UI적으로 Preloading 화면을 보여준다. 하지만 Flex가 아닌 ActionScript 3.0 프로젝트로 만들면 이런 UI를 보여주지 않는다. 하지만 못할 것도 없는 것이 Flex도 되는데 ActionScript라고 못할까? 사실 매우 쉬운 방법으로 이 기능을 추가할 수 있다. 다음…

  3. 동훈 says:

    오픈캐스트를 통해 처음 오게되었는데… 현실적으로 도움되는 좋은 글이 많네요~^^a 좀 어렵기도..
    그냥 보고가기 지송해서 글남겨요~~ 또 오겠습니다~ __)

  4. naiyumie says:

    좋은 클래스 감사합니다.
    유용하게 쓰겠습니다.
    항상수고하십니다.

    stage numChildren >> 2
    stage getChildAt >> false

    저는 이렇게 뜨는군요;;
    그래도 잘 작동하니’ㅅ’a;;; 문제 없다고 생각합니다;;;

  5. 원소랑 says:

    감사합니다. 정말 많은 도움이 되었습니다.
    제가 딱 찾던 내용을 정리 해주셨네요. ^^

  6. 라면스프 says:

    좋은 정보 감사합니다. 잘보고 갑니다. 자주 구경오겠습니다.^^

Leave a Reply