부록 - 주사위 컴포넌트

 


        원고를 마치고 내친 김에 주사위컴포넌트를 만들었기에 여기에 부록으로 첨부한다.  이 글을 쓰기 전에 나는 이 주사위를 이 작품집의 주제로 삼는데 회의를 가졌다. 그래서 저자들의 모임 클럽의 자유 게시판에 아래와 같은 글을 올렸다. 



 안녕하세요.

        내가 플래시를 배우고 처음 제작한 것이 주사위였습니다.  상당히 시간과 공을 들여 제작했기에 actionscript.org. 에올렸습니다.지금도가끔아래와같은 e-mail 을받습니다.

Dear Koo Chul Lee

I am not a technical guy but just wanted to say how impressed I am at the size efficiency of your dice etc on actionscript.org.

Really cool stuff.

Do you do flash design work?

Regards,

William ****

        그래서 주사위에 애착이 갑니다. 그래서 이 주사위를 이 프로젝트의 sample로 삼아서 해설을 할까 생각했습니다.  V6로 그린 것이라 V7(FlashMX2004)로 update 하려다 V6 코드를 다시 보니 세상에 나처럼 주사위를 그릴 사람은 나 빼고는 없을 것이라는 생각이 들어서 이 글을 씁니다.

        플래시에는 3D 엔진이 달려 있지 않기 때문에 플래시의 3D 애니메이션을 하는 경우 Swift3D, Vector3D 같은 제3자 소프트웨어 것을 연동해서 쓰라는 것이 전문가들의 권고입니다. 아마 이것이 정수일 것입니다.

        플래시만 써서 3D 애니메이션을 한다는 것은 엄청난 기술적 지식과 노동이 요구되는 작업입니다.  설혹 기술적 지식이 있다 하더라도 이 방법으로 3D를 하는 이는 없을 것입니다. 풀그림을 생업으로 하는 사람은 생산성이 떨어져 밥 먹기가 힘들 테니까요. 나처럼 시간이 많아 풀그림을 “놀이”로 하는 이나 할 일이라는 생각이 듭니다. 또 내가 그렇게 풀그림을 할 수 있는 것은 내가 마우스가 발명되기 훨씬 이전의 "원시시대"부터 풀그림을 해 왔기 때문에 가능할 것입니다.

        나는 응용 프로그램을 좋아 하지 않습니다.Versatility 가 없기 때문입니다.90년도 초에 Mathematica를 내 개인용 워크스테이션에 거금 2500불을 지불하고 single user license로 설치한 일이 있었습니다.몇 번 쓰고는 버렸습니다.Flexibility 가 없어서 내가 원하는 데로 output를 낼 수가 없어서 였습니다.Mathematica가 할 수 있는 것은 나는 Pascal 이나 C로 풀그림을 할 수 있기 때문이었습니다. 물론 엄청난 노동이 필요했지만 말입니다.

        요즘처럼 Productivity 위주의 세상에서 나 같은 방법을 전수한다는 것이 무슨 의미가 있는가 하는 의구심이 들어서 이 글을 씁니다.  여러분은 어떻게 생각하시는 지요?

 




        그런데 이 게시글에 다른 저자들의 덧글은 한결 같이 이 주제가 적합하다는 격려였다.  그래서 이 원고를 쓰게 된 것이다.  그러나 아직도 이 의구심은 남아 있다. 그래서 마지막으로 생각한 것은 이 주사위를 컴포넌트로 만들어 3d 풀그림에 익숙하지 않는 프로그래머나 디자이너에게 프로그램없이 사용하게 해 주는 것이었다.

        컴포넌트의 일반적 제작이나 사용법은 이 책의 다른 저자들이 상세히 다루었으므로 생략하고 이 주사위 컴포넌트의 사용법만 간단히 다루겠다.  먼저 컴포넌트를 컴포넌트 패널에 보이게 설치하려면 매크로미디어사가 무료 배포하는 extension manger를 내려 받아 사용하기 바란다.  아래의 사이트에서 무료 다운 받을 수 있다.

http://www.macromedia.com/exchange/em_download/





그림 18) 주사위 컴포넌트가 설치된 components panel


 

        이 이책의 CD에 포함된 DiceComp.mxp (Macromedia Exchange Package)를 Extension Manager 로 설치하면 자동적으로 component 패널에 <그림39>와 갈이 주사위 컴포넌트가  설치된다.


 



 

