'프로그래밍'에 해당되는 글 85건

  1. 2009.11.06 Cocos2d 애니메이션 구현하기 1
  2. 2009.11.06 메뉴 만들기
  3. 2009.11.06 Cocos2d의 구성 1
  4. 2009.11.06 cocos2d 개발환경 설정
  5. 2009.04.11 즐겁게 개발하자, 오브젝티브-C
프로그래밍/Cocos2D2009. 11. 6. 16:44
2009.07.26 21:08:06(*.112.98.77)
조회수:2046

 저번 주에 한주 강좌를 올리지 않았더니 이번주에 만드는 데 좀 힘들었습니다. ^^; 한주 쉬었더니 역쉬 다시 시작하기 힘들더군요.

그래도 강좌를 기다리신다는 쪽지를 보내신 분도 있고 제 글을 읽어주신다니 뿌듯할 뿐입니다. 


이번에는 기본적인 애니메이션을 구현하는 방법에 대해서 알아보겠습니다. 원래 움직이는 것이라 이미지를 많이 넣어야 하는데 이미지를 화나하나 캡춰하는 것이 좀 힘들더군요. 그래도 소스를 첨부했으니 해당 소스를 보시고 하나씩 따라하시면 쉽게 이해하실 수 있을 겁니다. 


자 그럼 본격적으로 강좌를 시작하겠습니다. 



Cocos2d로 애니메이션 구현하기 1


Cocos2d에서 애니메이션은 Sprite와 Action을 사용해서 구현할 수 있다. 내가 Cocos2d로 제작을 결정하게 했던 가장 큰 이유가 애니메이션을 쉽게 구현할 수 있다는 것이었다. Cocoa로 애니메이션을 구현하려면 여러가지 설정을 많이 해줘야 한다. 물론 랩핑을 해서 간단하게 만들 수 있지만 필요할 때마다 매번 만들어 줘야 한다는 것은 여간 불편한 것이 아니다. 그런데 Cocos2d는 이러한 것들을 단순히 Action을 상속한 클래스들을 사용해서 해당 애니메니션의 모션을 정의해 준 다음에 runAction을 호출해주면 애니메이션이 간단하게 구현된다. 뭔가 짜릿함을 느끼는 순간이다. (나만 그런가, 해킨토시에서 처음으로 Hello의 동영상을 음성과 함께 볼 때의 그 희열과 짜릿함과 같은 느낌이었다. ㅋㅋㅋ)


Cocos2d에서는 애니메이션을 편의상 다음과 같이 4개의 Action군으로 분류를 했다. 

  1. Transformation Actions
  2. Composable Actions
  3. Ease Actions
  4. Mics Actions

어짜피 편의상 분류를 했기 때문에 이해할 때만 이런식으로 구분했구나를 이해하고 사용할 때는 아무렇게나 마구 써도 상관없다. 어짜피 모두다 Action 클래스를 상속했기 때문에 모두다 결합해서 사용할 수 있기 때문이다.

애니메이션 구현은 위에서도 말했듯이 정말 간단한데 먼저 action 인스턴스를 생성한다. 

    id actionTo = [MoveTo actionWithDuration:2.0f position:ccp(s.width - 40, s.height - 40)];

여 기서는 MoveTo를 예시로 했는데 다른 것들도 비슷하게 생성할 수 있다. actionWithDuration은 애니메이션을 동작 시간을 초단위로 입력한다. 위 예제는 해당 위치까지 2초 동안 ccp(s.width - 40, s.height - 40)로 이동하라고 정의하는 것이다. 

그런다음에 이런 action 인스턴스를 실행시키려면 움직이려고 하는 Sprite의 runAction을 호출해준다.

    [someSprite runAction:actionTo];

이렇게 입력되는 순간부터 someSprite는 움직이기 시작해서 2초의 시간을 두고 해당 위치로 이동을 한다. 
전 에 내가 Java로 모바일 게임을 제작할 때 애니메이션을 구현할 때에는 매 프레임시간마다 해당 위치를 계산해서 매번 그려줬어야 하는데 Cocos2d는 그럴 필요없이 간단히 위와 같이 해주면 중간 계산도 알아서 해서 화면에 출력해 준다. 정말 대단하지 않은가...... ㅋㅋ

그리고 애니메이션은 Sprite에 줄 수 있기 때문에 여러개의 Sprite가 동시간에 각각 다른 움직임을 처리할 수 있다. 
이렇게 구현할 수 있는 애니메이션의 종류가 이동시키기, 회전하기 등등 여러개가 있는데 그것들을 몽땅 하나씩 살펴보도록 하자.

1. Transformation Action

Transformation Action은 Sprite의 이동, 회전 등 일회성의 움직임이나 효과를 분류해 놓은 것들이다. 

1.1 Move


Sprite를 이동시키는 Action으로 MoveTo와 MoveBy가 있다. 그리고 다음과 같이 사용할 수 있다. 

    id actionTo = [MoveTo actionWithDuration: 2 position:ccp(200, 200)];

    id actionBy = [MoveBy actionWithDuration:2  position: ccp(80,80)];

MoveTo 는 해당 위치로 이동을 하는 것이고 MoveBy는 현재 위치에서 position으로 지정한 좌표만큼을 이동한다. 위의 예제에서 보자면  actionTo는 Sprite가 어디에 위치해 있든지 200, 200의 위치로 이동을 한다. 그렇지만 actionBy는 sprite가 현재 있는 위치에서 80, 80을 더한 위치로 이동한다.


1.2 Rotate

Sprite에서 회전을 시키는 Action으로는 RotateTo와 RotateBy가 있는데, Move에서는 position을 설정해 주었는데 Rotate에서는 회전할 각도를 다음과 같이 설정해 주어야 한다. 

    id actionTo = [RotateTo actionWithDuration: 2 angle:45];
    id actionBy = [RotateBy actionWithDuration:2  angle: 180];

RotateTo 는 어떤 각도에 있든 angle로 설정한 각도록 회전한다. 하지만 RotateBy는 angle각 만큼을 회전을 한다. 위의 예제로 예를 들자면 actionTo는 어떤한 각도에 있더라도 기울기가 45도로 회전하지만, actionBy는 어떠한 각도에 있어도 현재 각도에서 360도를 회전한다. 그래서 actionTo는 애니메이션이 끝나면 항상 45도이지만 actionBy는 만약 0도에서 시작하면 180도에서 끝나고 45도에서 시작했으면 225도에서 애니메이션이 끝난다.

