자, addChild 를 한답니다. Sprite 에 addChild 하는 것과 똑같지 않나요? stage도 똑같은 DisplayObjectContainer 라는 얘기입니다.
그러면 꼭 Document Class 만 addChild 될까요? 아니죠. 똑같이 stage.addChild(new Sprite) 가 됩니다.
그러면 as2는 어떻게 되느냐...
아시다시피 as2는 addChild 가 없습니다. attachMovie 나 createEmptyMovieClip 을 사용해서 MovieClip 을 생성해야 하는데 as2에는 stage 를 가르키는 포인터가 없네요...
as2의 Stage 는 해당 컨테이너를 가르키는게 아니고, 속성만을 사용할 수 있기 때문입니다.
그런데 이걸 일일히 Loader 객체를 생성해내고, 이벤트 처리를 하고, 또 그에 따라 메모리를 관리해야 하는 것은 너무나 귀찮고, 힘든 일입니다. 중복된 일이 생겨나면 함수나 클래스로 만들어 처리해야 하는 것이 우리의 해야할 일입니다.
일단 가칭으로 이 클래스의 이름을 ImageLoader 라고 해두겠습니다.
이 클래스에 구현되어야 할 기능을 적어볼까요.
1. 단일 객체를 사용해야 합니다.
2. 이미지 컨테이너가 필요합니다.
3. 이미지를 받아올 api가 필요합니다.
4. 이미지를 삭제할 api가 필요합니다.
1. 단일 객체 사용
게임 UI를 만들다보면 같은 이미지를 여러군데 써야할 일이 많이 생깁니다.
예를 들어 mmorpg라면 물약 이미지는 인벤토리에도 있고, 창고에도 있어야 하며, 상점에도 있어야 합니다. 아, 그리고 퀵슬롯에도 있어야 하는군요.
각각의 UI 마다 이미지를 따로 불러들인다면, 굉장한 낭비겠죠... 뭐, 메모리야 엔진을 잘만져서 하나로 통일해(?) 쓸 지도 모르겠지만 로드라는 그 행위도 cpu 와 메모리의 낭비입니다.
그럼 여기서 우린 한번만 불러서 BitmapData 만 참조해서 여러군데 쓸 방법을 찾아야 합니다.
자, 중복되면 안된다고 했으니 단일 이미지 로드 클래스부터 만들어야 하겠죠... Singleton 을 써서 만드시던지, 그냥 모든 api 를 static 으로 만드시던지 해서 만들면 될겁니다.
2. 이미지 컨테이너 생성
Loader 처럼 하나의 이미지를 불러서 처리하는 거라면 컨테이너가 아닌 그냥 content 참조만으로도 되지만 단일 클래스를 만들다 보니 해당 클래스에서 여러 이미지를 관리해야할 필요가 생겼습니다.
컨테이너라면 Array, Object, Vector.<Bitmap>, DisplayObjectContainer 등등이 있을겁니다. 컨테이너에 관한 글은 이글을 읽어보시기 바랍니다.
저는 Object 를 사용했습니다. 그 이유는 경로의 중복을 제거하기 위해서입니다.
예를 들어보겠습니다.
path = "a.png"
이런 경로가 있어서 이미지를 불러왔다고 치죠. 불러와서 가장 흔히 사용하는 배열에다가 담으면
Array[0] = img
가 되겠죠. 그런데 다음에 또 불러와야 한다면 어떻게 해야하죠? 또 불러와서 1번에 넣을건가요? 그러면 데이터가 중복된겁니다.
여기까지가 초급자 코스라면, 중급자 코스로 넘어가겠습니다.
Array[0] = {path:"a.png", img:img}
이렇게 저장했다고 치죠. 여기서는 두가지의 문제가 발생합니다.
이걸 가져다 쓰려면 배열을 루프로 돌려 검색해서 해당 원소 객체의 path 속성이 가 불러올 path 속성과 같은지를 판단하여 가져오고 같은 경로의 객체가 없다면 그때 불러오는 방식을 써야 합니다.
그 전에는 또 {} 와 같은 객체를 생성했네요?
런타임 상에서 다음과 같은 비용이 발생하겠죠.
1. 객체 생성 비용
2. 반복문 비용
3. 비교문 비용
데이터의 중복은 제거했지만 위와 같은 비용의 문제가 발생했습니다. 이제는 다음 상급자 코스로 넘어가겠습니다.
여기서 유니크한 것은 경로입니다. (BitmapData 도 유니크 하지만 유니크한 것인지를 판단하는데 비용이 많이 소모됩니다.)
1. Loader 는 Pooling 으로 재활용하세요
2. 클로저를 활용해서 멤버변수와 핸들러를 모두 없앨 수 있습니다
3. gc도 해야겠죠...
그 외에도 여러가지가 추가되어 있습니다.
이 포스팅이 제 코드를 베껴가라는 건 아니니까요... (하지만 이또한 구글 검색을 잘 해보면 어딘가 숨어있을지도...)
두번째 인자인 handler 를 보시면 BitmapData를 인자로 받는 함수를 받도록 해놨습니다.
우리가 이미지를 로드해서 쓸 때 그냥 쓸 수는 없잖아요. Bitmap 에 붙이기도 해야하고, 그 후에 크기도 늘려야 하죠. 그에 해당하는 함수를 받도록 해놨죠.
그래서 이미지가 이미 존재한다면 핸들러에 비트맵데이터를 인자로 보내 실행시키고, 없다면 로드를 해서 완료됐을 때, 해당 핸들러를 실행시킵니다.
4. 마무리
좀 덧붙이자면 완벽한 메모리 관리를 꿈꾸신다면 두군데를 더 신경 쓰셔야 합니다.
1. 사용 후에 BitmapData.dispose() 를 실행하여 메모리를 해지한다.
2. Loader.content 는(contentLoaderInfo.content 도) 여전히 해당 비트맵을 참조하고 있기에 gc 대상이 아니다.
3. BitmapData 만 활용하므로 불러온 Bitmap 은 더이상 이 클래스에서 쓸모가 없다. 풀링에 반환하여 재활용한다.
하지만 스케일폼에서는
BitmapData.dispose() 를 지원하지 않습니다. 그래서 가짜 이미지를 로드하는 방법도 있습니다. content 가 null 이 되게 만드는거죠.
(전 굳이 여기까지는 하지 않았습니다. 3번 정도만 사용하죠)
이번 포스팅은 아무래도 독자가 UI 개발자라는 전제하에 작성한 포스팅이 되었습니다.
코드가 좀 많고, 내용도 개발자만 알아들을 만한 내용으로 도배를 했네요...