그림 19) 주사위를 stage에 끌어다 놓으면 그 밑에는 속성창이 뜬다.

        


         이컴포넌트가 일단 설치되면 그 사용법은 여느 다른 UI 컴포넌트와 같다. 즉 컴포넌트를 마우스로 잡아서 stage 에 가져 오면 <그림40>과 같이 주사위를 stage 에 생성한다.  그리고는 그 property 창에서 주사위의 속성을 편집한다. 속성창에서는 주사위의 6면의 색깔을 편집할 수 있다.   colArr 가 바로 그것이다.  다음은 귀퉁이의 색을 편집할 수 있다.  cornerColor가 그것이다. 다음은 초기 주사위의 놓임새를 편집할 수 있다. DiceNum 이 그것이다.  6을 입력하면 주사위의 초기 지향은 6이 위로 향하여 앉아 있게 된다.  또 주사위의 점 색깔을 편집할 수 있다.  dotColor 가 바로 그것이다. 끝으로 주사위를 클릭했을 때 뛰어 오르게 하거나 정지 상태에서 회전하거나를 선택할 수 있다.  그림<그림41>은 속성창을 편집했을 때의 보기를 하나 스크린 셧으로 보이고 있다. 

        마지막으로 주사위를 클릭하였을 때 나오는 점수를 액션스크립트에서 사용하기 위해서는 <그림42>의 오른쪽 위에 있는 스크립트와 같은 이벤트 핸들링을 쓴다.  이  스크립트는 여느 다른 UI컴포넌트의 이벤트 핸들링 메쏘드와 다를 바  없으므로 자세한 설명은 생략한다. 


 

그림 20) 속성창을 편집한 예

          여기서 myDice 는 이 컴포넌트의 인스턴스 이름이다.  아래 무비는 이 컴포넌트의 미리보기 무비다. 이 무비에서는 세 번째 제일 큰 주사위는 뛰지 않고 제 자리에서 회전하여 점수를 제공한다.  CD에 수록된 <DiceTeste.fla> 파일을 열어 액션을 살펴 보기 바란다.  이 컴포넌트는 주사위를 던져서  나오는 점수로 게임을 하는 풀그림에 사용할 수 있을 것이다.  

         




무비1 컴포넌트 미리보기 무비
주사위를 던지려면 주사위에 마우스를
대고 클릭한다.
3번째 주사위는 앉은 자리에서 그대로 회전한다.

       
이것이 내가 독자 여러분에 해 줄 수 있는 모든 것이다.  

CD.zip

 


---------------------
[후기]

첫번째 게재물(주사위1)의 글 첫머리에 밝혔 듯이 이 글은 원래 종이책을 위해 썼던 원고를 내키지 않아 기고를 철회하고 묻어 두었던 것을 거의 그대로 마디(section) 별로 나누어 웹 문서로 고쳐 게재한 것이다. 

거의 원고 그대로이지만 플래시 무비가 있는 경우는 무비를 그대로 올려 글을 읽으며 스크린위에서 실험해 볼 수 있게 했다.

이 게재를 마치면서 adobe 사의  컴포넌트 download 사이트에 가서 내가 만든 주사위 컴포넌트의 다운로드 횟수를 보니 오늘 날자(2009-02-20)로 54,684 Downloads 가 있었다.

그 정도 면 빚은 조금 갚고 가는 것 같다.  

 

 

Posted by 샛솔 :


 

          마지막 강좌에서 점의 위치를 인수로 받아서 그 주변의 8개의 점을 생성하여 반환하는 calDotData 함수의 코드까지 설명했다.


          다음은 24개의 꼭지점에서 6개의 8각형 주사위 면과  8개의 잘라나간 귀퉁이 3각형 면을 정의하는 인덱스 배열을 코딩하는 문제가 남아 있다.  이들은 앞마디(주사위9)의 <표1>의 줄16, 17에 정의된 cornsIndex8 과 cornsIndex3라는 배열이다.  <표1>에 올려 놓았다.



1

cornsIndex8 = new Array( [0, 1, 4, 3, 6, 7, 10, 9], [1, 2, 14, 13, 16, 17,  5, 4], [3, 5, 17, 15, 18, 20, 8, 6], [0, 9, 11, 23, 21, 12, 14, 2], [7, 8, 20, 19, 22, 23, 11,10], [12, 21, 22, 19, 18, 15, 16, 13])

2

cornsIndex3 = new Array([2, 1, 0], [3, 4, 5], [8, 7, 6], [9, 10, 11], [12, 13, 14], [17, 16, 15], [18, 19, 20], [23, 22, 21]);