이제 To와 By의 차이점을 어느 정도 알 수 있지 않을까? To는 절대적인 위치나 각도 등에 절대적인 값을 설정한든데, By는 현재 값에 추가되는 상대적인 값을 설정하는 것이다.


1.3 Scale

Sprite 의 크기를 변경하는 Action으로는 ScaleTo와 ScaleBy가 있고, ScaleTo는 scale로 설정한 배율로 크기를 변경한다. 1보다 작으면 작게 1보다 크면 해당 배수만큼 scale값으로는 1.0f와 같은 실수값을 설정한다.

    id actionTo = [ScaleTo actionWithDuration:2 scale:0.5f];
    id actionBy = [ScaleBy actionWithDuration:2  scale: 2];

위의 예제에서 actionTo는 0.5배의 크기로 Sprite를 2초동안 변환시키고, actionBy는 2초동안 현재 크기의 2배로 크기를 변경시킨다.


1.4 Jump

Cocos2d 에서는 애니메이션에서 특이하게 Jump도 설정할 수 있는데, JumpTo와 JumpBy로 Jump를 설정할 수 있는데 To와 By의 차이점은 각각 position에 대한 값의 절대적 상대적 차이로 설정된다. 즉, JumpTo는 절대적으로 설정한 position 위치로 Jump를 하면서 이동을 하는데 JumpBy는 현재 위치에서 position으로 설정된 값만큼을 더한 위치로 Jump하면서 이동한다.

    id actionTo = [JumpTo actionWithDuration:2 position:ccp(300,300) height:50 jumps:4];
    id actionBy = [JumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4];

위 의 예제에서는 actionTo는 2초동안 300, 300의 위치로 높이는 50픽셀로 4번의 점프를 하면서 이동을 한다. actionBy는 2초동안 현재 위치에서 가로로만 300픽셀을 더한 위치로 높이는 50픽셀로 4번 점프를 하면서 이동을 한다.


1.5 Bezier(곡선)


Sprite 가 이동할 경로를 곡선을 하고 싶을 때에는 BezierBy actionWithDuration:bezier:를 사용한다. Bezier 를 설정할 때에는 이동할 경로를 잘 계산을 해야 한다. 다음의 그림을 보면서 확인해 보자.


Cocos2d 곡선 애니1.png 


위의 그림에서와 같이 파란색으로 된 부분을 point로 start, controlPoint_1, controlPoint_2, end의 네군데를 point로 설정하면 빨간색으로 된 곡선을 이동 경로로 Sprite가 이동하게 된다. 
그 런데 여기서 Sprite가 위치한 현재 위치에서 설정한 상대적인 위치로 이동한다, 즉 다시 말해서 0,0에서 시작한다면 위의 그림의 빨간색 경로로 이동을 하지만 100,100에서 시작을 한다면 다음과 같이 중간의 경로는 변경없이 시작위치만 변경된 경로로 이동을 하게된다.


Cocos2d 곡선 애니2.png 


bezier의 파라미터로는 ccBezierConfig의 값을 설정해서 입력한다.

    ccBezierConfig bezier;
    bezier.startPosition = ccp(0,0);
    bezier.controlPoint_1 = ccp(0, s.height/2);
    bezier.controlPoint_2 = ccp(300, -s.height/2);
    bezier.endPosition = ccp(300,100);

    id bezierForward = [BezierBy actionWithDuration:3 bezier:bezier];

아무래도  Bezier를 사용할 때에는 확실히 계산을 하고 사용해야할 것 같다. ^^


1.6 Blink

Blink는 Sprite를 깜박이게 만든다. Blink actionWithDuration:blinks: 메소드를 사용하는데 여기서 blinks로 actionWithDuration에 설정한 시간만큰 깜박일 횟수를 설정한다.

    id action1 = [Blink actionWithDuration:2 blinks:10];

위 예제는 2초동안 10회 깜박이게 하는 Action의 예제이다.


1.7 Fade

Sprite 를 점점 나타나게도 점점 사라지게도 할 수 있는데 이렇게 할 수 있도록 하는 Action이 FadeIn과 FadeOut이다. FadeIn과 FadeOut은 간단하게 나타나거나 사라질 시간을 설정을 하면 된다. (FadeIn/Out은 Opacity를 사용하는 것이기 때문에  나타나게 하려면 먼저 Opacity값을 0으로 해서 먼저 안보이게 해야한다.)

    id action1 = [FadeIn actionWithDuration:1.0f];
    id action2 = [FadeOut actionWithDuration:1.0f];

위의 예제에서 action1은 1초 동안 점점 나타나게, action2는 1초 동안 점점 사라지게 한다.


1.8 Tint

Sprite 의 농도를 마음대로 변경도 할 수 있다. 이는 TintTo actionWithDuration:red:green:blue: 와 TintBy actionWithDuration:red:green:blue:를 사용해서 수정할 수 있다. 앞에서도 많이 경험했다 싶이 이번에도 To와 By가 나와았는데 To는 절대적으로 rgb에 맞는 농도가 변경되고 By는 현재의 값에서 rgb가 추가되어 색상이 변경되어 진다. 

다음은 이를 사용한 예제이다. 

    id action1 = [TintTo actionWithDuration:2 red:255 green:0 blue:255];
    id action2 = [TintBy actionWithDuration:2 red:0 green:-127 blue:-127];


1.9 Animate

지금까지는 하나의 Sprite를 가지고 이동시키거나 크기를 변경시키거나 회전시키는 등의 애니메이션을 살펴봤는데 이번에는 여러개의 Sprite이미지들을 사용해서 각각을 연속적으로 보여주면서 하나의 애니메이션을 출력하는 것을 알아보려고 한다. 

