ㅎㅎ 다시 Cocos2d의 강좌가 시작되었습니다. 

열심히 쓰기는 했는데 다들 어떻게 받아들이고 있는지 모르겠네요. 흠흠.. 피드백이 없으니 별 도움이 안되었는지 아님 글만 보고 만 것인지.. 암튼 이번주에는 Cocos2d를 사용해서 메뉴를 만드는 법을 배워보도록 합시다.

이번에도 설명글이 좀 많으니까 주의깊게 읽어야 할 것입니다. offline모임에 나오면 실제로 어떻게 구현하는지 볼 수 있을 것입니다. 


1 메뉴 만들기 

1.1 기본 메뉴

Cocos2d에서 메뉴 관련 클래스는 Menu, MenuItem, MenuItemFont, MenuItemAtlasFont, MenuItemFont, MenuItemImage, MenuItemToggle이 있다. 이 중에서 기본적으로 사용하는 것은 Menu, MenuItem, MenuItemFont 정도이고, 다음에 설명할 이미지를 사용한 메뉴를 만들 때에는  MenuItemImage 클래스를 사용한다.

기본 메뉴는 일반적인 글씨를 사용해서 메뉴를 생성하는 것이다. 글씨를 사용하기 때문에 어떤 폰트의 어떤 크기를 사용할지를 미리 설정해야 한다. 그러기 위해서 다음과 같이 MenuItemFont를 설정해 둔다. 

    [MenuItemFont setFontSize:30];
    [MenuItemFont setFontName: @"Courier New"];

여기서 setFontSize와 setFontName은 static변수이기 때문에 한번 설정해 놓으면 다시 변경하기 전까지 계속 같은 값을 참조할 수 있다. 
이제 이 폰트를 사용하는 메뉴는 다음과 같이 생성한다.

    MenuItem *item1 = [MenuItemFont itemFromString: @"Start" target:self selector:@selector(menuCallback:)];

itemFromString 에 표시할 메뉴의 내용을 target에는 메뉴가 click되었을 경우에 호출된 메소드를 가지고 있는 클래스의 인스턴스를, selector에는 메뉴가 click되었을 경우에 호출된 메소드의 주소를 입력해 준다. 메소드의 주소는 @selector를 통해서 가져온다.

이렇게 만들어진 MenuItem *item1을 Menu에 추가하고 다시 이 Menu를 화면에 표시할  Layer에 추가해야 메뉴를 화면에 출력할 수 있다. 

    Menu *menu = [Menu menuWithItems: item1, nil];

    [someLayer addChild: menu];

메뉴는 위와 같이 menuWithItems를 사용해서 각각 생성된 MenuItem의 하나 하나 넣어주고 마지막을 nil로 채워주면 해당 값들이 Array형태로 차례로 입력되어져서 화면 출력에 사용된다. 간단하지 않은가? 그런데 여기서 끝나는 것이 아니라 이제 Start라는 메뉴를 만들었는데 이 메뉴가 click 되었을 경우에 호출될 메소드를 구현해 주어야 한다. 위에서 menuCallback 을 호출하도록 했으니까. 다음과 같이 menuCallback 메소드를 만들어 준다.

-(void) menuCallback: (id) sender
{
    NSLog(@"called start menu");
}


1.2 이미지 메뉴 만들기

이미지 메뉴는 MenuItemImage를 사용해서 정의할 수 있다. 이미지 메뉴를 사용하기 위해서는 일반 이미지와 메뉴가 선택되어져서 눌려졌을 경우의 이미지가 있으면 된다. 추가적으로 비활성화 되었을 경우에 보여질 이미지도 추가할 수 있다. 

    MenuItemImage *item1 = [MenuItemImage itemFromNormalImage:@"normalImage.png" selectedImage:@"selectedImage.png" target:self selector:@selector(menuCallbackBack:)];

normalImage.png는 일반적으로 보여질 이미지를 selectedImage.png는 click되었을 경우에 보여질 이미지를 입력해 준다. target은 기본 메뉴에서와 동일하게 click되었을 경우에 호출될 메소드가 있는 클래스의 인스턴스를 selector를 호출될 메소드명을 입력한다. 이 메뉴도 일반 메뉴와 동일하게 [Menu menuWithItems:]를 통해서 입력한다.

1.3. 토글 메뉴

토 글 메뉴는 MenuItemToggle 클래스를 사용한다. MenuItemToggle클래스는 하나의 컨테이너로 기본 MenuItem 를 배열로 입력받아서 매번 click시마다 순서대로 다음 메뉴를 표시하게 해준다. 예를 들어서  "ON", "OFF"를 각각 표시하는 메뉴일 때 다음과 같이 사용하면 된다. 

    [MenuItemToggle itemWithTarget:self selector:@selector(menuCallback:) items:
        [MenuItemFont itemFromString:@"ON"],
        [MenuItemFont itemFromString:@"OFF"], nil];

그 리고 여기서 callback으로 호출되는 -(void)menuCallback:(id)sender 에서 sender는 현재 연결된 MenuItemToggle이 입력된다. 그리고 [sender selectedItem]를 호출하면 현재 click을 통해서 변경된 MenuItemFont가 반환된다. 그러니까 다시 말을 하자면, ON일 때 click을 하면 OFF로 변경이 되는데 이 때 [sender selectedItem]으로는 [MenuItemFont itemFromString:@"OFF"] 로 생성된 클래스가 반환된다. ^^ 설명이 잘되었는지 모르겠다.