표1  두개의 인덱스 배열 정의


           이 배열을 코딩하기 위해서  앞마디(주사위9)의 <그림1>왼쪽과  같은 모형이 필요했던 것이다. <그림1>의 제일 앞으로 보이는 귀퉁이 3각형은 cornsIndex3의 6 번째 요소 cornsIndex3[5] = [17,16,15]로 그 순서가 바로 오른쪽나사법칙으로 [17,16,15]->[16,15,17] 방향으로 회전하면 나사의 전진 방향이 3각형의 표면을 표시하고 있다. 이러한 순서를 잘 매기지 않으면 면의 앞뒷면을 가리는데 일관성을 유지할 수 없게 된다. 

 

1

 public  function showDice(dist:Number, isIso:Boolean):Void  {

2

 distZ = dist;

3

 isIsometric = isIso;

4

 var imax = aCube.length;

5

 var zz :Number ;

6

for (var i = 0;  i <imax;  i++)

7

{ currentCube[i]=  rotMat.transform3DBody(aCube[i]);

8

 if (isIsometric)  currentCube[i] = Isom.mTSB(currentCube[i]);}

9

 for (var i = 0;  i <imax;  i++)

10

aCube2D[i] = GraUtil.convScreen(currentCube[i], distZ);

11

 mc.clear();

12

 for (var s=0; s<6; s++) {

13

 for (var k = 0; k< 8; k++)

14

 corns2D[k] =  aCube2D[cornsIndex8[s][k]];

15

 zz = GraUtil.calZZ(corns2D);

16

  if(zz>0)

17

 {drawFilledPoly(corns2D,  colA[s], 100);

18

 var jb = s*(s+1)/2;

19

var je =  (s+1)*(s+2)/2;

20

for(var j = jb;  j<je; j++)

21

drawDot(dotData[j]);

22

 }

23

}

24

//will cut  8 corners

25

for (var s=0; s<8; s++) {

26

for (var ii = 0;  ii <3; ii++)

27

corns2D3[ii] =  aCube2D[cornsIndex3[s][ii]];

28

 zz = GraUtil.calZZ(corns2D3);

29

 if(zz>0)

30

drawFilledPoly(corns2D3,  colCorner[s], 100);

31

}      

32

}//showDice()  


표2 createDice 클래스의 퍼브릭 메쏘드 함수 showDice.

 

           CreateDice클래스의 생성자함수를 제외한 유일한 퍼블릭 메쏘드 함수는 showDice로 이것으로 주사위를 그린다.  아래의 <표2>에 코드를 올려 놓았다.  CreateDice의 내부 메쏘드 함수들을 써서 주사위를 그리는데 인수로 dist 라는 원근법 여부를 결정하는 거리와 아이소메트릭공간에 투영할 것인가를 묻는 isIso 변수를 인수로 받아 들인다.   이 인수는  줄10의 GraUtil.convScreen함수에 전달되는데 이 static class 유틸리티 함수는 아래의 <표3>에 올려 놓았다.  이 유틸리티 클래스에는  두 메쏘드 함수만 있는데 calZZ는 그리려는 면의 꼭지점의 배열을 인수로 받으면 벡터  (0→1)   와 (1→2) 를 구성하여 그 벡터 곱, (0→1)x(1→2)    의 Z성분을 셈하여 반환한다. 


          GraUtil.convScreen 함수는 거리를 인수로  받아서 그 거리가 1000보다 크면 원근법을 사용하지 않고 그 보다 작으면 그 거리를 카메라 거리로 삼아서 원근법 변환을 적용한다.  자주 쓰는 함수는 static 메쏘드 함수로 정의해 두면 아무 때나 Math 클래스 함수를 불러 쓰듯 손쉽게 사용할 수 있다.  

 

1

class GraUtil{

2

 

3

static function calZZ(vecArr:Array):Number {

4

        var x01 = vecArr[0][0]- vecArr[1][0];

5

        var y01 =vecArr[0][1]-vecArr[1][1];

6

        var x21 =vecArr[2][0]-vecArr[1][0];

7

        var y21 =vecArr[2][1]-vecArr[1][1];

8

        return (x01*y21-y01*x21);

9

}

10

static function convScreen(pt3D:Array, distance:Number):Array{

11

                var scale :Number ;

12

                if (distance <1000)

13

                scale = distance/(distance + pt3D[2]);

14

                else  scale = 1;

15

var pt2D = [];

16

pt2D[0]  =pt3D[0]*scale;

17

pt2D[1]  =pt3D[1]*scale;

18

return pt2D;}

19

}


