flash 개발환경이 AS3.0 대체되고 있는 상황에서 기존에 개발되어 있던 AS1.0 또는 AS2.0 으로 개발된 컨텐츠의 병행사용은 어느정도 같이 가져가여 할 부분이다.
여기서 약간 문제가 되는 부분은 AS3.0 으로 제작된 부분에서 AS1.0 또는 AS2.0 제작된 부분을 로드할 경우 발생하는 이슈이다.
AS3.0 부터는 AVM2 라고 하는 성능이 대폭개선된 가상머신을 도입했다. 이전의 가상머신은 AVM1 이라고 하여 AS1.0 또는 AS2.0 이 실행될 수 있는 환경을 말한다.
이런 기본적인 환경의 차이로 인해 기본적으로 AVM1 과 AVM2 무비사이간의 어떠한 커뮤니케이션이 가능하지 않다. 물론 LocalConnection 을 사용하면 가능하다.
단지, AVM2 에서 AVM1 을 로드하여 어떠한 cross-scripting 없이 사용한다면 로드한다는 것 자체에 별다는 이슈가 없어야 할텐데 실제적으로는 몇가지 문제가 발행한다.
만약에 AVM2 기반에서 AVM1 의 파일을 로드해서 사용했을 경우 일반적으로는 별 이상없이 작동한다. 하지만 AVM1 파일의 depth 가 커지면 커질수록 다시말해 AVM1 파일의 구조가 서로다른 로더에 의해 여러개의 구조로 이루어졌을경우 이상하게도 이 AVM1 파일들간의 인스턴스 구조가 변경되는 문제가 발생한다. 이는 여러 다른 플래시 개발자에게도 발생한 현상이다.
이러한 결과로 로드한 무비가 제대로 제어가 되지않거나 unload 가 되지 않는 현상이 발생한다.
아직까지 원인이 무엇인지는 찾지는 못했지만 약간의 팁으로 이런 위기상황를 극복할 수 있다.
첫째로, AVM1 컨텐츠들 간에서 다른 AVM1 컨텐츠를 로드해서 제어할때는 반드시 새로운 무비클립을 동적으로 생성하여 로드를 하고 언로드할 경우에는 remove 를 한다.
둘째로, 로드를 하는 AVM2 에서는 로더를 DisplayContainer 에 직접 붙이지 말고 로더의 컨텐츠(loader.content)를 사용하여 추가한다.
as3.0에서 여래개의 이미지파일을 외부에서 로드하려면 일반적으로 flash 에서 기본적으로 제공하는 Loader 클래스를 반복적으로 사용하여 처리를 해야하는데 . 이를 위해 다수의 파일을 순차적 또는 한꺼번에 로드할 수 있는 좀더 통합적인 리소스 관리가 가능한 시퀀스 로더를 제작해 보았다.
본 클래스는 ASAP를 사용하여 제작한 loader 클래스를 AS3.0 버전으로 마이그레이션(migration)한 것이다.
ASAP는 AS2.0 버전으로는 몇개 안되는 flash framework 중에 하나이다. 현재 전체적으로 AS3.0 버전으로 마이그레이션 중인 것 같다. 개인적인 생각으로는 코드자체가 방대하여 사용하기엔 좀 부담스럽지만 각각의 클래스와 클래스간의 연결고리, 이벤트처리와 같은 복합적인 내용들을 참고할 수 있어 유용한 부분이 있을 것이다.
(ASAP as3.0 버전 SVN http://asaplibrary.googlecode.com/svn/)
<sample code>
import com.dstrict.ub.utils.loader.*;
import com.dstrict.ub.events.*;
var loader:LoaderSequencer = new LoaderSequencer(1);
loader.addEventListener(LoaderEvent.START, onLoadStart);
loader.addEventListener(LoaderEvent.ALL_LOADED, onAllLoadFinished);
loader.addEventListener(LoaderEvent.COMPLETE, onLoadDone);
loader.addEventListener(LoaderEvent.PROGRESS, onLoadProgress);
loader.addEventListener(LoaderEvent.ERROR, onIOError);
//아래와 같이 load 메서드를 사용하여 다수의 파일을 로더에 등록할 수 있다.// loader.load(fileUrl,fileName)
loader.load("image1.jpg","1");
loader.load("image2.jpg","2");
loader.load("image3.jpg","3");
loader.load("image4.jpg","4");
functiononLoadStart(evt:LoaderEvent):void{//LoaderEvent 이벤트 오브젝트 속성인 fileName 으로 load 메서드의 fileName과 동일trace(evt.toString()+"-------->start : "+evt.fileName);
}function onLoadDone(evt:LoaderEvent):void{this["container"+evt.fileName].addChild(evt.loader.content);
}function onAllLoadFinished(evt:LoaderEvent):void{trace(evt.toString()+"-------->onAllLoadFinished");
}functiononLoadProgress(evt:LoaderEvent):void{trace(evt.toString()+"-------->progress : "+evt.bytesLoaded +" : "+evt.bytesTotal);
}function onIOError(evt:LoaderEvent):void{trace(evt.toString()+"-------->"+evt.message)}
flash 를 사용하면서 가장 많이 사용하는 부분은 아마도 트윈 부분일 것이다. 오브젝트의 속성값을 변경하면서 수많은 느낌의 모션을 만들수 있었다.
기존에 포함되어 있는 Tween 클래스는 많은 사용자들이 이런 다양한 트윈을 손쉽게 구현할 수 있게 해준다. 하지만, 생각보다 많은 기능들이 포함되어있어서 덩치도 클뿐더러 구현하는 코드 자체도 길어지게 되었다.
또한 AS3.0에서는 garbage collection 의 동작으로 인해 참조값이 없는 객체에 대해서는 collector 에 의해 메모리 수집의 대상이 되기 때문에 트윈클래스의 인스턴스를 지역변수로 참조하여 사용하게 되면 뜻하지 않게 트윈이 완료되기 이전에 객체가 사라지는 문제가 발생할 수도 있다..
따라서 이런 문제를 해결하기 위해 지역변수보다 좀더 높은 클래스 단계의 scope 의 참조를 사용하게 되는데 이럴경우 불필요하게 많은 참조 변수들을 클래스가 가지게되여 메모리를 소비하게 된다.
그리고 가장 빈번히 사용하게 되는 트윈의 경우 일일이 이벤트 핸들러 함수를 구현하기가 여간 번거로운게 아니다. 코드자체의 길이도 길어질 뿐만 아니라 가독성 측면에서도 떨어지는게 사실이다.
이런 문제를 조금이나마 해결하고자 이전 버전의 Tween 클래스를 기반으로 AS3.0 으로 컨버팅하였다.
기본적은 틀은 이전에 AS2.0 으로 구현한 로직과 큰 차이는 없다.
참조값을 유지하기 위해 전역속성으로 트윈을 적용하였고 객체를 생성하지 않아도 되기 때문에 한결 코드도 간결해졌다.
***** update(2007.10.30) ******
pause 와 resume 기능을 추가
원하는 오브젝트의 트윈에 적용가능함
pause(), pauseAll() 나 resume(), resumeAll() 를 사용하여 전체또는 부분적인 트윈을 제어할 수 있다.
<TweenEngine class>
package com.dstrict.ub.utils.transitions{importflash.display.DisplayObject;
importflash.events.*;
importflash.utils.Dictionary;
/**
DisplayObject tween class
트윈을 적용할 속성의 갯수에 관계없이 일괄적으로 적용가능
마지막 parameter 로 정해진 형식의 이벤트 오브젝트 적용시 이벤트 시작과 끝을 tracking 할 수 있음.
@example code
<code>
function onTweenStart(param):void{
trace("onTweenStart---->"+param);
}
function onTweenFinish(param):void{
trace("onTweenFinish---->"+param);
}
TweenEngine.start(circle,{x:400},Regular.easeOut,30,{onStart:onTweenStart,
onStartParams:["circle"],onFinish:onTweenFinish,onFinishParams:["circle"]});
</code>
*/publicclass TweenEngine {private static var _referContainer :Dictionary=newDictionary(true);
/**
@param targetObj : DisplayObject , tween 적용할 오브젝트
@param tweenProperty : Object , 속성오브젝트 ex. {x:100,y:100,alpha:1}
@param easing : Function , 이징함수
@param duration : int , 지속프레임
@param rest : Obejct , [optional] event object
ex. {onStart:onTweenStart,onStartParams:[],onFinish:onTweenFinish,onFinishParams:[]}
*/public static functionstart(targetObj:*,tweenProperty:Object,easing:Function,duration:int,...rest):void{vartime:int=1;
var beginning:Array=newArray();
varchange:Array=newArray();
var isCreated :Boolean=false;
var displayObj :DisplayObject;
TweenEngine.stop(targetObj);
if(!(targetObj isDisplayObject)){
displayObj = newShape();
isCreated=true;
}else{
displayObj = targetObj;
}for(var i:*in tweenProperty){
beginning.push(targetObj[i]);
change.push(tweenProperty[i]-targetObj[i]);
}if(rest.length&& rest[0].onStart){var eventStartObject:Object=newObject();
eventStartObject.onStart=rest[0].onStart;
eventStartObject.onStartParams=rest[0].onStartParams;
//onStart event
eventStartObject.onStart.apply(eventStartObject,eventStartObject.onStartParams);
}//Nested functionvar update:Function=function(){var objIdx:int=0;
for(var i:*in tweenProperty){
targetObj[i]=easing(time,beginning[objIdx],change[objIdx],duration);
objIdx++;
}time++;
if(time>duration){delete _referContainer[displayObj];
displayObj.removeEventListener(Event.ENTER_FRAME,update);
if(isCreated) displayObj=null;
if(rest.length&& rest[0].onFinish){var eventFinishObject:Object=newObject();
eventFinishObject.onFinish=rest[0].onFinish;
eventFinishObject.onFinishParams=rest[0].onFinishParams;
//onFinish event
eventFinishObject.onFinish.apply(eventFinishObject,eventFinishObject.onFinishParams);
}}}
_referContainer[displayObj] = update;
displayObj.addEventListener(Event.ENTER_FRAME,update);
}//Update 2007.10.30/**
* Pause a tweening for a given object.
*/public static functionpause(targetObj :*):void{if(targetObj.hasEventListener(Event.ENTER_FRAME)){
targetObj.removeEventListener(Event.ENTER_FRAME, _referContainer[targetObj]);
}}/**
* Pause all tweenings on the engine.
*/public static function pauseAll():void{for(var item:*in _referContainer){if(item.hasEventListener(Event.ENTER_FRAME)){
item.removeEventListener(Event.ENTER_FRAME, _referContainer[item]);
}}}/**
* Resume a tweening from a given object.
*/public static functionresume(targetObj :*):void{if(_referContainer[targetObj]!= null){
targetObj.addEventListener(Event.ENTER_FRAME, _referContainer[targetObj]);
}}/**
* Resume all tweenings on the engine.
*/public static function resumeAll():void{for(var item:*in _referContainer){if(_referContainer[item]!= null){
item.addEventListener(Event.ENTER_FRAME, _referContainer[item]);
}}}/**
* Stop a tweening for a given object
*/public static functionstop(targetObj :*):void{if(targetObj.hasEventListener(Event.ENTER_FRAME)){
targetObj.removeEventListener(Event.ENTER_FRAME, _referContainer[targetObj]);
delete _referContainer[targetObj];
}}/**
* Remove all tweenings from the engine.
*/public static functionstopAll():void{for(var item:*in _referContainer){if(item.hasEventListener(Event.ENTER_FRAME)){
item.removeEventListener(Event.ENTER_FRAME, _referContainer[item]);
delete _referContainer[item];
}}}}}import com.dstrict.ub.utils.transitions.*;
import fl.motion.easing.*;
TweenEngine.start(circle,{x:400,y:300},Cubic.easeInOut,20,{onStart:onTweenStart,onStartParams:["circle start"],
onFinish:onTweenFinish,onFinishParams:["circle finish"]});
//----> 마지막 오브젝트 값은 이벤트 핸들러를 참조하는 값으로 옵션사항이다.
단, 사용시 이벤트 오브젝에서 함수이름을 onStart , onFinish 로
이벤트 파라미터를 onStartParams,onFinishParams 로 키값을 사용해야만 한다.
function onTweenStart(param):void{trace("onTweenStart---->"+param);
}function onTweenFinish(param):void{trace("onTweenFinish---->"+param);
}//stop tweenstage.addEventListener(MouseEvent.CLICK,onStop);
function onStop(evt:MouseEvent){
TweenEngine.stop(circle);
}
AS3.0 에서 사용되는 stage 와 root 의 개념은 AS2.0 버전과는 차이가 있다.
우선 stage 를 살펴보면 이전 버전에서는 stage 오브젝트는 static class 로서 주로 movie size 나 onResize 이벤트를 처리하는데 사용하였지만 새롭게 바뀐 AS3.0 에서는 플래시 무비에 존재하는 모든 displayObject 를 담을수 있는 최상위 컨테이너 개념이 첨가되었다.
이는 어떻게 생각하면 기존의 _root 와 같이 생각할 수도 있다. 하지만 바뀐 모델에 있어서 stage 속성은 인스턴스 속성이다. 다시말해, 전역적으로 접근할 수 없고 반드시 displayObject 의 인스턴스 속성으로서만 참조가 가능하다는 이야기이다. 또한 DisplayObject 가 스크린상의 timeline 이나 display list 에 등록하지 않는다면 null 값을 가지게 된다. (Document class 와 screen 상에 보여지는 오브젝트는 제외)
이런 점이 stage 속성을 사용하는데 있어 상당히 제약사항으로 작용하게 된다. 만약 DisplayObject 가 아닌 다른 Object 를 사용할때 stage 속성은 정의된 속성이 아니므로 constructor 에 stage 속성값을 parameter 로 넘겨주는 방식으로 사용할 수 밖에 없다.
root 는 상황에 따라 다를 수 있지만 일반적으로 현재의 flash movie 에서 main timeline 을 참조할 수 있는 DisplayObject 속성이다. 다시말해 상대적인 최상위 DisplayObject 를 참조한다.
이전의 _root 속성은 전역속성으로서 가장 최상의 컨테이너를 참조하였기 때문에 로더를 통해 불러왔을경우 그 갯수에 따라 _root 의 참조값이 변하였다. 하지만 root 속성은 현재의 flash movie 를 기준으로 참조값을 얻어오기 때문에 각 swf 에 해당하는 main timeline 의 참조값이다. 즉, 이전버전에서 _lockroot 를 적용한 것 같이 root 속성을 상대적으로 참조할 수 있다. 모든 displayObject 가 각각 timeline 의 참조값을 가질 수 있다는 의미이다.
하지만 root 도 stage 속성과 마찬가지로 인스턴스 속성이다. 따라서 stage 의 제한사항을 그대로 가지고 있다.
따라서 이런 문제점들을 해결하기 위해서는 위와같이 각각의 참조값을 parameter 로 넘겨주는 방법과 초기화 함수를 실행하여 addChild 이후에 참조할 수 있도록 처리해야한다.
또다른 방법으로는 Document class 를 활용하여 전역속성으로 만드는 것이다.
Document class 에서는 Stage 에 자동으로 추가되는 main timeline 으로 인해 생성되자마자 stage 속성과 root 속성을 참조할 수 있다.
위와 같이 document class 에서 MainStage class 을 상속받아 사용하면 어느 오브젝트에서라도 전역적으로 stage를 참조 할 수 있다.
하지만 이는 전역속성으로서 의미가 있는 stage 에만 유용할 뿐 각기 다른 root 속성을 참조하기에는 문제가 있다.
root 속성 참조 대한 문제는 ADD_TO_STAGE 이벤트를 이용하여 오브젝트마다 일일이 체크하는 방법이나 아니면 초기화 함수를 실행하여 처리하는 방법밖에 없는것 같다.(어떤 다른 방법이 있을까?????)
timeline 으로 부터 종속되지 않도록 stage 속성과 root 속성을 추가한것은 참 좋은 방법인것 같다. 하지만 그로 인해 개발자들이 일일이 신경쓰지 않으면 안되는 것들이 정말 많이 생긴것 같다.
timeline 과 class……….플래시의 특성상 종속적일 수 밖에 없는데……
어떤 것이 더 나은 방법일까?….의구심이 든다.
가변적인 메소드의 parameter 갯수를 이용하려면 AS2.0 버전까지는 arguments 오브젝트를 사용하여 임의의 parameter 에 접근할 수 있었다. 하지만 AS3.0 에서는 컴파일러의 좀더 철저한 데이타 타입의 체크와 parameter 관리에 의해 기존의 방법을 쓸 수가 없게 되었다.
<AS2.0>
function parameterTest():Void{
trace(arguments.length) //parameter 갯수를 알수 있음
}
parameterTest(1,2,3);
result–> 3
<AS3.0> function parameterTest():void{
trace(arguments.length)
}
parameterTest(1,2,3);
result–> compiler error : Incorrect number of arguments.
AS3.0 에서는 메소드 정의시 parameter 갯수와 호출시 parameter 갯수가 일치해야만 한다.
그렇지 않으면 위와 같이 arguments 갯수에 오류가 있다고 에러를 발생시키게 된다.
이를 위해 …(rest) paremeter 구문을 새롭게 제공하였는데 사용법은 단순히 … 뒤에 사용자가 원하는 parameter array 이름을 써주면 된다.
function parameterTest(…param):void{
trace(param.length) // param 은 배열 타입이다.
}
다른 parameter 와 같이 사용하려면
function parameterTest(str1:String,str2:String,…param):void{
//…..statements
}
마지막에 …(rest) parameter 를 써주면 된다.
주의할 점은 위 방법을 사용하면서 arguments 오브젝트를 동시에 사용할 수 없다는 점이다.
Recent Comments