이를 위해서는  기존과 다른 방식을 사용해야하는데 우선 Animation 인자를 먼저 생성하고,

    Animation* animation = [Animation animationWithName:@"dance" delay:0.2f];

 여기에 이 애니메이션에서 출력할 이미지들의 파일이름으로 로드해서 적재하게 한다.

    for( int i=1;i<15;i++)
        [animation addFrameWithFilename: [NSString stringWithFormat:@"dance_%02d.png", i]];

그런 다음에 이 애니메이션을 Action으로 생성한다.

    id action = [Animate actionWithAnimation: animation];

이를 다음과 같이 실행하면 된다. 

    [someSprite runAction:action];


지금까지 아주 기본적인 애니메이션을 구현하는 법을 공부해 봤다. 
이것만 가지고도 같단한 애니메이션을 구현할 수 있는데 좀 더 복잡한 Sprite가 커지면서 이동하는 등의 애니메이션을 구현을 하려면 몇가지만 더 알면 된다. 이것은 다음에 알아보려고 한다. 


위에서 설명한 내용들을 Cocos2d의 SpriateTest를 가지고 강좌에 맞도록 수정한 소스를 첨부합니다. 

공부하시면서 보면 쉽게 이해하실 수 있으실 것입니다. SpriteTest.zip 

'프로그래밍 > Cocos2D' 카테고리의 다른 글

Cocos2d 애니메이션 구현하기 3  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 2  (0) 2009.11.06
메뉴 만들기  (0) 2009.11.06
Cocos2d의 구성  (1) 2009.11.06
cocos2d 개발환경 설정  (0) 2009.11.06
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 16:43
zemyblue
2009.07.12 21:03:02(*.112.98.77)
조회수:1717

 ㅎㅎ 다시 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

'프로그래밍 > Cocos2D' 카테고리의 다른 글

Cocos2d 애니메이션 구현하기 3  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 2  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 1  (0) 2009.11.06
Cocos2d의 구성  (1) 2009.11.06
cocos2d 개발환경 설정  (0) 2009.11.06
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 16:43
2009.07.04 12:09:13(*.112.98.77)
조회수:2242

 이번에는 Cocos2d가 어떻게 구성이 되어 있는지를 알아보려한다. 그런데 이번 강좌에서는 설명이 많아서 그림이 별로 없어서 지루해 하지 않을까 걱정이 앞선다. 

(글을 쓰면서 느낀건데 처음에는 경어체로 쓰다가 나중에 가서는 점점 구어체를 쓰게 되어서 그냥 내가 쓰기 편하게 구어체로 통일을 하기로 했다. 나중에 편집을 하면서 경어로 변경하는 작업을 하기가 귀찮아 져서다. ^^;;;;, 싸가지 없는 놈이라고 해도 겸허하게 받아들이겠다. )


1. Cocos2d의 특징


1.1 cocos2d는 기본적으로 cocoa frame의 UIView 기반위에서 작동하는 거라서 하나의  UIView와 UIViewController를 AppDelegate에 연결하고 사용하면 된다. 이는 다른 app과는 다른 구조를 가지고 있어서 약간 혼란스러울 수도 있다. 하지만 하나의 UIView만 사용한다는 것만 기억하고 개발을 한다면 문제없을 것이다. 

1.2 cocos2d는 OpenGL을 사용하므로 UIView와 다른 좌표계를 가지고 있다. UIView는 좌측 상단이 (0,0)이어서 오른쪽으로 가면서 x값이 증가하고 아래로 가면서 y값이 증가한다. 하지만 cocos2d는 좌측 하단이 (0,0)이므로 위로 갈수록 y값이 증가한다.

1.3 화면의 개체를 표시하거나 이동시킬 때 position이 일반적인 경우와 같은 왼쪽위가 아니라 중심이기 때문에 주의를 기울여서 계산을 해야한다.


2. Cocos2d의 구조

cocos2d는 CocosNode를 기준으로 Director, Scene, Layer, Sprite의 계층적인 포함관계를 가지고 Action 클래스를 이용해서 애니메이션을 구현한다. 


Director > Scene > Layer > Sprite 

2.1 CocosNode

cocos2d의 모든 클래스는 CocosNode를 최상위 클래스로하고 상속한다. cocos의 NSObject와 같이 클래스간의 최상위 부모로 사용되는 것이다. 이 클래스는 position, scale, visible과 같은 속성들을 가지고 각 오브젝트간의 계층을 구성할 수 있는 add, remove, reorder를 가지고 있다. 또한 하나의 오브젝트가 각기 자체의 schedule을 가지고 있어 개별적으로 작동시킬 수 있다. 

2.2 Director

Cocos2d 에서 신과 같은 존재로 Cocos2d의 세계를 관리하고 관장하는 클래스가 Director이다. 신과 같은 역활을 하지만 Cocos2d가 하나의 영상물을 만드는 것과 비슷하므로 Director라고 이름을 붙였을 것 같다. 여하튼 Director는 말 그대로 영화감독처럼 게임의 흐름을 Scene을 통해서 관리한다. 그리고 Singleton Pattern으로 오로지 하나의 유일한 Director객체를 [Director sharedDirector]로 호출해서 실행할 수 있다.

2.3 Scene

Scene 은 Director의 명령을 받아서 하나의 화면을 구성하게 한다. 게임에서 하나의 배경을 맡는다고 생각하면 될 듯 싶다. 각각의 다른 배경이 필요하다면 서로 다른 Scene을 구성해서 장면을 전환하면 된다. 처음 장면의 시작은 [[Director sharedDirector] runWithScene:scene]; 을 호출해서 실행하고 다음 장면으로 변환은 [[Director sharedDirector] pushScene:nextScene]; 으로 화면을 전환시킬 수 있다. 그런데 메소드명을 자세히 보면 알 수 있듯이 각 Scene의 오브젝트들은 queue로 관리되어 진다. 이전 Scene으로 다시 이동을 하고 싶으면 현재의 Scene을 정리하고 popScene을 호출하면 된다.

2.4 Layer

Layer는 Scene에 포함되는 세부 소품이나 세트라고 볼 수 있다. 두 클래스의 역활을 비슷하지만 Layer와 Scene의 차이점은 사용자의 이벤트를 입력받을 수 있느냐 없느냐의 차이라고 볼 수 있다. Scene은 터치 이벤트를 수신할 수 없지만 Layer는 TouchEventsDelegate 프로토콜을 구현함으로써 터치 이벤트를 수신할 수 있다. 