표3 GraUtil static 유틸리티 클래스

 


          <표2> 의 showDice 메쏘드는 먼저 줄6,7,8에서 24개의 꼭지점을 현재 변환행렬을 가지고 변환시킨다.  아이소메트릭 공간에 투영하고 싶으면 한번 더 변환을 수행한다. 배경 자리표를 그린다든가 공간에서의 지향 관계를 보이고 설명을 할 필요가 없을 때엔 이 변환은 생략하는 것이 CPU에 짐을 덜어 줄 것이다.        

          줄10은 원근법여부를 정하기 위하여 넣은 코드인데 이것 역시 원근법이 필요가 없으면 showDice 의 인수 dist에 1000 보다 큰 수를 넣으면 된다.   줄12에서 줄22까지가 주사위의 6면을 그리는 부분인데 s가 면을 나타내는 변수이고 k는 8각형의 꼭지점를  나타낸다.  aCube2D는 회전된 24개의 꼭지점으로 Z성분을 제거한 2차원 스크린 자리표를 나타낸다. 이 때 줄14의 인덱스가 어떻게 쓰이는가를 잘 음미하게 바란다. s면의 k번째 꼭지점인 corns2D[k](2차원 점) 는 2차원화된 일반적인 꼭지점 aCube2D 중에서 s면 인덱스(8개의 8각형 꼭지점을 담은 배열)의 k번째 성분으로 결정하는 것이다. 

          줄15는 이렇게 정해진 8각형의 2차원 꼭지점배열을 인수로 하는 GraUtil.calZZ(corns2D)함수로 꼭지점 "0", "1", "2"를 가지고 벡타   (01)   와 (12) 를 구성하여 그 벡터 곱, (0→1)x(1→2)    의 Z성분을 계산한다. 이 때 배열은 크기가 3요소이상(3각형이상) 이고 성분은 2이상(2차원 이상)이면 된다. <표15>의 줄3~9를 음미하기 바란다. 이렇게 하여 16줄에서 s면이 앞면이면 면과 점을 그리라는 명령을 수행한다.  

            앞마디(주사위9)의 <표3>에서 21개의 주사위의 점에  0, 1, 2, ....20까지  일련번호를 매긴 것을 기억할 것이다.  <표2>의 줄18과 19는 s 면의 점의 시작과 끝 번호를 계산한 것이다. 마지막으로 줄24~31은 잘려 나간 주사위 귀퉁이 3각형을 그리는 명령부분이고 주사위의 큰 면을 그릴 때 쓴 방법과 같은 방법을 쓴다. 되풀이 설명이 필요 없을 것이다.    

            이렇게 하여 마침내 주사위를 그릴 수 있게 되었다. 그러면 이 주사위를 어떻게 제어할 것인가?  <표4>의 코드는 주사위를 제어하기 위해 만든 것이다.



1

var dpt:Number = -1000;

2

createEmptyMovieClip("dice_mc",  dpt++);

3

var colArr:Array =  new Array(ColorUtil.violet, ColorUtil.pink, ColorUtil.lightgreen,  ColorUtil.cyan, ColorUtil.magenta, ColorUtil.beige);

4

var colorCorn:Array =  new Array( ColorUtil.darkgray,  ColorUtil.darkgray,  ColorUtil.darkgray,  ColorUtil.darkgray,  ColorUtil.darkgray,  ColorUtil.darkgray,  ColorUtil.darkgray,  ColorUtil.darkgray);

5

var dotColor:Number = 0x333333;

6

var dice:CreateDice  = new CreateDice(dice_mc , 80,  0.3 ,  0.1,  colArr,  colorCorn, dotColor)

7

dice.mc._x= 290;

8

dice.mc._y= 300;       

9

        var ranNum = Math.floor (6*Math.random ()); 

10

var eulerSet:Array = new Array([180 ,180 ,270], [180, 270, 270], [270, 90, 180], [270, 270, 180], [270, 90, 270], [180, 360, 270]);

11

var phi = eulerSet[ranNum][0];

12

        var theta = eulerSet[ranNum][1];

13

        var psi = eulerSet[ranNum][2];

14

        dice.rotMat.eulerAngles(phi,theta,psi);

15

        dice.showDice(999, true);

16

         var tt = 0;

17

function runthis(){

18

                if (  tt<400)

19

{tt += 10;     

20

dice.mc._y =  0.005*(tt - 200)*(tt - 200) + 100;

21

theta  -=Math.log(6*Math.abs(theta - eulerSet[ranNum][1])+1);

22

psi  -=Math.log(7*Math.abs(psi - eulerSet[ranNum][2])+1);

23

dice.rotMat.eulerAngles(phi,theta,psi);

24

         dice.showDice(1000,true);

25

          updateAfterEvent();

26

                 }//if (  tt<400)       

27

                else{

28

                clearInterval(id1); delete  id1;

29

                }//else

30

                }//function runthis(){

