본문 바로가기

마이크로소프트웨어

실전 아이폰 게임 개발 준비하기

드디어 한국에도 불기 시작한 아이폰 광풍은 개발자들의 관심을 끌기에도 충분합니다. 이에따라 주변 개발자들 중 전혀 다른 분야에 근무하던 분들까지 아이폰 개발에 관심을 가지거나 직접 아이폰 애플리케이션 개발을 시작하게 되는 경우들을 많이 접하게 되는데요. 그중에는 여러가지 시행 착오를 겪는 분들도 많습니다. 이번 글은 월간 마소에 실린 글 중 이창신 님이 쓰신 글이며, 아이폰 게임 개발을 하기 위해 알아두어야 할 내용들이 잘 정리되어 있습니다. 아이폰 게임 개발을 준비하는 분들께 도움이 되면 좋겠네요. 참고로 이창신 님은 월간 마소 연재를 통해 아이폰 게발과 관련된 노하우들을 독자들에게 제공할 예정입니다.

아이마소 : http://www.imaso.co.kr

최근 한 한국 개발팀이 만든 아이폰 게임이 앱스토어 상위에 올라 화제가 된 적이 있고 아이폰 애플리케이션의 여러 종류 중 가장 인기 있는 것은 단연 게임이기도 하다. 그동안 잘 된다는 말은 많이 들었지만, 막상 가까운 같은 나라에서 히트작이, 그것도 본업(회사원)이 있으면서 개발자와 디자이너 2명의 힘으로 일궈낸 성과라 많은 사람들에게 희망과 자극이 되지 않았나 싶다.

이창신 iasandcb@gmail.com, http://ias.myid.net|현재 아이폰 애플리케이션 개발에 몰두하고 있다.

그래서 막상 아이폰 게임 제작에 뛰어 들어보면 꽤나 난관이 많음을 알게 된다. 일단 개발과 관련된 것만 추려 보면 다음과 같다.

● 아이폰 애플리케이션 개발 기반 마련: 크게 두 가지 문제가 있다.
   - 하드웨어: 일단 맥 컴퓨터가 있어야 한다. 게다가 국내에 아이폰이 들어오지 않아 아이팟 터치를 대신 써야 하는데, 고환율 덕분에 맥과 아이팟 터치 둘 다 매우 비싸다.
   - 소프트웨어: 개발 환경이 매우 낯설다. 개발 언어도 무척 생소한 Objective-C이고, IDE도 Xcode를 쓴다. 이전부터 맥 애플리케이션 개발에 관심이 없었던 경우는 그야말로 ‘듣보잡’ 수준일 수도 있다.
● 아이폰 애플리케이션 개발 기본 습득: 전에는 우리말로 된 책조차도 없어 학습에 애로가 많았지만, 최근 입문 번역서가 나와 한숨 돌릴 수 있게 되었다.
● 아이폰 게임 개발에 필요한 기술 습득: 입문서에서는 자세히 다루지 않은 그래픽스와 사운드 처리 쪽을 더 파야 한다.

그리고 다음과 같은 배경을 가졌다면 앱스토어에 있는 높은 수준의 게임을 보며 갈 길이 먼 것을 깨달을 것이다.

● 전에 게임을 개발해 본 적이 (별로) 없다.
● (자바나 스크립트 언어 같은 환경에 비해) C 언어에 익숙하지 않다.