2.5 Sprite

Sprite 는 영화의 최하위 단에서 실제로 움직이고 작동하는 배우나 물체의 단위이다. 대부분의 화면에서 움직이는 것은 Sprite라고 보면 이해하기 쉬울 것이다. Sprite는 보통 이미지이기 때문에 이미지로 생성되는 부분에서 특화되어 있다. 가장쉽게 생성하는 방법도 initWithFile로 이미지 파일을 resource에서 직접 로드하게 하는 방법이다. 보통 png 이미지를 사용한다. 또한 PowerVR 3D 가속칩에 최적화된 PVRTC 이미지 파일도 사용할 수 있다. 

2.6 Action

Action 은 보통 CocosNode에 움직임을 주게하는 클래스이지만 주로 Sprite를 움직이게 하는데 사용한다. Action은 한번만 실행되는 InstanceAction, 지속적으로 일정시간동안 반복되는 IntervalAction, 그리고 일정시간동안 일정한 패턴을 반복하는 RepeatAction으로 구성된다. 다음은 각각 Action의 종류들이다. 차차 이것들이 어떻게 작동하는지를 배워볼 것이다. 

  • Trasformation Actions : Move, Rotate, Scale, Jump, etc.
  • Composable Actions : Sequence, Spawn, Repeat, Revers.
  • Ease Actions : Exp, Sin, Cubic, etc.
  • Mics Actions : CallFunc, OrbitCamera


3. Hello World!

지금까지 Cocos2d의 기본을 클래스들을 알아보았다. 그러면 이것들을 가지고 프로그래밍의 입문인 "Hello World of Cocos2d."를 화면에 출력해 보자.


첫 강의에서 만든 cocos2d 를 선택해서 HelloCocos2d 프로젝트를 생성해 보자.


Cocos2dAppDelegate.m에서 다음의 내용을 추가해 보자.


----------------------------------------------------------------


#import "Cocos2dAppDelegate.h"
#import "cocos2d.h"

@implementation Cocos2dAppDelegate

@synthesize window;


- (void)applicationDidFinishLaunching:(UIApplication *)application { 
    // UIWidow 개체를 생성
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    // Director에서 사용할 View등록
    [[Director sharedDirector] attachInView:window];
    
    // HelloScene 생성
    Scene *helloScene = [Scene node];
    Label *helloLabel = [Label labelWithString:@"Hello World of Cocos2d."
                                      fontName:@"Helvetica" fontSize:20.0f];
    helloLabel.position = ccp(150, 200);    // 위치 지정(가운데를 글자의 가운데 부분을 위치시켜야함)
    [helloScene addChild:helloLabel];
    
    // HelloScene을 실행
    [[Director sharedDirector] runWithScene:helloScene];
    
    // Override point for customization after application launch
    [window makeKeyAndVisible];
}


- (void)dealloc {
    [window release];
    [super dealloc];
}


@end

----------------------------------------------------------------

(Zeroboard에서 소스답게 보이는 법을 모르겠어요.ㅜㅜ)

bold체로 굵게 한 부분이 추가된 부분이다.



이제 build and Go를 실행해 보자.
화면에 다음과 같이 실행되면 제대로 된 것이다. 잘 실행이 안된다면 warning 경고를 무시하지 말고 잘 풀어본다. Objective-C는 동적인 객체 언어라서 warning 이라도 컴파일은 되지만 실행시에 문제가 발생할 수 도 있기 때문이다.


그림 2.png 

(이번강좌에서 처음으로 사용된 이미지이네요. ㅋㅋ)


cocos2d-iphone API는 여기서 찾아보자. 

다음은 메뉴를 생성하는 법을 알아보도록 하자. 기대하시라. ^^


참고 : 이 글을 마이크로소프트웨어 2009년 4월호의 이창신님의 글을 인용했습니다.

'프로그래밍 > Cocos2D' 카테고리의 다른 글

Cocos2d 애니메이션 구현하기 3  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 2  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 1  (0) 2009.11.06
메뉴 만들기  (0) 2009.11.06
cocos2d 개발환경 설정  (0) 2009.11.06
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 16:42
2009.06.28 12:51:39(*.76.17.65)
조회수:2341

cocos2d는 openGL을 사용하서 iPhone/IPot touch용 게임을 개발하기에 편리한 API이다. Menu, sprite, tilemap등 편리한 여러가지 기능을 제공하며, Chipmunk를 통해서 물리엔진까지 쉽게 사용할 수 있다. 자세한 내용과 최신 버젼은 cocos2d-iphone 사이트에서 확인할 수 있다.

(현재 만들 개발환경은 cocos2d 0.7.3버젼을 가지고 iPhone SDK 3.0에 최적화된 환경입니다.)


cocos2d-iphone은 소스파일을 직접 추가해서 개발을 할 수도 있지만 라이브러리를 링크하는 방식으로 개발할 수도 있다. 처음 개발을 할 때 라이브러리의 작동방식을 공부하기를 원한다면 소스를 직접 추가해서 하는 방식을 추천하지만 그렇지 않다면 라이브러리로 개발하는 것이 편리하다. 여기서는 라이브러리를 링크해서 개발 환경을 구축하는 것을 알아보겠다.


cocos2d-iphone 다운로드


1. 라이브러리 컴파일 하기

cocos2d-iphone에서 최신 버젼의 소스를 다운로드한다. iPhone OS 3.0 으로 변경되면서 많은 부분이 좀 까탈스러워졌다. 그러므로 3.0에서도 지원을 하게원한다면 0.7.3 이상의 버젼을 이용하는 편이 좋다. 다운로드한 소스를 압축을 풀면 Xcode의 프로젝트로 열수 있는 xcodeproj확장자를 실행한다.


Xcode SDK에서 좌측상단에 보면 개발 환경을 설정할 수 있는 Select box가 있다. 이걸 눌러서 위에서부터 다음과 같이 선택한다.


Active SDK : iPhone Device 2.2.1

Active Conficuration : Release

Active Target : cocos2d

이와 같이 선택한 다음에 Build(Command-B)를 한다. Build and Go가 아니다. 좀 기다리면 우측 하단에 진행되는 표시가 Succeed라고 표시될 것이다. 이렇게 되면 라이브러리가 컴파일된 것이다. 