31

dice.mc.onRelease = function(){ ranNum = Math.floor (6*Math.random ());  phi = eulerSet[ranNum][0]; theta =  eulerSet[ranNum][1]+250; psi  = eulerSet[ranNum][2]+250;

32

tt= 0;

33

if (id1 == undefined)

34

id1= setInterval(runthis, 25);

35

else{clearInterval(id1); delete  id1; id1= setInterval(runthis, 25);}

36

} //function


표4 주사위 제어하기

 
             제일 먼저 주사위를 담을 무비클립을 줄2에 만든다. 이 무비 클립의 이름은 dice_mc이다. 이제 주사위의 면, 귀퉁이의 3각형과 면위의 점들의 색깔을 정한다. 이 색깔 배열과 기타 주사위의 크기와 자세한 설계 데이터를  인수로 넣고 줄6에서 createDice 클래스의 인스턴스를 만든다.  그 이름은 dice 이다.         이제 이 dice의 속성만 가지고 주사위를 제어한다. 주사위의 병진 운동은 dice 인스턴스의 속성 mc, 즉, dice.mc의 위치로 제어 한다. 이 무비클립 은 처음에 만든 dice_mc와 동등하다.


          그러나 일단 이 인스턴스 dice 만 가지고 모든 풀그림을 짜는 것을 원칙으로 하기 때문에 dice.mc._x, dice.mc._y 로서 운동을 제어한다.  다음은 dice 의 지향인데 이 dice의 속성, 즉 주사위 속성인 rotMat를 써서 제어한다. rotMat 는 dice를 생성할 때에 초기화 과정에서 만들어진 Rotation3D 의 인스턴스다. 그러므로  주사위5 (박스 그리기)의 <표2>에 나열했던 모든 속성과 메쏘드함수를 불러 쓸 수 있다.   

        줄10에는 주사위의 6개의 면이 위로 향하는 지향에 해당하는 오일러각 세트를 6개 구해서 배열에 담아 놓는다.  처음에 0과 5사이의 막수를 생성하여 (줄9) 그 지향으로 주사위를 놓는다.  (줄11~15 참조) 


        주사위를 클릭하면 주사위는 병진운동과 회전운동을 수행하다 바닥에 떨어지면 새로운 숫자가 위를 향해서 앉게 한다.  그 부분이 줄17에서 줄30까지의 코드이다. 이 부분은 물리와 약간의 수학을 가미하여 운동이 자연스레 보이도록 하였다. 이러한 것이 일종의 물리모델인 것이다.  예를 들어 줄20은 고등학교 물리교과서에 나오는 내용으로 지구위에서와 같이 일정한 중력가속도   [g]가 작용할 때 물체의 속도는 [v=-gt +v0]  가 된다는 사실을 쓰고 있다.  그래서 줄31에서 주사위를 클릭하면 주사위가 던져지고 자연스런 운동을 하면서 내려 앉게 된다.  일단 내려 앉으면 더 이상 CPU는 아무 일도 않는다. 






무비 1 완성된 주사위
주사위를 던지려면 주사위에 마우스를 대고 클릭한다. 
 


맺음말
             여기  우리는 순전히 액션스크립트 프로그래밍만으로 주사위를 그렸다.  그래서 swf 파일의 크기가  4144 바이트, 약 4KB 에 불과하다.  바이트란 무엇인가?  1 바이트는 8비트다. 1 비트는 1 binary digit 즉 2진수의 1자리를 뜻한다. 4144 bytes = 4144 x8 = 33152 bits 이다. 나는 주사위를 그린 것이 아니라 33152자리의 2진수를 하나 적은 것이다.  

            주사위를 그리기 위해 플래시를 배웠고 플래시를 배우고 처음 그린 것이 주사위였다.  그래서 이 주사위에 애착이 가기에 이 작품집의 주제로 내 놓은 것이다. 그러나 과연 이러한 방법으로 플래시를 사용하는 것이 권장할 만한 일인가?   생산성이란 면으로는 결코 권장할 만한 일이 못 된다고 생각한다. 그러나 나는 이것을 즐기며 할 수 있었기에 작업할  수가 있었던 것이다.

           Donald Knuth 교수는 프로그래밍은 시를 쓰거나 작곡을 하는 것과 같은 심미적 체험을 안겨 준다고 말했다.  나는 여기에 덧붙여 플래시는 조각가가 돌을 깎는 작업과 같다고 말하고 싶다.  힘들고 고된 작업이라도 조금씩 조금씩 형상이 보이고 마침내 머릿속에 그렸던 그림이 화면에  나타 날 때의 기쁨은 길고 긴 여정의 피로를 씻겨 준다.  
 
                                        2004. 5. 8  샛솔 이구철 
 
 


 