2. 설정하기

등록된 MenuItem들은 위지를 조절할 수 있고, 수직, 수평정렬을 할 수 있고 메뉴의 행과 열의 개수도 다음과 같은 메소드들을 사용해서 설정할 수 있다.


2.1 출력 위치 조절

메뉴는 기본적으로 스크린의 가운데를 기점으로 상하 또는 좌우로 동일한 간격으로 메뉴들이 위치하게 된다. 그러므로 메뉴를 특정한 위치에 시키고 싶다면 Menu의 setPosition의 메소드를 사용하거나 position을 직접 제어해서 Menu의 중앙의 값을 설정하면 된다. 
만약 각각의 MenuItem들을 각기 다른 위치에 위치시키고 싶다면 Menu에 MenuItem을 하나만 입력하고 각각의 위치를 설정하면 된다. 

다음 이미지는 메뉴를 구성할 때 간격과 위치에 대한 설명이미지이다.

menutest_small.png 



2.2 수직 정렬

수직 정렬은 alignItemsVertically으로 postion값을 중심으로 KDefaultPadding(5) 간격으로 나열된다. 
각각의 간격을 다른 값으로 하려면 alignItemsVerticallyWithPadding:(float) padding 메소드로 간격값을 실수로 입력하면 된다.

그림 3.png 


2.3 수평 정렬

수평 정렬은 alignItemsHorizontally 메소드로 수직정렬과 마찬가지로 position을 중심으로 kDefaultPadding간격으로 나열된다. 
이 역시 각 간격은 alignItemsHorizontallyWithPadding: (float) padding 메소드로 간격값을 변경시킬 수 있다.

그림 1.png 


2.4 Column 개수 설정

만약에 다음 그림과 같이 일률적으로 정렬만 되는 메뉴를 행마다 다른 수로 정렬시키고 싶다면 alignItemsInColumns: (NSNumber *) columns, ... 메소드를 사용해서 각 행마다 개수를 다음과 같이 입력한다.

    [menu alignItemsInColumns:
     [NSNumber numberWithUnsignedInt:2],  // Sound, Music
     [NSNumber numberWithUnsignedInt:2],  // On, On
     [NSNumber numberWithUnsignedInt:2],  // Quality, Volume
     [NSNumber numberWithUnsignedInt:2],  // High, 66%
     [NSNumber numberWithUnsignedInt:1],  // Go Back
     nil
    ]; // 2 + 2 + 2 + 2 + 1 = total count of 9

그림 4.png 


2.5 Row 개수 설정

Row도 Column과 동일한 방법으로 설정할 수 있다. 이 부분은 각자 연구해 보기 바란다.



3. 응용하기


3.1 이미지 메뉴 크기 변경

이미지 메뉴의 크기는 CocosNode에 정의된 속성인 scaleX와 scaleY를 이용하면 된다. 각 값은 실수값으로 1.0f는 실제 배율의 크기이고 이 값보다 작을 경우에는 작게 클 경우에는 큰 이미지로 변경된다. 

     MenuItemImage *item1 = ...;
    item1.scaleX = 1.5f
    item1.scaleY = 0.5f

scale1.png 기본이미지

scale 2.png scaleX = 0.5f

scale 3.png scaleY = 0.5f

scale 4.png scaleX = 1.5f

scale 5.png scaleY = 1.5f


3.2 메뉴 비활성화 시키기

메뉴를 비활성화 시키기 위해서는 isEnabled를 NO로 설정하면된다. 

    MenuItem *item1 == ...;
    item1.isEnabled = NO;


메뉴를 비활성화 시키면 다음 그림과 같이 선택도 안되지만 Opacity값이 줄어든채 색으로 설정된다.

그림 5.png (비활성화 된 경우)

그림 6.png (활성화 된 경우)


3.3 종료 메뉴 만들기

메뉴가 선택되었을 때 다음을 호출하면 어플이 종료된다. 그런데 이 명령은 iPhone 2.2.1이하에서만 동작한다. 3.0에서는 동작하지 않는데 아는 분은 답글 부탁한다.

    [[Director sharedDirector] end];
    if( [[UIApplication sharedApplication] respondsToSelector:@selector(terminate)] )
        [[UIApplication sharedApplication] performSelector:@selector(terminate)];




Cocos2d에서 제공하는 테스트용 소스를 좀 편집해서 강좌에서 설명한 내용을 다음과 같이 만들어봤다. 소스(MenuTest.zip)를 보면 좀더 이해하기 쉬울 것이다. 

그림 10.png 

<메인 메뉴>


그림 8.png 

<MenuItemImage 클래스로 구현>


그림 9.png 

<MenuItemToggle 클래스로 구현>



다음에는 Sprite의 사용법에 대해서 알아보겠습니다. 애니메이션 구현이 주를 이룰 것 같은데 두번의 강좌를 통해서 Cocos2d의 애니메이션 구현 방법을 알아보겠습니다. 다음 강좌도 기대하시라...^^a