같은 방법으로,


iPhone Device 2.2.1 - Release - Chipmunk

iPhone Simulator 2.2.1 - Release - cocos2d

iPhone Simulator 2.2.1 - Release - Chipmunk


이렇게 3개를 더 컴파일 해준다.  Chipmunk는 단순히 openGL의 간단한 라이브러리가 아닌 물리엔진 등을 위해 사용하는 라이브러리이다. 


2. 라이브러리 및 참조 파일들 복사

    1) 라이브리러 복사

라이브러리를 참조하기 위해서 라이브러리 디렉토리를 다음의 위치에 만든다. /Developer/Library/Cocos2d 

앞에서 컴파일한 라이브러리 파일들은 모두 Build 디렉토리에 존재하게 된다. 이 라이브러리 파일들을 Device와 Simulator에 따라서 다음과 같이 Release-iphoneos는 Device에 Release-iphonesimulator는 Simulator에 복사한다.

그림 15.png 

  →    

그림 16.png 


   

     2) 헤더파일 복사

압축을 푼 cocos2d-iphone디렉토리에 보면 cocos2d라는 디렉토리가 있는데, 여기에 cocos2d의 헤더파일들이 들어 있다. Support 디렉토리까지 통채로 /Developer/Library/Cocos2d/Include에 복사한다.

 그림 11.png 


    3)  fps_images.png 복사

이제 마지막으로 fps의 진행등을 숫자로 표시할 수 있는 이미지를 기본으로 추가해줘야 한다. Resouce하위에 fps_images.png라는 파일을 찾을 수 있을 것이다 이 파일을 /Developer/Library/Cocos2d/Images에 복사해준다.

그림 12.png 



3. Xcode 설정

이제 호출할 수 있는 라이브러리 준비를 마쳤고 실제로 Xcode 에서 개발할 수 있는 환경을 설정해보자. 먼저 Xcode의 새로운 프로젝트를 생성해서 Window-base application을 선택해서 Cocos2d라는 프로젝트명으로 프로젝트를 생성한다.

그림 27.png    


    1) Path 추가

cocos2d의 헤더와 라이브러리가 있는 디렉토리를 Path로 설정해줘야 참조를 할 수 있으므로 Groups & Files에서 제일 위에 있는 Cocos2d를 선택해서 마우스 오른쪽 키를 눌러 Get Info를 선택한다. 또는 SDK 상단의 Info 아이콘을 선택한다.

그림 28.png   그림 29.png

Project "Cocos2d" Info 다이얼로그에서 build탭을 선택한다. 하단을 스크롤하다보면 Search Path의 Header Search Paths와 Library Search paths를 찾을 수 있다.


여기서 Header Search Paths를 더블클릭해서 Cocos2d의 헤더파일이 있는 디렉토리(/Developer/Library/Cocos2d/Include)를 추가해 준다. 추가할 때 Support도 참조할 수 있도록 recursive를 체크한다.


그리고 Library Search Paths에도 라이브러리가 있는 디렉토리를 추가해 준다. 이 때에는 하위 디렉토리에서 참조할 라이브러리가 없으므로 recursive를 체크할 필요는 없다.

그림 20.png 

그림 18.png 


개발완료된 후에 배포를 할 때에는 Device로 컴파일을 해야 하므로, 위쪽의 Configuration을 눌러서 Release로 바꾼 다음에 Library Search Paths를 /Developer/Library/Cocos2d/Device로 한번 더 설정해 준다.

 

그림 30.png 


    2) Link 추가

Project "Cocos2d" Info 다이얼로그에서 스크롤을 하다보면 Linking의 Other Linker Flags를 볼 수 있는데 여기서 외부 참조할 라이브러리를 입력할 수 있다. 그런데 등록할 라이브러리는 -l옵션을 사용하고 라이브러리 명이 libcocos2d.a일 때 cocos2d만 사용해서 다음과 같이 입력한다. (참고 : objective-c는 기본적으로 gcc 컴파일러를 사용하는데 gcc 라이브러리들은 lib[라이브러리명].a로 구성이 되어 진다.) 


그리고 물리엔진도 필요에 따라서 다음과 같이 추가해 준다.

그림 17.png 


    3) 참조 이미지 추가

 그림 21.png

 /Developer/Library/Cocos2d/image에 복사한 참조 이미지를 프로젝트의 Resource Group에 추가해 준다. 드래그해서 Resources에 끌어다 놓으면 된다.



    4) 프레임워크 추가

cocos2d가 OpenGL을 참조하는 API이므로 iPhone SDK에서 제공하는 그림을 그리는 QuartzCore와 OpenGLES를 추가한다.


Group & Files->Target->Cocos2d를 마우스 오른쪽 키를 크릭하거나 상단메뉴에서 info아이콘을 크릭해서 Target "Cocos2d" Info 다이얼로그를 띄운다.

그림 24.png 


그림 25.png 


여기서  Linked Libraries 아래에 있는 +를 크릭해서 OpenGLES.framework와 QuartzCore.framework를 선택해서 추가한다.

그림 26.png 



    5) MainWindow.xib 설정변경

iPhone OS 3.0으로 변경되면서 전 버젼에서는 무시했던 것들을 세세하게 검사를 하기 때문에 2.2.1에서는 잘 실행이되지만 3.0에서는 화면이 하얗게만 표시되는 경우가 있다. 이는 2.2.1에서는 Interface Builder에서 연결을 설정했더라도 소스에서 재코딩을 하면 무시되었던 것이 우선순위가 interface Builder설정으로 변경된 것 같다.(이것은 어디까지나 나만의 생각이다. ^^;;) 


그러므로 Window-base Application으로 프로젝트를 생성하면 MainWindow.xib에서 기본 바탕화면이 하얀 Window가 App Delegate에 연결되어 있기 때문에 아무리 하단에 그림을 그리더라도 하얀 window가 덮어 씌워버리기 때문이다. 그러므로 이를 풀어줘야 한다. 