Posted by 샛솔 :


 

마침내 주사위

          이제 주사위를 그릴 모든 준비가 끝났다. 정6면체의 8개의 꼭지점을 3각형으로 자른  주사위는  24개의 꼭지점을 가진 볼록 14면체이다.   6개의 8각형 큰 면과 8개의 3각형 작은 면을 가진 14면체다. 따라서 주사위를 그리려면 14개의 면을 그려야 한다.  24개의 꼭지점에 번호를 매기고 그 번호로 면을 정의하는 인덱스를 코딩하여야 한다. 나는 프라톤 입체를 그릴 때와 마찬가지로 모형을 만들어 사용하였다.  찰흙과 이쑤시개로 만든 정6면체 모형의 8 귀퉁이에 생긴 3각형 꼭지점에 번호를 붙였다.


          아래 그림 <그림1>의 왼쪽 것이 바로 그것이다.  이 경우 번호 매김은 어떤 순서로 하거나 상관이 없다.  나중에 면을 정의하는 인덱스 배열을 만들 때 그 순서를 일관성 있게만 해 주면 된다. 

 



그림 1) 주사위를 그리기 위하여 만든 모형과 플래시로 만든 주사위 스크린 셧


           위 그림 <그림1>의 오른 쪽 것이 왼쪽 모형에 따라 만든 주사위 최종 결과물 스크린 셧이다.   이제 이 주사위를 던질 수 있게 만드는 법을 설명한다. 

먼저 이 주사위를 생성하는 CreateDice 클래스의에 대해 설명한다.  그 클래스의  빼대는 <표1>에 올려 놓았다.  20개의 속성과  6개 메쏘드 함수로 구성되었다. 20개의 속성 가운데 2개만 public 으로 밖에서 불러 내어 조종할 수 있고 나머지 18개는 private 속성으로 클래스 내부에서만 사용할 수 있다. 메쏘드 함수도 두개만 public이고 나머지 4개는 private로 클래스 안에서만 사용할 수 있다.  public 함수중에서 하나는 생성자(constructor) 함수로 클래스 인스턴스를 만들 때 한번만 쓰이는 것이므로 아무때나 밖에서 실행시킬 수 있는 public 함수는 하나뿐이다. 


 

1

class CreateDice{

2

        public var mc:MovieClip;

3

        private var sizeCube:Number;

4

        private var cornerSizeRatio:Number;

5

        private var sd :Number ;

6

        private var sizeDot:Number;

7

        private var colA:Array ;

8

        private var colCorner:Array;

9

        private var colDot:Number;

10

        public var rotMat : Rotation3D ;

11

        private var aCube:Array;

12

        private var currentCube:Array ;

13

        private var aCube2D:Array ;

14

        private var corns2D:Array;

15

        private var corns2D3:Array;

16

        private var cornsIndex8:Array ;

17

        private var cornsIndex3:Array ;

18

        private var dotsC:Array ;

19

        private var dotData:Array;

20

        private var distZ:Number;

21

