FLASH - swapDepths(심도)에 대해서 알아보자
01 14, 2007 02:08
1. 샘플무비 미리 말씀드리지만, 이 무비는 강의를 위해 급조된 것이고, 완벽한 디버깅을 거치지 않았기 때문에 스크립트를 100% 신뢰할 수 없습니다. 공부하시다가 버그를 발견하시거나 예기치 않은 동작이 일어난다면 어딘가 이상한 거죠.. 제게 메일로 알려주시면 고맙겠습니다. 이번에 공부할 것은 swapDepths입니다. flash4에서 duplicateMovie를 사용한 약간의 트릭으로 구현을 하곤 했었는데, 플래쉬5에서는 무비클립 오브젝트의 method로 스크립트 한줄이면 간단히 해결할 수 있게 되었습니다. 우선, depth 라는 말에 대해 생각해 봐야 할 것 같습니다. layer와 depth 그리고, nested movie(무비클립안의 무비클립) [영어로 표기하는 게 구태여 우리말로 번역해서 하는 것보다 의미전달이 명확할 경우가 있습니다. 특히, 외산 프로그램의 경우, 혼동을 피하기 위해 원래 프로그램상에 정의된 용어를 사용하는 게 이해하기 쉬운 경우가 많죠.. 영어많이 쓴다고 미워하지 마세요..] depth를 심도라고 우리말도 하기도 하지만, swapDepth란 명령어가 존재하니까..depth로 밀고 나갑니다. 애니메이션 작업을 하면, layer라는 게 아주 중요한 요소중의 하나죠. 플래쉬에서의 layer역시 같은 개념에서 stage에 놓여지는 심볼의 높이값에 해당하죠. 여기서 dhtml에 익숙하신 분은 z-depth라는 것을 상기하실 지도 모르죠. z-depth는 화면의 수평면을 수직으로 하는 축을 z축이라고 하고, z좌표축에 대화 좌표상의 높낮이 즉, 실제 우리눈에 보이는 층이죠. 뒤에 있는 것이 앞에 있는 물체에 의해 가려져 안보이는 경우 앞에 있는 물체의 z-depth가 뒤에 있는 z-depth보다 높다고 말하죠. flash에서의 depth도 거의 유사한 개념인데, 요개 layer랑 헷갈리죠... 왜냐하면, layer역시 앞뒤 개념이 있고, 한 layer에 상에도 각 심볼들이 순서가 존재하잖아요. 도대체 플래쉬에서의 depth는 무엇이란 말이고, swapDepths는 layer랑은 전혀 상관 없는 건가요? 그리고, 여기에 _level은 또 무엇인가...? 괜히 복잡해지죠..사실 개념적으로는 복잡합니다. 실제 사용하는 방법이 오히려 간단하죠. _level0,_level1,_level2 등은 swf 과 관련된 개념입니다. 서로 다른 무비들을 만들어서 이 무비에서 저 무비를 보여주고 싶은 때 사용하죠. 가장 관련된 action이 뭐죠? loadMovieNum 이죠... 가장 기본적인 무비는 _level0 죠, 만약, 어떤 다른 무비를 _level0로 로드하면, 현재 무비를 대체합니다. layer는 플래쉬 무비를 제작할 때 사용하는 개념이죠, 실제로 무비실행중에 스크립트로 제어가 되지는 않습니다. 작업의 편의상 layer라는 것이 있지만, action script의 관점에서는 depth로만 생각하시는 게 편리할 거예요. 한레이어 상에 여러개의 mc들이 존재할 수 있죠, depth는 사실 상대적인 겁니다. 절대 값이 있다기 보다는 어느게 어느것보다 위에 있다 아래에 있다가 중요하죠. 그리고, 이 depth의 비교는 인스탄스들이 모두 같은 _parent를 가질 때 비교의 의미가 있습니다. 만약, a 라는 무비에 b와 c가 들어 있다면, b,c사이의 depth비교는 의미가 있지만, a와 b를 비교하는 것은 무의미,불가능하지요. 그럼, duplcateMovie나 attachMovie로 만들어진 무비는 depth를 인스탄스 생성시 결정해 주는 것은 알고 있는데, 플래쉬 작업을 위해 라이브러리에서 drag and drop 으로 timeline에 오려놓은 인스탄스들의 depth는 어떻게 처리되나요? 이것들 역시 depth값을 갖고 있습니다. 하지만, 플래쉬플레이어내부적으로 어떤 변수를 사용해서 어떤 형식으로 처리하는 지는 모르겠어요. 하지만, swapDepths를 이용할 때, duplicate에 의해 depth값이 정해져 있는 instance와 기존의 timeline상에 놓여져 있는 인스탄스의 depth가 교환(swap)되거든요. 즉, 한 _parent mc내에 놓져진 모든 인스탄스들은 상대적 depth가 부여되어 있다는 거죠. 무슨 말인지..헷갈린다구요... 사실, 이 부분들은 저 나름대로 개념정리를 할려고 적고 있는 거예요..명확히 이해가 안되시면, 나중에 swapDepth를 많이 이용해야 하는 플래쉬 무비를 만든다거나 하실 때 다시 한번 읽어보세요.. 제가 나름대로 어떤 노력을 하고 있는지 이해하실 수 있을 거예요.. 이번 강의는 swapDepth뿐만 아니라, prototype 정의하는 방법, getBounds method 그리고, array 조작 방법등을 중심으로 공부하거든요. 샘플 무비도 좀 의도적으로 그런 부분을 사용했어요. 실제로, 목적하는 효과를 내기 위해 꼭 그렇게 할 필요가 없는데,,라는 부분이 있지만, 공부를 위해서 스크립트가 그렇게 된 거예요... 우선, array 조작방법부터 다루도록 하겠습니다. 2. 배열 샘플무비는 윈도우를 무한정 추가할 수 있도록 되어 있습니다. 상단 오른쪽 끝의 x 표로 되어 있는 버튼을 누르시면, removeMovieClip에 의해 윈도우가 없어집니다. 중간의 윈도우가 클릭되면, 맨위로 올라오도록 되어있습니다. 이 때, click된 mc의 depth가 가장 높은 값으로 치환되었기 때문이죠. 어쨋든, 이렇게 무비 실행중에 변수들의 리스트가 있고, 이 리스트에 요소가 추가되거나 또는 삭제될 경우, 가장 먼저 떠오르는게 배열(array)입니다. 손쉽게 추가, 삭제가 가능하니까요. array object의 method들은 다른 객체에 비해 상대적으로 많습니다. 그것은 이 array라는 게 프로그래밍언어에 있어서 상당히 중요한 역할을 수행하고 그에 따라 여러가지 기능들이 많기 때문입니다. 여기 swapDepth와 관련해서 다룰 method는 pop 과 push 그리고, splice입니다. 요것들만 잘 이해하면, 나머지도 같은 원리로 이해하시면 어렵지 않을 겁니다. 우선, 새로운 윈도우를 만들어 놓고, 이 새로운 윈도우를 배열에 추가한다고 합시다. 우선 배열을 새로 선언하는 방법부터 살펴봅시다. flash dictionary를 보면 array를 선언하는 방법은 다음과 같이 3가지가 있습니다. new Array(); new Array(length); new Array(element0, element1, element2,...elementN); 다음은 [] array acess operator에 따른 array를 선언하는 방법입니다. myArray =[]; myArray = ["red", "orange", "yellow", "green", "blue", "purple"] //아무요소도 없는 배열선언하기 arr = new Array()라고 하면 배열에 들어가 있는 요소의 숫자(이하 length)가 0 , 즉 전혀 초기화가 되어있지 않은 배열객체의 인스탄스를 하나 생성하게 됩니다. arr = [] 에서 []는 new Array()와 똑같은 의미를 갖습니다. //배열의 길이를 초기화하면서 배열선언하기 arr = new Array(5)라고 하면, 배열의 길이가 5인 arr를 생성합니다. 확인하시는 방법은 trace(arr.length)를 이용하시면 됩니다. //배열의 요소들 값을 대입하면서 배열선언하기 arr = new Array("a","b","c"); arr = ["a","b","c"] 플래쉬에서는 2차원 이상의 배열을 지원하지는 않지만, 자바스크립트와 마찬가지로 배열객체를 이용해 2차원 배열처럼 사용할 수 있는 방법이 있습니다. arr = []; arr[0][];를 선언하면 arr[0]가 배열을 요소로 갖게 됩니다. 실제로는 2차원 배열이 아니지만, 이렇게 선언을 하고 나면, 2차원배열처럼 다루어도 무방합니다. 실제로 플래쉬에서 무비제작을 하면서 이차원 배열이상을 만들일도 없고, 코드의 효율을 따져볼때 그 이상은 무의미한 것 같습니다. 자 이렇게 선언된 배열들은 실제로 우리가 다룰려는 swapDepth의 예제처럼 동적으로 크기나 요소가 변하기 마련입니다. 새로운 요소를 추가하는 방법은 arr[4] = "b" 이렇게 요소의 위치(번호)에 값을 직접 대입하도록 하는 방법이 있습니다. 가장 확실한 방법이겠죠... push는 배열의 맨끝에 요소를 추가하는 것이고 unshift는 맨앞에 새로운 요소를 추가합니다. arr.push("e");라고 하면, 배열 arr의 맨 마지막 요소의 값은 "e"가 될 것입니다. 배열의 요소를 삭제하는 방법은 중간의 임의의 값을 없애는 splice 맨앞의 값을 없애는 shift 맨 뒤의 값을 없애는 pop이 있습니다. 우리가 swapDepths해야 할 일은 배열의 중간에 있는 값을 빼내서 맨뒤로 보내는 것입니다. 이것을 한번에 해주는 method는 없습니다. 위에서 열거한 배열의method를 이용해 배열중간의 요소를 맨뒤로 보내는 스크립트를 구성해 봅시다. 배열의 n번째 요소를 맨뒤로 보내고자 한다면, n번째 요소를 추출해서 임시 변수에 저장을 하고, 배열에서 n 번째 요소를 삭제합니다. 그 다음에 배열의 맨끝에 임시 변수에 저장된 값을 추가합니다. 그렇다면, var temp = arr[n-1]; 이 되겠죠. 배열의 첫번째 요소는 arr[0]으로 됩니다. 0부터 시작한다는 것을 명심하세요. 임시변수 temp에 arr배열의 n번째 값이 저장되었습니다. 이제, splice를 사용해서 n번째 요소를 배열로 부터 제거해봅시다. splice는 배열의 중간에 한개이상의 요소를 추가하거나 삭제하는 데 사용합니다. arr.splice(n-1,1) 이라고 하면 n번째 요소부터 시작해서 1개를 제거하라가 됩니다. 이제 push를 이용해 temp에 저장된 값을 array의 맨끝에 추가하면 되겠죠.. arr.push(temp); 자, 배열은 이정도로 하죠. 가능하면 배열에 대해서는 보다 깊이 다루어야 하겠지만, 중요한 것은 method들이 아니라 배열의 기본 개념입니다. 참, 배열의 요소들은 숫자, 문자 들 뿐만 아니라, 무비클립에 대한 참조가 될 수도 있습니다. 예제를 보시면 배열의 요소가 바로 각각의 윈도우의 인스탄스로 선언되어 있습니다. 3. prototype과 bitwise 그 다음에 공부해야 할 것은 prototype이란 녀석입니다. 이것은 건드리기 시작하면 한정없이 커질 수도 있는 무서운 놈이지만, 한번 다루어야 겠다고 생각해서 샘플에서 의도적으로 다루었습니다. prototype뿐만 아니라 _proto_ , contructor등이 이와 관련된 객체지향적 관점의 용어들입니다. 엄밀하게 객체지향적인 게 아니라 prototype지향적이라고 해야겠지만요. 객체지향언어들이 각광을 받는 것은 코드의 재활용성이 가장 두드러집니다. 객체를 선언할 수 있는 것은 객체에 prototype이 존재하기 때문이라고 생각하시면 됩니다. 어쨋든, 이번은 이 prototype에 대한 맛만 보기로 하죠. swapDepth가 주인공인데, prototype이란 벌집을 건드리면, 주객이 전도하거든요. flash의 object에 사용되어지는 내장함수 혹은 method들은 플래쉬에 의해서 제공되는 것 말고, 직접 사용자가 선언을 해서 추가를 할 수 있습니다. 추가하는 방법의 문법은 형식적으로 2가지가 있습니다. 첫번째: function 추가함수(){ 어쩌구저꺼구; } array.prototype.추가함수 = 추가함수; 두번째: array.prototype.추가함수 = function(){ 어쩌구저쩌구; } 이렇게 array객체의 prototype에 새로운 함수를 추가하면 우리는 이함수를 array객체에 push나 pop처럼 직접 적용할 수 있습니다. 이렇게 사용자 정의 함수를 객체들의 prototype에 추가하게 되면 new를 이용해 생성된 새로운 인스탄스들에 모두 함수를 적용할 수 있습니다. 일반 함수의 정의와 다른 점은 일반 함수의 경우는 함수를 사용할 때, 함수가 선언된 정확한 경로를 통해 호출하여야 합니다. 반면, 객체를 통해 선언하게 되면, 각 객체의 인스탄스의 _proto_ 라는 속성에 의해 객체의 prototype을 통해 선언된 모든 함수들이 계승됩니다. 그렇다면, 플래쉬의 내장객체들말고 새로운 객체를 정의하는 방법도 있겠죠. 다음은 flash reference에 있는 예제입니다. 아래는 Biker라는 새로운 객체를 정의하고 있습니다. Biker가 객체를 정의하는 constructor에 해당합니다. function Biker(t, d) { this.time = t; //this는 객체의 인스탄스 자신을 지칭합니다. this.distance = d; } //아래의 Speed라는 함수는 prototype에 rate란 함수로 추가되었습니다. function Speed() { return this.time / this.distance; } Biker.prototype.rate = Speed; //위에서 정의한 새로운 객체는 역시 new라는 키워드에 의해 인스탄스를 만들 수 있습니다. 위의 constructor정의부분을 보시면 2개의 인자를 취합니다. 인스탄스를 생성할 때 역시 2개의 인자를 통해 생성을 해야겠죠. emma = new Biker(30, 5); hamish = new Biker(40, 5); 이런 객체 지향적 스크립트를 적극적으로 이용하면 게임이나 복잡한 동작의 무비클립을 만드실 때 코드를 간결하게 만들 수 있습니다. 실제로 플래쉬에서 이런 객체지향적 프로그래밍을 하는 것은 기존의 내장 객체들의 함수를 추가하거나 연장하는 경우입니다. 이번 샘플처럼요.. 이 부분은 실제 샘플의 코드를 설명드릴 때 다시한 번 설명드리겠습니다. /////////////////////////////////////////////////////////////////////////////////////////// prototype말고, 또 공부하셔야 할 부분은 16진법이란 것과 << >>등의 bitwise연산자 들입니다. 컴퓨터가 0,1로 구성된 기호체계를 바탕으로 모든 것이 처리된다는 것은 아시죠.. 컴퓨터는 실제로 우리가 사용하는 10진법을 2진코드로 바꾸어서 연산처리를 한 다음 다시 우리가 이해할 수 있는 10진법으로 고쳐서 보여줍니다. 우리가 칼라표기법의 하나인 rgb는 많이 익숙하시겠지만, ffddee 등으로 표기하는데 이것은 r값에 해당하는 ff, g값에 해당하는 dd, b값에 해당하는 ee의 세가지 16진법 숫자가 결합되어 있는 것입니다. 16진법은 0부터, 9, a,b,c,d,e의 16개의 숫자와 문자조합으로 표기가 됩니다. 그러니까..a는 10에 해당하는 숫자죠.. ff 를 10번숫자로 고친다면 f * 16 + f*1 가 됩니다. f는 15에 해당하겠죠..계산을 해보면 255가 됩니다. 실제로 r이 가지는 값의 범위는 16진법의 2자리수이니까..15*16 + 15 = 255 즉, 0부터 255가 됩니다. 많이 익숙하시다고요..예..rgb색상코드의 범위죠. 나름대로 ffeeff등의 표기에 익숙하긴하지만, 이런 표기법을 이용한 연산은 익숙하지 않죠.. 16진법의 표기는 컴퓨터내의 메모리연산방식과 상당히 밀접한 관계가 있기 때문에 계산 방식도 bitwise로 움직여집니다. << 는 왼쪽으로 몇자리, >>는 오른쪽으로 몇자리, |는 또는 에 해당하는 bitwise연산자입니다. x = 1 << 10 라면 현재 x값의 자리수를 10만큼 왼쪽으로 이동시켜라. 입니다. 그래서 x = 1000000000이 됩니다.(이것은 2진법상의 표기입니다.) x값은 1024가 됩니다. 경우에 따라서는 이런 bit연산자로 처리하는 게 속도향상에 도움이 많이 됩니다. 스크립트도 깔끔해지구요.. 이밖에 bitwise 연산자는 | & ^ <<< >>> 등이 있습니다. 이 bitwise 연산자들은 웬만한 프로그래밍 교본에 대부분 수록되어 있습니다. 플래쉬의 bit연산자들이 이해가 안가시면 서점에 가셔서 C 프로그래밍 기초등의 책을 한번 들여다 보세요. 아래는 dictionary에서 발췌한 | (bitwise or 연산자)에 대한 설명입니다. | 는 좌우의 표현식을 32-bit unsigned integers로 변환하고 각 자리수를 비교해서 하나라도 1이있우면 1의 값을 반환한다.// 15 = 1111 (2진법) x = 15;// 9 = 1001 (2진법) y = 9;// x | y z = x | y; z = 15 그래서 15 | 9 는 15가 된다. (1111 | 0011 = 1111) 보충설명 끝자리는 1과 1이죠 그래서 -> 1 맨첫자리는 0과 1이죠 1이 하나라도 1이 있으면 1; 만약 0과 0이 면 1이 하나도 없으니 0; /////////////////////////////////////////////////////////////////////////////////////////////// 우선 몇가지를 개략적으로 훑어보았는데, 시간내셔서 좀 더 확실하게 이해해 두실 필요가 있는 부분들입니다. 하지만, prototype이나 << >> 같은 녀석들은 몰라도 플래쉬 무비를 실제로 제작하는 데는 아무런 불편이 없습니다. 좀 더 고급스러운 프로그래밍을 할 수 있지만, 반드시 그렇게만 해야하는 경우가 있는 것은 아니거든요.. 이 부분도 가능하면, 직접 코드를 설명할 때 비교를 해보도록 하겠습니다. 4. swapDepths 스크립트를 설명하기 전에 이번 강좌에서 가장 중요한 swapDepths에 대해서 살펴보겠습니다. 온라인 multi-user game을 제작할 경우, 내가 제어하는 캐릭터를 전후,좌우로 움직일 때, 캐릭터들의 앞뒤의 위치를 계산하고 위치를 바꿔주는 게 아주 중요하겠지요. 이럴때 가장 필요한 기능이 swapDepths입니다. 적용하는 방법은 아래의 2가지가 있습니다. anyMovieClip.swapDepths(depth);//anyMovieClip의 depth를 괄호안의 depth값으로 바꾼다. anyMovieClip.swapDepths(target);//anyMovieClip의 depth와 target에 지정된 무비클립의 depth를 교환한다. depth를 교환하게 되면 두개의 놓여지는 z-order가 바뀝니다. 만약 a,b두개의 무비클립에서 a가 b를 가리고 있다면 swapDepths를 하면 b가 a를 가리게 되겠죠. 클릭한 무비가 항상 맨 위로 올라오게 한다고 해봅시다. 여러개의 무비가 duplicate혹은 attachMovieClip을 통해 depth가 정의되어 stage에 생성되었다면, 그 무비들의 depth는 가능하면 1000이하에서 처리합니다. 그리고, _root.MaxDepth = 1000 라고 선언합시다. 클릭해서 가장 위로 올라올 모든 mc들의 버튼에 on (release){ this.swapDepths(_root.MaxDepth++); } 를 하게 되면 클릭되는 순간 클릭된 mc들은 _root.MaxDepth의 값으로 바뀌고 _root.MaxDepth값에 1을 더해 둡니다. 그래서,클릭된 mc는 항상 맨위로 올라오게 됩니다. 샘플로 제시한 무비는 이방법을 통해 간단하게 구현할 수 있습니다. 왜 복잡하게 만들었냐구요...하하...강좌를 위해서... 그리고, 만약 앞서 말씀드린 온라인게임등에서는 swapDepth가 빈번하게 적용됩니다. flash 플레이어가 아직도 치명적인 문제로 안고 있는 것은 메모리 관리입니다. 인터넷 익스틀로러 등에서 이미 경험한 분도 있을 지 모르겠지만, 플래쉬 무비가 있는 페이지를 아무동작도 취하지 않고 내버려 두는데, 점점 메모리를 잡아먹다가 결국, 시스템을 다운시키는 경우가 있습니다. flash player가 계속 메모리를 삼켜버리기 때문에 생긴 문제이지요. 가끔 swapDepths를 위와 같은 방식으로 적용하면 swap되기전의 movie가 메모리상에 남겨져서 스크립트 오류가 난다거나, 이상한 동작을 할 경우도 있습니다. 그래서, swapDepths가 빈번하게 일어나는 플래쉬 무비라면 앞으로 설명드릴 방법을 사용하기를 권장합니다. 우선 swapDepths를 통해서 swap할 무비들의 갯수를 알고 있어야 합니다. 각 무비들의 depth를 배열객체에 저장해 둡니다. swap이 일어나면, swap된 순서에 따라 다시 배열객체를 재 배열합니다. 이것은 철저한 관리라고 표현해야 겠죠. swap이 일어나면 거기에 따른 정보를 다 기록해 두는 거죠. 대신 swap되는 depth는 일정한 범위(각 mc들이 depth를 교환하기 때문에 위의 예처럼 depth가 계속 새로운 게 생겨나지는 않죠)내에 있게됩니다. 이 방법은 샘플 무비에서 구체적으로 실현되어 있습니다. 이제는 샘플 무비의 스크립트를 하나씩 뜯어 보기로 하죠. 5. 샘플무비 스크립트 샘플무비의 상단왼쪽의 동그라미가 control무비입니다. 우선 function getWindowSize(){ _root.attachMovie("unit", "window", 1); wWindow = _root.window._width; hWindow = _root.window._height; _root.window.removeMovieClip(); } function getStageSize(){ //get the width and height of stage var bound = _root.stage.getBounds(); xbound = bound.xMax - bound.xMin; ybound = bound.yMax - bound.yMin; trace(xbound + ":" + ybound); } 는 새로운창이 놓여질 범위를 계산하기 위한 겁니다. getWindowSize는 unit라는 mc(이게 window 입니다.)의 _width와 _height를 측정하기 위해 순간적으로 stage에 올려놓고 그 값들을 구한다음 제거하고 있습니다. 만약, 작업을 하실 때, unit의 사이즈를 정확히 고정시키셨다면, 이 함수 대신에 바로 wWindow, hWindow 에 해당값을 대입하셔도 무방합니다. 이 함수는 지극히 단순한 목적, 생설될 윈도우의 폭과 넓이를 구하기 위해 편법같은 겁니다. getStageSize는 쉽게 갈 수 있는 길을 택하지 않고 좀 어려운 길을 택했습니다. getBounds란 method를 사용했죠..이것은 무비클립의 x,y최대최소값을 object요소로 반환합니다. bound는 위 4개의 값을 갖는 object가 되고, xMax, xMin ,yMax, yMin이란 property로 각각 의 해당값을 추출할 수 있습니다. 대개는 무비 제작시 해당 영역을 계산하는 것은 어렵지 않기 때문에 직접 xbound = 300; ybound = 280 등을 직접 값을 대입하는 게 실행속도에 조금이라도 이득이 있을 겁니다. 또는 xbound = _root.stage._width 로 간단히 할 수 있습니다. getBounds는 좌우폭보다는 hitTest와 사촌쯤이라고 생각됩니다. getBounds는 movieClipe 객체의 method 중의 하나입니다. 무비클립의 인스탄스가 stage에 놓여졌을 때 좌우,상하 의 각 좌표값을 반환하는 method입니다. 플래쉬에서의 좌표값이란 게 좀 복잡한 녀석입니다. 좌표를 계산하는 기준이 되는 mc들이 항상 지정되어 있어야 합니다. 그렇지 않으면 대부분 해당 mc들의 _parent를 좌표를 계산하는 기준으로 정합니다. a = this.getBounds(_root) 라고 한다면 현재 이 스크립트가 실행되는 mc의 좌우상하좌표값을 _root를 기준으로 되돌립니다. 4개의 값을 되돌리기 때문에 각 값은 a.xMax(오른쪽 x좌표값) , a.xMin (왼쪽 x좌표값), a.yMax(아래쪽 y좌표값), a.yMin(위쪽 y좌표값)에 저장되어 있습니다. 순서가 좀 바뀌었지만, function init(){ fscommand("allowscale",false); //testMovie에서 무비사이즈가 화면에 100%비례로 꽉차는 것을 방지, 실제 사이즈로 보여준다. name = 0; //각 윈도우의 이름첨자를 위한 변수의 초기화 depthArray = new Array(); //생성된 윈도우들을 저장할 배열선언 getWindowSize(); getStageSize(); } 는 필요한 변수들을 초기화하거나 값을 구합니다. getWindowSize나 getSageSize는 구태여 함수로 사용하지 않고, init내에 모두 스크립트를 옮겨도 무방합니다. 왜냐하면 초기화할 때 단 한번만 호출이 되거든요. 함수가 여러번 호출되어 사용된다면 유용하게 사용된 거지만, 단한번 호출될 녀석을 함수로 만들 필요는 없겠죠. 하지만, 이렇게 함수로 만들어 두면, 다음 프로젝트를 진행할 때 비슷한 기능을 적용한다면, 그대로 copy & paste해서 사용할 수 있겠죠..(재사용성) function newWindow(){ var depth = depthArray.length; //depthArray의 길이를 새로 생성될 윈도우의 depth로 정한다. name++; //윈도우의 name 은 새루운 윈도우가 생길 때마다 1씩 증가한다. 이것은 윈도우의 삭제와 무관하다. _root.attachMovie("unit","window" + name, depth); //set user-defined property of window _root["window" + name].name = "window" + name; //random positioning _root["window" + name]._x = _root.stage._x + random(xbound - wWindow); _root["window" + name]._y = _root.stage._y + random(ybound - hWindow); //coloring backColor = new Color(_root["window" + name].windowBack); var r = random(255); 0과 255사이의 값 var g = random(255); var b = random(255); //bitwise operator backColor.setRGB(r<<16 | g<<8 | b); //windowBack을 r<<16| depthArray.push(_root["window" + name]); //depthArray의 맨 마지막에 새로 생성된 윈도우를 삽입합니다.(참조하도록) _root["window" + name].depth = depth; //depth는 윈도우를 클릭했을 때 자기자신의 depth를 추적하기 위해 필요합니다. info(); } 이 newWindow함수는 몇가지 작업을 수행하고 있습니다. 1. 새로운 윈도우를 stage상에 올려놓는다.(attachMovie) 2. 새로운 윈도우를 주어진 영역의 적당한 곳(random()곳에 위치시킨다. 3. 새로운 윈도우의 backColor를 적당히 (random()) 설정한다. 4. 새로 생성된 윈도우를 depthArray 배열에 추가한다. 5. 새로운 윈도우의 depth속성에 값을 지정한다. 6. stage에 놓여진 윈도우들에 대한 정보를 화면에 출력한다. (info()) 입니다. 여기서 swapDepths와 관련해서 가장 중요한 것은 depthArray에 push되는 새로운 윈도우..그리고, depth입니다. 아래의 함수는 이 무비의 가장 핵심적인 요소입니다. 실제로 swapDepths를 하고, 배열의 순서를 바꿔줍니다. function depthExchange(clicked){ if (clicked.depth != depthArray.length - 1){ var currentDepth = clicked.depth; for (var i = depthArray.length - 2; i >= clicked.depth;i--){ var temp = depthArray[i].depth; depthArray[i].depth = depthArray[i+1].depth; depthArray[i+1].depth = temp; depthArray[i].swapDepths(depthArray[i+1]); } depthArray.swapToEnd(currentDepth); info(); } } 위 함수는 3가지 동작이 수행되고 있습니다. 1.각 mc들이 변수인 depth의 값을 교체한다. var temp = depthArray[i].depth; depthArray[i].depth = depthArray[i+1].depth; depthArray[i+1].depth = temp; 2. 실제 swapDepths를 한다. depthArray[i].swapDepths(depthArray[i+1]); 3. mc들을 참조하는 depthArray의 순서를 바꾼다. depthArray.swapToEnd(currentDepth); 1에 대한 설명: depthArray는 앞서 말씀드린 대로 새로운 윈도우가 생성될 때 생성된 mc들을 push를 통해 모두 저장하고 있습니다. depthArray[1]이라고 하면 depthArray의 2번째 요소(0이 첫번째 요소)가 됩니다. depthArray의 두번째 요소에 저장된 윈도우를 지칭하게 되는 거죠. 그러니까, depthArray[1].depth는 실제로 해당 윈도우의 depth라는 변수를 참조하고 있는 겁니다. 식을 다시 풀어본다면 temp = depthArray의 i에 저장된 윈도우의 depth 변수값; depthArray의 i+1에 저장된 depth값을 i번째 저장된 윈도우의 depth변수에 대입 i+1번째에 참조(저장)된 윈도우의 depth값을 temp값으로 결국, i번째 depth값과 i+1번째 depth값을 교환한 거죠. 참, 여기서 depth는 그냥 정의된 변수입니다. swapDepths의 depth랑은 관련없어요. 2에 대한 설명 i번째,i+1번째 윈도우의 swapDepth 3에 대한 설명 depthArry는 배열객체입니다. swapToEnd는 prototype을 통해 선언된 배열객체의 사용자 정의 함수입니다. array.prototype.swapToEnd = function(index){ var temp = this[index]; this.splice(index,1); this.push(temp); } 가 이 부분이죠. index값이 넘어오면 temp에 index의 위치에 있는 값을 저장하고 splice에 의해 index의 위치에 있는 값을 제거합니다. 그리고, push에 의해 temp값을 맨뒤에 추가합니다. 결국, index의 위치에 있던 값을 맨뒤로 옮겨놓은 것과 같은 결과지요. 이렇게 배열에 기본적으로 제공되어 있지 않은 기능을 prototype을 통해 정의하면 앞으로 생성되는 모든 배열객체는 swapToEnd라는 함수를 적용할 수 있습니다. 배열뿐만 아니라, date,movieClip등 모든 객체들에 적용할 수 있는 있습니다. prototype 잘 알아두시면 매우 유용합니다...공부..공부 (-_-;) 위의 1,2,3을 통해 하고 있는 일은 array를 통한 depth의 관리입니다. 안타깝게도,플래쉬에서 각 무비클립의 depth값을 뽑아내는 함수나 property가 없습니다. 그래서, 무비클립을 생성하는 시점부터 모두 배열객체에 집어넣고, swapDepths를 통해 depth가 바뀔 때마다 모두 추적해서 배열에 저장해서 관리를 하는 거죠. 새롭게 생성된 mc들은 depth가 바로 전의 depth보다 1씩 큽니다. 배열에 push를 통해 삽입하기 때문에 역시 depth가 큰게 뒤쪽에 옵니다. swap이 일어나면, 배열의 해당위치에 있는 것을 역시 맨뒤로 돌려놓습니다. 이렇게 해서, 실제 각 윈도우들의 depth들은 배열에 그대로 순서대로 저장되어 있습니다. 윈도우를 화면상에서 제거하면, 배열에서도 제거를 합니다. 제거를 하면 배열의 중간을 삭제하는 게 아니라, 제거하기 전에 맨위로 올려놓고, 맨위에 있는 윈도우를 삭제하도록 하였습니다. 중간에 이가 빠지게 되면 스크립트가 더욱 복잡해지니까요... function removeWindow(clicked){ depthExchange(clicked); depthArray.pop(); clicked.removeMovieClip(); info(); } 그래서, depthExchange함수를 호출, clicked된 mc의 depth를 가장 위에 올려놓고, pop을 통해 depthArray배열의 맨 마지막 요소를 삭제하고, removeMovieClip에 의해 clicked된 mc를 실제 stage상에서 제거합니다. 예,,압니다..쓸데없이 복잡하고, 설명도 어지럽다는 것... 정리가 잘 안돼네요.. 우선, 앞에 강의한 개념들을 명확히 이해하고 계시면, 설명없이 스크립트를 분석해보세요. 그다음에 설명을 읽어보시면, 아하,,이런 말들이구나,,라고 하시지 않을 까..싶어요.. |
http://yawoong.com/board/view.php?id=tutorial_yonwoo&no=53&keyword=allowscale&sn=off&ss=on&sc=on