그러기 위해서 Resouces의 MainWindow.xib를 더블크릭해서 Interface Builder를 연다. 맨 끝에 Window 항목이 보이는데 (여기서 Window를 마우스 오른쪽 키로 크릭하면 다음과 같이 Delegete와 연결되어 있는 것을 확인할 수 있다), 이를 과감하게 Del 키를 눌러 지워준다. 그리고 저장한다.


그림 22.png  

→ 

그림 23.png  


이제 기본 설정을 완료했다. Build and Go를 해보면 시커먼 화면이 잘 뜰 것이다. ㅋㅋ
이제부터 여러분이 원하는 것을 개발하면 된다.



4. Xcode 템블릿 만들기

위와 같은 설정을 매번 Cocos2d를 사용하는 프로젝트를 할 때마다 설정을 해줘야 한다면 정말 귀찮을 것이다. 그래서 템플릿으로 설정을 해놓으면 Window-base Application을 선택해서 프로젝트를 생성할 수 있듯이 Cocos2d프로젝트를 생성할 수 있다. 


단순히 지금까지 생성한 Cocos2d의 디렉토리 전체("위치>windship"에 생성된 Cocos2d 폴더)를 /Library/Application Support/Developer/Shared/Xcode/Project Templates/Application 하위에 복사해 준다.

그러면 다음과 같이 새 프로젝트를 만들 때 Cocos2d를 선택할 수 있을 것이다. 

그림 31.png 


다음은 지금까지 설정된 내용을 압축한 파일입니다. 이를 그냥 위의 템플릿 디렉토리에 압축을 풀어도 됩니다. cocos2d.zip(cocos2d-iphone 0.7.3 기준, iphone 2.2.1 로 컴파일 됨)

'프로그래밍 > Cocos2D' 카테고리의 다른 글

Cocos2d 애니메이션 구현하기 3  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 2  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 1  (0) 2009.11.06
메뉴 만들기  (0) 2009.11.06
Cocos2d의 구성  (1) 2009.11.06
Posted by windship
프로그래밍/Objective-C2009. 4. 11. 13:59
박진형 jenix@jinhyung.org