        private var isIsometric:Boolean;

22

 

23

 

public function CreateDice(diceMc:MovieClip , diceSize:Number, cornSize:Number , dotSizeR:Number,  colorArray:Array,          colorCorner:Array,dotCol:Number){//***//}

24

private function init():Void {//***//}

25

private function calDotData(pt3D1:Array):Array {//***//}

26

private function drawFilledPoly(corners,  fillC,  alp):Void {//***//}

27

private function drawDot (oneDot):Void{//***//}

28

public function showDice(dist:Number, isIso:Boolean):Void  {//***//}

29

}//end class def.


표1) class CreateDice 의 코드의 골자



          줄2의 MovieClip 속성 mc 는 주사위가 생성되는 무비클립이다. 따라서 주사위의 병진 운동은 mc._x, mc._y를 변동시켜 스크린 위에서 움직이게 한다.  줄3의 sizeCube는 주사위의 크기로서 초기화할 때 한번 생성자 함수의 인수로 받아 정하면 주사위를 없앨 때까지 바꿀 수 없다. 줄4의 cornerSizeRatio는 주사위의 모서리의 잘린 부분의 크기를 정한다. <그림37>에서 cornerSizeRatio=0.3이다. 줄5의 내부 속성변수 sd=1-cornerSizeRatio 로<그림37>에서 보면 0.7이 된다.  나머지 속성변수를 모두  여기서  설명하지 않겠다.  클래스 안에서 이들 속성이 어떻게 쓰였는가를 보면 그 속성의 의미를 이해할 수 있다.  



그림 2) 무비1에 그린 주사위 설계도



 

           가장 중요한 속성은 aCube 라는 배열로 전에 말했던 24개의 꼭지점을 정의하는 데이터 배열이다.  이 x,y,z 성분으로 표시되는 24개의 3차원 점의 배열임에 주의하기 바란다.  주사위의 중심을 3차원공간 자리표의 원점(0,0,0) 에 놓고  이 원점에서 주사위의 변까지의 거리를 1로 잡았을 때의 꼭지점 자리표를 <그림36>의 모형의 번호순으로 기입한 것이다.  생성자 함수를 실행하면 그 일부에서 아래 <표10>의 코드로 기술 되는 aCube 배열이 생성된다.

 

 aCube = new Array( [sd, 1, -1],  [1, sd, -1], [1, 1, -sd], [sd, -1, -1], [1, -sd, -1], [1, -1, -sd], [-sd, -1, -1], [-1, -sd, -1], [-1, -1, -sd], [-sd, 1, -1], [-1, sd, -1], [-1, 1, -sd], [sd, 1, 1], [1, sd, 1], [1, 1, sd], [sd, -1, 1], [1, -sd, 1], [1, -1, sd], [-sd, -1, 1], [-1, -sd, 1], [-1, -1, sd], [-sd, 1, 1], [-1, sd, 1], [-1, 1, sd] );

표2 주사위의 24개의 꼭지점의 3차원 자리표로 된 배열  (표10)

 


          다음 중요 속성 변수는 주사위의 텍스쳐 데이터인 점들의 위치를 담은 배열 18줄의 dotsC와 그리기 데이터를 담은 19줄의 otData 다.  dotsC 는 주사위의 점들의 중심의 자리표를 모은 배열이다.  주사위에는  점이 1+2+3+4+5+6 = 21 개가 있다. 이들의 위치를 다음과 같이 배열에 담았다.

        <그림2>의 설계도에 점들의 위치를 표시했는데 는 "2" 와 "4" 만 실선으로 표시된 위치에 점을 그리고 나머지 "1", "3", "5", "6" 은 점선으로 그린 원들의 위치에 그렸다. 그리고 21개의 점들은 ·"1", "2", "3", "4", "5", "6" 면의 점들의 순서대로 배열에 담는다. 즉 "1" 면의 점은 dotsC[0] 에, "2" 면의 점은 dotsC[1], dotsC[2] 에, "3" 면의 점은 dotsC[3], dotsC[4], dotsC[5]에 담는다. 나머지도 마찬가지이다.  줄4~6은 점의 크기를 주사위의 크기에 비례하여 키운다. 줄7~11은 이제 이 주사위의 점을 그리기 위한 텍스쳐 데이터를 만드는 코드다.


 

그림 3) 주사위의 한점을 그리는데 필요한 8개의 데이터 점.

 

 

          점(근사원) 하나마다  8개의 3차원점을 사용하기로 한다. <그림3>의 가운데 점이 주사위의 점의 위치를 나타내고 정4각형의 변과 꼭지점에 놓인 8개의 점이 근사원을 그리는데 쓰인다. 원 위의 점을 양끝으로 하고 꼭지점점을 조절점(control point)으로 해서 curveTo를 사용한 곡선 4개로 근사원을 그린다.




1

dotsC =  new Array([0, 0, -1], [1, 0.4, -0.4], [1, -0.4, 0.4], [-0.5, -1, -0.5], [0, -1, 0], [0.5, -1, 0.5], [-0.5, 1, -0.5], [0.5, 1, -0.5], [0.5, 1, 0.5], [-0.5, 1, 0.5], [-1, 0, 0], [-1, 0.5, -0.5], [-1, -0.5, -0.5], [-1, -0.5, 0.5], [-1, 0.5, 0.5], [-0.5, -0.5, 1], [0, -0.5, 1], [0.5, -0.5, 1], [0.5, 0.5, 1], [0, 0.5, 1], [-0.5, 0.5, 1]);

2

for (var i=0; i<21; i++) {

3

 for (var j=0; j<3; j++) {

4

dotsC[i][j] = dotsC[i][j]*sizeCube/2;

5

}

6

}

7

dotData = new Array( 21);     

8

for (var i= 0; i<21; i++)

9

dotData[i] = new Array[[0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0]];

10

for (var i= 0; i<21; i++)

11

dotData[i] = calDotData(dotsC[i]);

표3 주사위의 점들을 그리기 위한 코드



 

          <표3>의 줄9에 정의된  8요소 배열에 이 8개의 3차원 점들을 담는다. 그리고 모두 21개의 점에 대해서 이 데이터점을 생성한다. (<표3> 줄10, 11 참조)  이것을 손으로 일일이 코딩하는 것은 힘들 뿐 아니라 자칫 에러를 유발할 수 있으므로 이것을 피하기 위하여 아래의 코드와 같은 생성함수를 만들어 <표3> 줄11에서 시행한다. 

           아래의 <표4>에 올려 놓은 CalDotData 함수는 인수로 받은 근사원의 중심이 어느 평면에 놓여 있나를 판별한 다음 이 평면에 근사원 중심점 주변에 8개의 점을 생성하여 반환하는 함수다.  

 


1

private function calDotData(pt3D1:Array):Array {

2

var pt3DA = [[0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0]];

3

if (Math.abs (pt3D1[0])==(sizeCube/2))

4

{pt3DA[0] =[pt3D1[0], pt3D1[1] + sizeDot,  pt3D1[2]];

5

pt3DA[1] = [pt3D1[0], pt3D1[1] + sizeDot,  pt3D1[2]+sizeDot];

6

pt3DA[2] = [pt3D1[0], pt3D1[1],  pt3D1[2]+sizeDot];

7

pt3DA[3] = [pt3D1[0], pt3D1[1] -sizeDot,  pt3D1[2]+sizeDot];

8

pt3DA[4] = [pt3D1[0], pt3D1[1] -sizeDot,  pt3D1[2]];

9

pt3DA[5] = [pt3D1[0], pt3D1[1] -sizeDot,  pt3D1[2]-sizeDot];

10

pt3DA[6] = [pt3D1[0], pt3D1[1],  pt3D1[2]-sizeDot];

11

pt3DA[7] = [pt3D1[0], pt3D1[1] +sizeDot,  pt3D1[2]-sizeDot];

12

        }

13

else

14

{if( Math.abs (pt3D1[1])==(sizeCube/2))

15

{pt3DA[0] = [pt3D1[0] + sizeDot,  pt3D1[1],  pt3D1[2]];

16

pt3DA[1] = [pt3D1[0] + sizeDot,  pt3D1[1],  pt3D1[2]+ sizeDot];

17

pt3DA[2] = [pt3D1[0],  pt3D1[1], pt3D1[2]+sizeDot];

18

pt3DA[3] = [pt3D1[0] -sizeDot,  pt3D1[1], pt3D1[2]+sizeDot];

19

pt3DA[4] = [pt3D1[0] -sizeDot,  pt3D1[1], pt3D1[2]];

20

pt3DA[5] = [pt3D1[0] -sizeDot,  pt3D1[1], pt3D1[2]-sizeDot];

21

pt3DA[6] = [pt3D1[0],  pt3D1[1], pt3D1[2]-sizeDot];

22

pt3DA[7] = [pt3D1[0] +sizeDot,  pt3D1[1], pt3D1[2]-sizeDot];

23

        }

24

          else

25

{pt3DA[0] =[pt3D1[0],  pt3D1[1] + sizeDot,  pt3D1[2]];

26

pt3DA[1] = [pt3D1[0]+sizeDot , pt3D1[1] + sizeDot,  pt3D1[2]];

27

pt3DA[2] = [pt3D1[0]+sizeDot , pt3D1[1],  pt3D1[2]];

28

pt3DA[3] = [pt3D1[0]+sizeDot , pt3D1[1] - sizeDot,  pt3D1[2]];

29

pt3DA[4] = [pt3D1[0],  pt3D1[1] - sizeDot,  pt3D1[2]];

30

pt3DA[5] = [pt3D1[0]-sizeDot , pt3D1[1] - sizeDot,  pt3D1[2]];

31

pt3DA[6] = [pt3D1[0]-sizeDot , pt3D1[1],  pt3D1[2]];

32

pt3DA[7] = [pt3D1[0]-sizeDot , pt3D1[1] + sizeDot,  pt3D1[2]];

33

         }

34

        }

35

        return pt3DA;

36

}

표4 calDotData 함수의 코드.  점의 위치를 인수로 받아서 그 주변의 8개의
점을 생성하여 반환한다.(12)

 

 

          이 마디는 너무 길어지므로 다음 강좌에서 이어서 설명한다. 

Posted by 샛솔 :