그러던 차, 게임에도 프레임워크가 있을 터, 우연히 발견하게 된 것이 바로 이 글을 통해 소개할 cocos2d iPhone(이하 cocos2d, 참고로 cocos2d는 파이썬으로 되어 있는 게임 엔진이고, 기본 개념을 따와 Objective-C로 응용해 아이폰에서도 쓸 수 있게 한 것이 cocos2d iPhone이다)이다. 이름에서 알 수 있듯이 이 게임 엔진은 2차원을 대상으로 하고 있다. 혹 3차원 게임 개발에 관심이 있는 독자는 SIO2(http://sio2interactive.com/HO ME/HOME.html)가 3D 엔진으로 cocos2d 격이므로 참고해 보길 바란다.

Hello cocos2d

cocos2d는 2D 게임을 위한 엔진이지만, 흥미롭게도 기반은 OpenGL, 즉 3D 기술이다. 이는 근본적으로 아이폰(아이팟 터치도 동일)에 PowerVR이라는 3D 가속 칩이 들어 있어 3D 처리가 무척 강력하기 때문에, 2D조차도 3D로(즉 3차원에서 한 차원 줄이면 되므로) 처리하는 방식인데, 3D 기능이 강력한 PC나 가정용 게임기에서도 쓰인다. 하지만 그렇게 cocos2d를 쓰기 위해 OpenGL을 알아야 필요는 없다. 되레 게임 개발을 쉽고 편하게 하기 위해 OpenGL과 같은 하부 기술은 상당히 감춰 놓았다. cocos2d의 또 하나의 장점은 코코아 스타일의 API이다. 객체 지향 프로그래밍에 익숙하다면 Objective-C의 기본 감각으로도 충분히 이해할 수 있는 구조여서 자바나 C#과 같은 플랫폼에서 작성하는 기분을 느낄 수 있다. cocos2d는 오픈소스 프로젝트로 http://code.google.com/p/ cocos2d-iphone/에서 진행되고 있으며, 2009년 3월 현재 0.7 버전이 릴리즈되어 있고 곧 0.8과 1.0을 출시할 계획이다.

흥미로운 것은 이 cocos2d 엔진을 만든 사람 자신도 이 엔진을 써서 Sapus Tongue이라는 게임을 만들었다는 사실이다. 엔진이 오픈소스에 무료라서 수익 모델이 없어 보이는데, 아직 관련 자료가 많지 않아 실제 게임 개발에 참고할 만한 예제가 충분치 않다.

cocos2d 준비

cocos2d를 배워보려고 하는 데 있어서 가장 큰 장애라면 역시 문서의 부족이다. 특히 공식 문서라 할 수 있는 프로젝트 문서가 거의 없다(이점은 Ricardo도 인정하고 있다). 그나마 있는 문서들은 많이 흩어져 있는데, cocos2d의 공식 블로그(http:// blog.sapusmedia.com/)에서 문서에 대한 안내 포스트(http:// blog.sapusmedia.com/2009/03/documentation-in-cocos2d. html)에도 가장 먼저 소개된 문서인 cocos2d whitepaper (http://monoclestudios.com/cocos2d_whitepaper.html, 이 문서는 cocos2d를 이용해 게임을 만드는 회사에서 작성한 것)를 참고로 cocos2d 준비를 설명해 보려 한다.
cocos2d는 일종의 라이브러리이므로 쉽게 가져다 쓸 수 있을 것 같지만, 실은 소스를 자신의 프로젝트에 넣어야 한다. 그래서 아예 cocos2d를 이용한 애플리케이션을 만들 때에는 다음과 같은 과정으로 프로젝트를 생성하고 설정해야 한다.

먼저 Xcode를 실행하여 File -> New Project를 선택한 다음, 오른쪽에서 Window-Based Application을 선택하고 Choose 버튼을 누른다. 프로젝트 이름은 HelloCocos2d라고 해보자.

cocos2d 통합

먼저 cocos2d를 받아 적당한 곳에 압축을 푼다. 그리고 앞으로 파일을 추가할 때는 Copy items into destination group’s folder (if needed)는 선택해도 좋지만 압축을 푼 파일의 사본이 프로젝트에 추가되는 것이므로 만약 cocos2d 코드를 한 곳에서 관리하고 싶다면 선택하지 않는 것도 방법이다.

첫 번째, HelloCocos2d 프로젝트로 돌아가 Project -> Add to Project...를 선택하여 cocos2d를 푼 디렉토리로 가서 그 밑의 external 디렉토리를 선택한다. 그러면 팝업 메뉴가 하나 뜨는데 Add 버튼을 누른다. 그러면 프로젝트 뷰의 왼쪽에 external이라는 그룹(폴더 모양)이 생긴 것을 볼 수 있는데, 이 그룹을 펼쳐서 Chipmunk 디렉토리 밑으로 Demo라는 디렉토리가 보이는데 이것을 지운다(지울 때, 위의 Copy items into destionation... 옵션을 선택했다면 Also Move to Trash, 아니면 Delete References를 선택한다).

두 번째, 또 다시 Project -> Add to Project를 선택하여 cocos2d를 푼 디렉토리 밑의 cocos2d 디렉토리를 선택한다(혼동하지 말도록. cocos2d를 푼 디렉토리를 선택하는 것이 아니라 그 밑의 cocos2d 디렉토리를 선택하는 것이다).

세 번째, 이번에는 프로젝트 뷰의 왼쪽 Groups & Files에서 Resources 그룹을 선택하여 컨텍스트 메뉴로 Add -> Existing Files...를 선택한 다음, cocos2d를 푼 디렉토리 밑의 Resources 아래의 Images 디렉토리에 있는 fps_images.png을 선택하여 추가한다.

마지막으로 네 번째, Groups & Files에 Frameworks라는 그룹이 있는데 이것을 선택하여 컨텍스트 메뉴로 Add -> Existing Frameworks...를 선택한 다음 OpenGLES.framework와 QuartzCore.framework을 선택하여 추가한다.

이로써 cocos2d를 사용하는 애플리케이션을 만들 준비가 다 되었다. 그럼 어떤 새로운 기술을 배우더라도 꼭 처음에 등장하는 예제인 ‘Hello World’를 바로 만들어 보자.

Hello cocos2d

<리스트 1>은 지금까지 작업한 결과로 생성되는 HelloCocos2 dAppDelegate.m 파일이다.


<리스트 1>  HelloCocos2dAppDelegate.m의 내용

    //
    //  HelloCocos2dAppDelegate.m
    //  HelloCocos2d
    //
    //  Created by Changshin Lee on 09. 03. 19.
    //  Copyright __MyCompanyName__ 2009. All rights reserved.
    //
    #import "HelloCocos2dAppDelegate.h"
    @implementation HelloCocos2dAppDelegate
    @synthesize window;

    - (void)applicationDidFinishLaunching:(UIApplication *)application {    
        // Override point for customization after application launch
        [window makeKeyAndVisible];
    }

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


이 파일에 추가해야 할 것은 크게 세 가지다.

첫째, cocos2d를 쓰기 위해 #import “cocos2d.h”를 맨 위에 넣는다.
둘째, cocos2d 엔진의 초기화 코드를 // Override point for ... 부분에 넣는다.
셋째, Hello cocos2d를 출력하는 로직을 구현한다.

<리스트 2>의 코드는 앞서 언급한 세 가지 작업을 마친 것이다. 엔진 초기화는 간단히 주화면인 window를 Director 싱글턴([Director sharedDirector]의 반환값)에 붙이면(attachIn Window) 된다. 이후 코드는 아직 배우지 않은 것들이 많이 나와 생경하지만, 간단히 <그림 1>과 같은 흐름이라 할 수 있다.

 

사용자 삽입 이미지


<리스트 2> 세 가지 작업을 마친 결과

    //
    //  HelloCocos2dAppDelegate.m
    //  HelloCocos2d
    //
    //  Created by Changshin Lee on 09. 03. 19.
    //  Copyright __MyCompanyName__ 2009. All rights reserved.
    //

    #import "HelloCocos2dAppDelegate.h"
    #import "cocos2d.h"
    @implementation HelloCocos2dAppDelegate
    @synthesize window;

    - (void)applicationDidFinishLaunching:(UIApplication *)application {    
    [[Director sharedDirector] attachInWindow:window];   
    Scene *helloScene = [Scene node];
    Label *helloLabel = [Label labelWithString:@"Hello cocos2d" fontName:@"TrebuchetMS" fontSize:40.0f];
    helloLabel.position = cpv(150, 100);
    [helloScene add:helloLabel];
    [[Director sharedDirector] runWithScene:helloScene];
    [window makeKeyAndVisible];
    }

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


cocos2d의 특징

가장 먼저 눈에 띄는 것은 아이폰 애플리케이션의 기본 구조인 Model-View-Controller의 표준 클래스인 UIView와 UIView Controller를 쓰지 않는 cocos2d 고유의 모델이다. 물론 기존 UIView·UIViewController와 같이 써도 되지만, 쓰지 않고도 완결된 애플리케이션 작성이 가능하다는 점이 독특하다. 그래서 아마 처음 이 모델을 보면 꽤나 적응이 안 될 수도 있는데, 자주 쓰게 되면 자연스럽게 차츰 적응이 된다(필자의 경험상).

또 하나는 좌표계다. UIView의 기본 좌표계는 왼쪽 위 모서리가 (0, 0)이고 오른쪽으로 갈수록 x 좌표가, 아래로 갈수록 y 좌표가 증가한다. cocos2d는 OpenGL 기반의 좌표계를 채용하여 왼쪽 아래 모서리가 (0, 0)이고 위로 갈수록 y 좌표가 증가한다.

좌표계와 더불어 독특한 것이 뷰의 위치 지정인데, 앞의 코드에서 나온 position은 UIView의 frame.origin이 아닌 center, 즉 뷰의 중심을 뜻한다. 뷰의 위치를 좌표로 지정하다 보면 왼쪽 위 모서리에 해당하는 origin이 편할 경우가 있는데, 항상 중심을 지정해야 하므로 계산이 필요할 수 있다.

cocos2d API의 기본 구조

cocos2d 아이폰 버전의 API는 cocos2d 원조(파이썬 버전) API로부터 기본 개념을 가져왔다. 따라서 cocos2d의 개념 파악을 위해서라면 파이썬 버전 기반의 프로그래밍 가이드인 http://www.cocos2d.org/doc/programming_guide/도 충분히 도움이 된다. 이 글에서는 cocos2d API의 핵심 클래스들을 나열하며 소개해 보려 한다.

CocosNode

엔진의 이름과 겹치는 만큼, CocosNode 클래스는 핵심 중의 핵심이다. 이후 나오는 Scene, Layer, Sprite 등 cocos2d 엔진을 바탕으로 돌아가는 그래픽 오브젝트의 뿌리가 되는 클래스이다. 이 클래스에는 position이나 visible과 같은 공통 프로퍼티와 그래픽 오브젝트의 계층 구조를 구성하게 해주는 add, remove와 같은 메소드, 그리고 액션과 스케줄링을 위한 기능까지 들어 있다.

특히 그래픽 오브젝트의 계층 구조는 UIView의 그것과 마찬가지라고 이해하면 쉽다. 따라서 화면을 구성함에 있어 이 계층 구조를 잘 활용하는 것이 관건이기도 하다. 액션에 대해서는 Action 클래스에서 언급하기로 하고 스케줄링에 대해 말하자면, NSTimer와 같이 중앙 집중적인 스케줄링 기능을 이용하는 것이 아니라, 오브젝트마다 분산된 자신만의 스케줄링이 가능하다는 점에서 매력적이다.

Scene과 Director

하지만 앞의 예제에서는 CocosNode 클래스는 나오지 않는다. 대신 Scene과 Director가 나오는데, 클래스 이름이 영화에서 온 것 같아 이해가 쉽다. 먼저 Director는 말 그대로 cocos2d 엔진에 있어 감독 같은 존재이다. 영화에 감독은 보통 한 명이듯이, Director 오브젝트도 하나, 즉 싱글턴(singleton)으로 되어 있어서 항상 [Director sharedDirector] 클래스 메소드 호출로 싱글턴을 얻어 낸다.

감독이 영화 제작에 있어 단위로 삼는 것은 씬(Scene), 즉 장면이다. cocos2d에서도 Scene은 가장 큰 화면 구성 단위이며 Director는 이 Scene 오브젝트 단위로 화면을 표시한다.

Layer

Scene이 게임에 있어 전체 화면을 가리킨다면, Layer는 그 화면을 구성하는 요소요소를 나타낸다. Scene이 영화에서 배경화면이라면, Layer는 배우나 소품에 해당하는 셈이다.

 

사용자 삽입 이미지

Scene과 Layer의 기능상의 가장 큰 차이점은 사용자 입력을 받을 수 있는가이다. 따라서 사용자 입력이 필요 없는 정적인 요소는 Scene에 바로 추가하는 방식을 취하며, Layer는 주로 사용자 입력이 필요한 동적인 요소를 구성하는 바탕이 된다. Layer가 터치 이벤트나 액셀로미터 이벤트를 받으려면 TouchEvents Delegate 프로토콜을 구현해야 한다.

 

사용자 삽입 이미지

Sprite

스프라이트는 게임 개발에 있어 화면상에 움직임이 있는 물체의 단위이며, Sprite 클래스도 바로 그런 의미이다. 스프라이트를 생성하는 방법에는 몇 가지가 있는데, 가장 쉽게 이미지 파일(아이폰에서는 PNG를 주로 쓴다)로부터 생성하며 애니메이션을 위해 하나의 스프라이트에 복수 개의 이미지를 넣을 수 있다. 부가적으로, 앞서 소개했던 PowerVR 3D 가속칩에 최적화된 PVRTC 이미지 파일도 사용할 수 있다.

 

사용자 삽입 이미지

Action

Action은 기본적으로 CocosNode에 액션을 취하게 하는 클래스이긴 하지만, 보통은 게임에서 주로 움직이는 대상인 Sprite에 많이 적용하게 된다. Action에는 크게 InstantAction, IntervalAction, 그리고 RepeatForever가 있는데

- InstantAction은 즉시 실행되고 바로 끝나는 단발성의 액션
- IntervalAction은 일정 시간동안 지속되는 액션
- RepeatForever은 특정 IntervalAction을 무한 반복하는 액션

각 종류별로 무척 다양한 액션들이 제공되어 있어 스프라이트의 움직임을 프로그램으로 작성할 때 무척 편리하다.

cocos2d 기반 게임의 기본 구성

앞서 cocos2d의 핵심 API를 배웠다고 해도, 게임 자체의 개발과 직결되는 것은 아니다. Director - Scene - Layer - Sprite - Action을 개별적으로 사용하여 아주 간단한 게임을 구성할 수 있겠지만(또는 게임의 핵심 부분을 빠르게 구현하는 프로토타이핑도 가능할 것이다), 제품 수준의 게임이라면 <그림 2>와 같은 구도를 갖게 마련이다.

 

사용자 삽입 이미지

<그림 2>를 이루는 요소 하나하나가 Scene이 되는데, 그럼 차례대로 살펴보자.

TitleScene과 MenuScene

게임을 시작하면 보통 화려한 오프닝이 시선을 집중시킨다. 타이틀 화면은 동영상으로 간단하게 처리할 수도 있지만, 여기서부터 인터렉션과 애니메이션이 필요할 경우는 TitleScene의 별도 작성도 요구된다.

타이틀 화면을 마치면 게임의 준비 화면인 메뉴가 나오게 된다. MenuScene은 모든 게임에 필수이므로, 따라서 cocos2d는 Menu와 MenuItem이라는 클래스를 제공하여 메뉴 구성의 편의성을 높이고 있다. Menu는 Layer를 상속하고 있어 당연히 사용자 입력을 받을 수 있는데, MenuItem을 이미지나 텍스트로 추가만 하면 알아서 터치 이벤트를 해당 MenuItem과 연결된 셀렉터(selector)로 이어준다. <그림 2>에서와 같이 메뉴 아이템에는 본 게임, 도움말, 스코어, 설정 등이 있으며 해당 아이템을 터치하면 Director의 replaceScene으로 화면 전환을 해주는 식으로 작성한다. 타이틀과 메뉴를 제외하고 모든 화면은 언제고 메뉴 화면으로 돌아올 수 있어야 하는 점도 간과해서는 안 된다.

GameScene

GameScene은 게임 본편에 해당한다. 여기에 게임 로직이 들어가게 되는데, 게임 오버가 되면 보통 ScoreScene으로 이어진다.

ScoreScene

ScoreScene은 그동안의 점수와 하이 스코어일 경우 이름을 받는 등의 작업이 일어난다.

HelpScene과 OptionScene

HelpScene은 게임을 처음 하는 사람을 위한 안내를 하며, 간단히 그림이나 동영상으로 설명할 수도 있다. OptionScene은 게임의 콘트롤이나 난이도, 사운드 등을 설정하게 해주는 화면이다.

아직 cocos2d에 대한 문서가 많지 않아 배우기가 만만치 않지만, 그래도 뜻이 있는 곳에 길이 있는 법이다. 가장 좋은 학습방법은 실제 cocos2d로 만든 게임 코드를 보는 것이다. 마침 오픈소스로 되어 있는 cocos2d 기반 게임인 Gorillas(http:// gorillas.lyndir.com/trac/wiki/TheSource)가 있어(게임 자체도 꽤 재밌다) cocos2d로 게임을 만들어 보려는 이들에게 최고의 도우미 역할을 해주리라 본다.

더불어 이번 특집에서의 cocos2d 소개에 이어 다음 달부터는 마소의 연재 코너를 통해 cocos2d와 함께 아이폰 게임을 만드는 과정을 좀 더 깊게 살펴보려 하니 아무쪼록 cocos2d의 기초를 잘 닦는 시간으로 4월을 보내고 5월호에서 다시 만나기를 소망한다.


cocos2d의 새 버전 0.7.1

원고를 마소 편집부로 넘기고 난 다음 날 아침, cocos2d의 새 버전인 0.7.1이 나온 것을 알고 받아보니 중요한 변경 사항이 있어 서둘러 추가했다. 본문에도 소개된 CocosNode의 멤버 메소드들 중 코드 표기법에 맞지 않는 메소드들을 대거 교체했다.

[self add:node];        // OLD
[self addChild:node];   // NEW

[self add:node z:0];          // OLD
[self addChild:node z:0];     // NEW

[self add:node z:0 tag:t];          // OLD
[self addChild:node z:0 tag:t];     // NEW

[self add:node z:0 tag:t parallaxRatio];        // OLD
[self addChild:node z:0 tag:t parallaxRatio];   // NEW

[self getByTag:tag];        // OLD
[self getChildByTag:tag];   // NEW

[self remove:node];                    // OLD
[self removeChild:node cleanup:NO];    // NEW

[self removeAndStop:node];                  // OLD
[self removeChild:node cleanup:YES];        // NEW

[self removeByTag:tag];                    // OLD
[self removeChildByTag:tag cleanup:NO];    // NEW

[self removeAndStopByTag:tag];              // OLD
[self removeChildByTag:tag cleanup:YES];    // NEW

[self removeAll];                           // OLD
[self removeAllChildrenWithCleanup: NO];    // NEW

[self removeAndStopAll];                    // OLD
[self removeAllChildrenWithCleanup: YES];    // NEW

[self do: action];          // OLD
[self runAction: action];   // NEW

[self absolutePosition];                   // OLD
[self convertToWorldSpace:CGPointZero];    // NEW

0.7.1에서도 // OLD에 해당하는 옛 메소드를 사용할 수 있지만 비추천(deprecated) 상태이며 0.8부터는 아예 사라진다고 하니 지금부터 0.7.1을 쓰더라도 // NEW에 해당하는 메소드를 쓰기 시작하자. 따라서 본문의 HelloCocos2d 예제 코드에서 [helloScene add:helloLabel];도 [helloScene addChild:helloLabel];와 같이 바꾸는 것이 좋다.