연세대학교 수학과를 휴학하고 넥스지(NexG)에서 병역 특례로 일하고 있다. 리눅스 커널 개발, 오브젝티브-C, 코코아(Cocoa) 애플리케이션 개발에 관심이 많으며 OSXDev(http://www.osxdev.org)의 공동 대표를 맡고 있다.


난이도 : 초급 
2007년 2월27일


[오픈 디벨로퍼웍스]는 여러분이 직접 필자로 참가하는 코너입니다. 이번에는 박진형님이 작성한, 그동안 잘 알려지지 않은 프로그래밍 언어 중 하나인 오브젝티브-C에 대해 소개합니다.

오브젝티브-C란

오브젝티브-C는 1980년대 중반부터 사용되어 온 오래된 언어지만 GNU C 컴파일러나 넥스트(NeXT) 시스템, 혹은 애플(Apple), 이 세 가지 중 어느 하나에라도 관심이 없던 독자라면 처음 들어보는 생소한 프로그래밍 언어일지도 모른다. 오브젝티브-C는 C에 스몰토크(Smalltalk)의 몇 가지 문법을 채택해 객체 지향 특성을 더한 C의 상위집합(superset)이다.

오브젝티브-C를 위한 런타임 라이브러리에는 현재 애플 맥 OS X에서 쓰이는 코코아 프레임워크와 GNU 프로젝트에서 만든 GNUstep, 이 두 가지가 있다. 코코아 프레임워크는 맥 OS X에서만 사용할 수 있고 GNUstep은 리눅스를 포함하여 유닉스 계열(맥 OS X도 포함) 운영체제와 MS 윈도우에서도 사용할 수 있다.

오브젝티브-C를 이용해 개발하려면 GNU C 컴파일러와 런타임 라이브러리인 맥 OS X의 코코아 또는 GNUstep 환경이 필요하다. GNUstep의 경우 대부분의 리눅스 배포본에서 패키지로 제공되며 MS 윈도우 사용자는 오브젝티브-C와 GNUstep을 모두 이용할 수 있는 바이너리 형태의 설치 프로그램을 사용하면 된다. 설치에 관한 자세한 내용은 GNUstep 홈페이지의 튜토리얼을 참고하면 된다.

오브젝티브-C는 비록 잘 알려지지 않았고 사실 C에 객체 구현을 위한 클래스 관련 키워드와 문법에 메시징 메커니즘만 추가한 것이 전부라고도 할 수 있지만 C에 이렇게 작은 변화를 준 것만으로 비교적 최근에 만들어진 스크립트 언어인 파이썬(Python)이나 루비(Ruby)만큼이나 재미있는 특징을 지니고 있다. 이제 그 특징들을 하나씩 함께 살펴보기로 한다.




위로


C에 추가된 문법 #1: 메시징

오브젝티브-C에 추가된 새로운 문법 하나는 객체에 메시지를 보내기 위한 것으로 다음과 같이 쓴다.

  

[anObject doSomethingWith: anotherObject];


이 문법은 스몰토크를 사용해 본 개발자라면 어느 정도 친숙하게 보일 것이다. 대괄호([])는 객체에 메시지를 보내는 것을 나타내는 표현이다. 즉 ‘anObject란 객체에 anotherObject란 객체와 함께 doSomethingWith이란 일을 시킨다’라는 의미다. 위 코드를 C++에서 함수를 호출하는 것으로 표현하면 다음과 같다.

  

anObject->doSomethingWith(anotherObject);


[]를 사용한 새로운 문법은 처음에는 매우 이상하게 보일지 모르지만 이 문법은 메시지(함수)의 인자가 많아질수록 코드의 의미를 더 명확하게 한다. 다음과 같은 예를 보자.

  

[NSCalendarDate dateWithYear:1965
month:12
day:7
hour:17
minute:25
second:0];


위 코드를 보면 각각의 인자가 무엇인지를 이해할 수 있다. 이를 C++에서는 다음과 같이 호출할 것이다.

  

dateObject->newDate(1965, 12, 7, 17, 25, 0);


물론 위와 같이 간단한 날짜 객체를 생성하기 위해 오브젝티브-C 코드는 C++에 비해 사용자가 입력해야 할 코드 분량이 늘어나는 단점도 있다. 하지만 여러 사람이 프로젝트를 진행하는 과정에서 다른 사람이 작성한 코드를 봐야 한다면 인자 설명을 위한 별다른 주석 없이 메시지만으로도 이 메시지가 무엇을 하는 코드인지 쉽게 알 수 있다는 것은 큰 장점이다.




위로


C에 추가된 문법 #2: 클래스

객체 지향 프로그래밍을 어느 정도 해본 독자라면 오브젝티브-C 문법을 몰라도 다음 코드가 하는 일을 바로 알아차렸을 것이다.

  

	myclass.h
	@interface MyClass : NSObject
	{
	  int aVariable;
	  id  subObject;
	}
	+ (id) alloc;
	+ (id) defaultObject;
	- (id) init;
	- (int) doSomethingWith: (id)anotherObject;
	@end

	myclass.m
	#import <myclass.h>
	@implementation
	- (id) init
	{
		... 객체에 대한 초기화 ...
	}
	- (int) doSomethingWith: (id)anotherObject
	{
		... 코드들 ...
		return 0;
	}
	@end


MyObject : NSObject는 MyObject가 NSObject를 상속한다는 의미다. NSObject는 GNUstep에서 모든 클래스의 최상위 클래스다. 물론 최상위 클래스를 새로 정의해 사용해도 되지만 이렇게 할 경우 GNUstep 런타임 라이브러리에서 제공하는 많은 기능을 사용할 수 없다. NSObject 외에도 배열 자료구조, 사전형 자료구조 등 수십 가지 자료구조를 GNUstep에서 제공한다.

C의 기존 키워드와 겹치지 않도록 클래스 선언과 클래스 메서드 선언을 위한 키워드가 따로 추가되었다. 클래스 정의는 @interface ... @end로 선언하고, 클래스 메서드 구현은 @implementation ... @end 사이에 구현한다. 클래스 메서드는 + 혹은 -로 시작하는데, +는 클래스 메서드(객체 인스턴스 없이 바로 클래스에서 사용 가능한 메서드)를 나타내고, -는 객체의 인스턴스 메서드를 가리킨다. 위에서 선언한 - (int) doSomethingWith: (id) anotherObject; 메서드는 앞에서 잠깐 설명했던 것처럼 다음과 같이 바로 사용할 수 있다.

  

int result = [myObject doSomethingWith: anotherObject];


또 다른 새로운 키워드는 #import인데 이는 기본적으로 #include와 용도가 같다. 단 C에서는 헤더 파일을 작성할 때 중복되어 인클루드 되는 것을 막기 위해 #ifdef HEADER_H #define HEADER_H ... #endif 매크로를 이용하는 데 비해 오브젝티브-C의 #import는 이를 하지 않더라도 헤더 파일을 한번만 인클루드 되도록 해주는 점에서 큰 차이가 난다.

한 가지 특이한 점은 소스코드 확장자로 m을 쓴다는 것이다. 또한 오브젝티브-C는 C++와도 섞어 사용할 수 있는데 이럴 경우엔 확장자를 mm으로 한다. GNU C 컴파일러는 확장자가 m이면 오브젝티브-C로, 확장자가 mm이면 오브젝티브-C++로 인식하여 컴파일을 한다. 필자는 C++ 코드를 포함하기 위해 오브젝티브-C++를 사용하기보단 다른 개발자들이 만들어둔 좋은 C++ 라이브러리들을 사용하기 위해 mm을 사용하는데 이렇게 하고 라이브러리를 링크만 하면 C++ 라이브러리를 오브젝티브-C에서 바로 사용할 수 있다. 오브젝티브-C로 개발을 새로 하게 되더라도 기존에 작성해 두었던 라이브러리들을 재사용하는 데 큰 문제가 없다. 멋지지 않은가?




위로


동적 바인딩

오브젝티브-C는 완전한 동적 바인딩(dynamic binding)을 지원한다(이는 순수 객체 지향 언어에 필요한 조건이다). 이는 메시지를 보내는 객체는 프로그램이 실제로 실행되기 전까지 어떤 클래스나 어떤 함수에도 제한되지 않는다는 의미다. 다시 말하면, 프로그래머는 프로그램을 실행해보기 전까지는 해당 객체가 특정 메시지에 대해 어떻게 반응할지 모른다는 것이다.

이러한 오브젝티브-C의 동적 바인딩은 GNUstep이나 애플의 코코아 프레임워크 같은 런타임 라이브러리에 의해 이루어진다. 위의 예에서 doSomethingWith:와 같은 메시지를 해당 객체에 보내면 런타임 라이브러리는 해당 객체의 정의를 살펴보고 메시지에 대응하는 메서드가 해당 객체에 있다면 해당 메서드를 호출한다.

기존 C 혹은 C++와 다르게 메시지를 일종의 테이블에서 찾아보고 호출하는 방식이 프로그램 속도를 느리게 하지 않을까 하는 의문이 생길 것이다. 하지만 메시지에서 메서드를 찾아내는 이러한 작업은 런타임 라이브러리에서 최적화되어 있기 때문에 그렇게 걱정할 일이 아니다. 속도가 특별하게 중시되는 경우라면 오브젝티브-C는 메시지에 해당하는 메서드를 미리 지정해 성능 문제가 없도록 할 수도 있다.

이러한 메시징 기법을 사용하기 때문에 오브젝티브-C에서 다형성은 C++와는 많이 다르다. 예를 들어 보자. 위의 MyClass에서 doSomethingWith: 메시지를 구현했고 다른 클래스인 YourClass에서 doSomethingWith: 메시지를 또 구현했다고 하자. 두 객체는 랜덤으로 생성되고 프로그램 로직에서 어떤 객체가 들어오는지 알 수 없다. 다음 코드를 보자.

  

// 이 코드는 오타가 아니다. C 함수에서 객체를 사용한 경우다.
	void someFunction(id randomObject)
	{
		[randomObject doSomethingWith:globalObject];
	}


위와 같이 행동하는 someFunction이 있다고 하면 randomObject에 doSomethingWith: 메시지를 보낸다. 그럼 randomObject가 무엇이든 원래 클래스에 구현되어 있는 doSomethingWith: 메시지대로 행동한다. 이것이 오브젝티브-C의 다형성이다. 기존 C++ 등에 익숙한 독자라면 뭔가 이상할 것이다. 저렇게 모든 객체를 가리킬 수 있는 id로 객체를 가져와 메시지를 보냈는데 만약 doSomethingWith:이란 메시지가 구현되어 있지 않다면 어떻게 될까? 오브젝티브-C에서는 이럴 경우 메시지를 해당 메시지를 처리할 수 있는 객체로 넘겨줄 수 있다.

  

    - (void) forwardInvocation: (NSInvocation*)anInvocation
    {
        if ([anObject respondsToSelector: [anInvocation selector]])
            [anInvocation invokeWithTarget: anObject];
        else
            [super forwardInvocation:anInvocation];
    }


anObject에 처리할 수 없는 메시지가 있는지 확인한다. 있으면 해당 객체로 메시지를 넘겨주고 아니면 해당 메시지를 상위 객체로 다시 전달한다. 바로 런타임에 해당 메시지가 있는지 확인하기 때문에 이러한 것이 가능하다.

오브젝티브-C의 이러한 특징은 클래스를 디자인할 때 일관성을 제공한다. 워드 프로세서 프로그램을 작성한다고 생각해 보자. 문서 안에는 텍스트가 있을 수 있고 그림 파일 또는 동영상 파일까지 첨부할 수도 있다. 복사하고 싶은 대상을 선택하고 메뉴의 복사 버튼을 누른다고 가정해보자. 이럴 경우 어떻게 할 것인가?

복사 메뉴를 누른 곳에서 선택된 아이템이 무엇인지 확인하고 행동하는 것이 맞는 것인가, 아니면 해당 객체를 어떻게 복사할지를 결정하는 것이 적절하겠는가? 각각의 텍스트 객체, 사진 객체, 동영상 객체에 copy: 메시지를 구현해 두면 워드 프로세서의 복사 메뉴에서는 id로 객체를 받고 [anObejct copy];라고 해당 객체에 copy: 메시지만 보내면 된다. 각각의 객체에서 copy: 메시지를 최종적으로 클립보드에 복사되게만 해두면 된다. 복사가 불가능한 객체라 copy: 메시지를 구현하지 않아 copy 메시지를 처리할 수 없는 경우 copy 메시지를 처리하는 객체를 하나 만들어 사용자 경고창으로 해당 객체를 복사할 수 없다는 경고문을 표시한다든가 하면 된다.




위로


손쉬운 확장

오브젝티브-C로 작성된 라이브러리들은 손쉽게 확장할 수 있다. 기존 클래스를 제작한 사람이 바이너리 파일만을 제공했을 경우, 해당 클래스에 새로운 기능을 추가하고 싶다면 일반적인 객체 지향 언어에서는 해당 클래스를 상속해 거기에 새로운 기능을 추가하려 할 것이다. 하지만 오브젝티브-C에서는 그렇게 하지 않는다. 다음 코드를 보자.

  

@interface MyClass (MyAdditionalMethods)
- (int) doSomethingWith: thisObject andThenWith: thatObject;
@end


@interface 클래스 이름 다음에 ()로 뭔가 하나 추가됐음을 볼 수 있다. 이를 오브젝티브-C에서는 카테고리라고 부른다. 이렇게 기존 MyClass에 자신만의 카테고리를 만들어 이 카테고리 안에 새로운 메서드를 추가할 수 있다. 이렇게 추가한 메서드는 MyClass 객체에서 모두 사용할 수 있다. 제한이 있다면 기존 클래스의 인스턴스 변수를 새로 추가하는 것은 불가능하다는 점이다. 이렇게 기존 클래스에 새로운 변수를 추가해야 할 경우는 새로운 객체를 디자인한다고 생각하고 C++와 마찬가지로 해당 클래스를 상속해 변수를 추가해야 한다.

기존 클래스의 메서드를 변경할 수도 있다. 기존 클래스에 있던 메서드와 동일한 메서드를 카테고리에 추가한다면 객체에 메시지를 보낼 때 카테고리에 있는 메서드를 우선적으로 찾아 사용하게 된다.

결론

지금까지 오브젝티브-C가 C와 다른 점 중에서 중요한 내용 몇 가지를 살펴보았다.

가장 큰 특징은 역시 오브젝티브-C는 동적 프로그래밍 언어라는 점이다. 스크립트 언어인 루비나 파이썬처럼 기본 자료형까지 동적은 아니지만(이는 C 언어의 상위집합이기 때문이다) 오브젝티브-C의 객체는 모두 실행시에 객체가 무엇인지 결정된다. 이는 프로그램을 디자인할 때 많은 이점을 제공한다.

또한 오브젝티브-C는 앞에서 언급했듯이 C의 상위집합이기 때문에 C를 사용하듯이 그대로 사용해도 된다(중간에 예제에서 봤듯이 C의 함수 선언, 호출, 구조체 사용 등 모든 것을 C로 생각해도 된다). 다만 기존 C에 없던 객체 지향 언어에 필요한 기능들이 추가된 것이기 때문에 기존에 C를 사용하던 사람이라면 쉽게 접근할 수 있을 것이다.

앞에서 소개한 것 이외에도 오브젝티브-C에는 생성한 객체의 메모리 관리, GUI 코드 작성을 쉽게 해주는 객체의 바인딩 등 런타임 라이브러리에서 제공하는 수많은 기능이 있다. 이러한 기능들에 익숙해지면 오브젝티브-C로 애플리케이션을 개발하는 데 있어서 생산성이 높아짐을 알 수 있을 것이다.

오브젝티브-C는 조만간 2.0 규격이 나올 예정이다. 보다 향상된 메모리 관리와 더 풍부해진 런타임 라이브러리를 지원한다고 한다. 20년간 꾸준히 사용되어 온 오브젝티브-C는 앞으로도 여러 개발자들이 꾸준히 이용할 것이고 발전할 것이다. 이번 기사를 통해 오브젝티브-C에 대한 모든 것을 소개할 수는 없었지만 앞으로도 꾸준히 발전할 오브젝티브-C로 즐겁게 개발할 수 있었으면 하는 바람이다. 

Posted by windship