Cocos2d-x 엔진을 이용한 게임 프로젝트 2014년 3월 19일 작성자: 박지혁 (nadag), 송상헌 (ssh5598) 메일 주소: [email protected], [email protected] http://newheart.kr 목차 Chapter 1 개요 1.1 문서를 작성하며 1.2 Cocos2d-x란 Chapter 2 설치 방법 2.1 설치 및 프로젝트 생성 2.2 안드로이드 포팅 Chapter 3 Cocos2d-x 엔진 분석 3.1 Cocos2d-x 엔진 3.2 Cocos2d-x 기본 구조 1) Director 2) Scene 3) Layer 4) Node 3.3 Cocos2d-x 심화 LayerClipping 3.4 Cocos2d-x 심화 Touch System 완벽 분석 3.5 Cocos2d-x 심화 메모리 관리 기법 Chapter 4 Cocos2d-x 프로젝트 분석 4.1 Main 4.2 AppDelegate 4.3 StartScene 4.4 GameScene Chapter 5 마치며 5.1 맺음 참조 Chapter 1 개요 1.1 문서를 작성하며 이 문서는 게임 프로젝트를 하면서 공부한 것을 따로 정리해두는 용도로 제작한 문서이다. Cocos2d-x라는 엔진을 사용하였으며 프로그램을 안드로이드 앱으로 실행시키는 것을 목표로 하 였다. 일단 Chapter 2 에서는 엔진 설치 방법 및 안드로이드로 포팅하는 과정을 서술하였다. 그 후 Chapter 3 에서는 Cocos2d-x의 엔진 구조에 대해서 적었다. Chapter 4 에서는 Chapter 3 에서 설명한 내용들을 토대로 프로젝트를 만들었다. 마지막으로 Chapter 5는 프로젝트에 대한 결론이 다. 1.2 Cocos2d-x란 컴퓨터, 디자인, 음악, 스토리 등이 결합된 종합 예술이라고 할 수 있는 게임은 길지 않은 시간 동안 많은 발전을 거듭해 왔다. 게임의 장르가 다양해지고 그래픽이 발전함에 따라 게임을 구현 하는 코드의 양도 많아졌다. 이 많은 코드들을 효율적으로 연결시키고 관리해주기 위해 만들어 진 것이 게임 엔진이다. 또한 게임 엔진을 통해서 여러 종류의 게임을 쉽게 만들 수 있다. 요즘 나오는 게임들은 모두 게임 엔진을 사용하여 만들었다고 해도 과언이 아니다. 언리얼 엔진이나 소스 엔진 등 유료 엔진을 사용하는 경우도 있고, 일리히트처럼 오픈 소스를 사용하는 경우도 있 다. 또 어떤 경우에는 게임을 제작하기 위해 자체적으로 엔진을 먼저 만들어 사용하는 경우도 있 다. Cocos2d는 OpenGL을 기반으로 한 오픈 소스 엔진으로 원래 ios앱 개발용으로 제작되었다. 이 후 어떤 프로그래머에 의해 안드로이드로 포팅이 가능한 Cocos2d-x 버전이 개발되었고, 현재 많 은 사람들이 앱에 그래픽을 추가하거나 게임을 제작할 때 이 엔진을 사용하고 있다. 개발언어는 C++이지만 이후 안드로이드로 포팅하려면 C++를 자바로 변환시켜야 한다. 변환시키는 과정은 추후에 알아보도록 한다. 이름에서부터 알 수 있듯이 cocos2d-x는 2d 그래픽이다. Cocos2d-x는 스프라이트, 액션, 스케줄 링 등 다양한 기능들을 제공하며 직관적이고 편리한 함수들로 이 기능들을 사용할 수 있다. 또한 무료로 배포하는 엔진이기 때문에 쉽게 다운로드 할 수 있고 내부 구조를 살펴볼 수 있다. Chapter 2 설치 방법 2.1 설치 및 프로젝트 생성 그림 2-1 Cocos2d-x를 설치하는 방법은 어렵지 않다. 그림 2-1처럼 Cocos2d-x 공식 사이트 (http://www.cocos2d-x.org/)에 들어가 다운로드 탭을 클릭한 후 원하는 버전을 선택하여 다운로 드 하면 된다. 그림 2-2 이 문서를 작성하면서 사용한 엔진은 Cocos2d-x 3.0 beta2 버전이고 현재(2014년 3월 24일)를 기준으로 Cocos2d-x 3.0rc0 버전까지 나왔다. 옆에 보이는 것들은 js용 엔진과 Cocos2d-x를 기반 으로 한 제작 툴인 Cocostudio이다. 설치가 끝나 적당한 디렉터리에 압축을 풀면 그림 2-3과 같은 화면이 나온다. 그림 2-3 디렉터리 안에 존재하는 많은 폴더 중 build라는 폴더에 들어가면 cocos2d-win32.vc2012.sln 이 라는 파일을 찾을 수 있는데 이 파일을 실행시키면 된다. 참고로 2.x대 버전에서는 visual studio2012 이하 버전도 지원하였으나 Cocos2d-x가 3.0버전이 되면서 visual studio 2012이상의 버전만 지원한다. 그림 2-4 그림 2-4는 cocos2d-win32.vc2012.sln을 실행시킨 화면이다. 엔진 코드와 기본적으로 제공하는 모든 예제 소스들이 여기 들어있다. 각 프로젝트에 들어가 내용을 살펴볼 수도 있고 때에 따라 수정할 수도 있다. 일단 프로젝트를 생성하려면 솔루션 빌드를 한 번 해주어야 하므로 ctrl + shift + B 버튼을 눌러 솔루션을 빌드 해준다. 수 많은 warning 메시지가 나올 텐데 무시하고 느긋하게 기다리면 모든 프로젝트가 빌드 된다. 여기까지 프로젝트를 생성할 모든 준비가 되었다. 2.x버전에서는 이 솔루션에서 프로젝트를 새 로 만든 후 작업하는 방식을 사용했지만 3.0버전부터는 새로 프로젝트를 만들어 작업한다. 새로 프로젝트를 만들기 위해 우선 솔루션을 종료한다. 그 후 다시 아까 압축을 풀었던 디렉터리로 돌 아와 이번에는 tools 디렉터리에 들어간다. 그 다음 project-creator 디렉터리에 들어가면 그림 2-5와 같은 화면이 나타난다 그림 2-5 . Create_project.py 파일을 실행하면 Cocos2d-x엔진의 기본 소스들만 가지고 솔루션을 생성할 수 있다. 참고로 그림 2-5를 보면 알 수 있듯이 python file 형식이기 때문에 python을 설치해야 Create_project.py를 실행시킬 수 있다. Python을 설치한 후 이 파일을 더블 클릭하면 그림 2-6과 같은 창이 등장한다. 그림 2-6 굉장히 심플한 창이다. 적당한 Project Name과 Project Path를 설정한 후 밑에 있는 create 버튼 을 클릭한 후 또 다시 느긋하게 기다린다. 시간이 지나 성공했다는 메시지가 뜨면 프로젝트가 성 공되었다는 뜻이다. 실제로 아까 설정한 디렉터리 안에 프로젝트 폴더가 있고 visual studio를 통 해 실행시키면 그림 2-7과 같은 화면이 나타난다. 그림 2-7 아까 cocos2d-win32.vc2012.sln와는 다르게 솔루션에 예제 프로젝트들이 없어 굉장히 깔끔한 모습이다. MyGame2 프로젝트를 수정함으로써 개발을 진행해 나갈 수 있다. 그림 2-8 그림 2-8은 처음 생성하면 기본적으로 제공되는 프로젝트를 실행한 화면이다. 2.2 안드로이드 포팅 Windows 환경에서 개발한 앱 프로그램을 안드로이드 앱으로 변환시키는 데는 많은 과정이 필 요하다. Cocod2d-x가 아이폰 ios용으로 개발되었기 때문이다. 또 개발 킷들이 버전마다 다르고 환 경 변수를 설정하는 것도 많으니 각자의 컴퓨터 환경에 따라 다른 결과가 나오는 경우가 많다. 일단 필요한 프로그램을 나열하자면 다음과 같다. 1) JDK 2) ADT 3) Android SDK 4) NDK 5) Python 6) Eclipse 다음으로 환경 변수를 설정해야 한다. 사용자 변수에 추가할 변수와 경로는 총 네 개가 있다. 나중에 Python을 이용해 빌드할 때 사 용되므로 변수의 이름을 바꿔서는 안 된다. “-“ 왼쪽이 변수 이름이고 오른쪽이 경로이다. 내용은 다음과 같다. 1) NDK_ROOT – “NDK가 설치된 디렉터 리”\android-ndk-r9d-windows-x86_64\android-ndk-r9d 2) ANDROID_SDK_ROOT –“SDK가 설치된 디렉터 리”\adt-bundle-windows-x86_64-20131030\adt-bundle-windows-x86_64-20131030\sdk 3) COCOS2DX_ROOT – “Cocos2d-x가 설치된 디렉터 리”\cocos2d-x-3.0beta2\cocos2d-x-3.0beta2 4) NDK_MODULE_PATH – “Cocos2d-x가 설치된 디렉터 리”\cocos2d-x-3.0beta2\cocos2d-x-3.0beta2\cocos\2d\platform\android 그림 2-9 그림 2-9는 환경변수를 설정해준 그림이다. 시스템 변수에는 Python, JDK, SDK의 경로 등 각자 프로그램을 설치할 때 기본적으로 설정해야 하는 경로만 추가해주면 된다. 보통 Path 변수 내에 “이미 있는 경로;추가 할 경로” 이렇게 세미 콜론으로 구분하여 하나씩 추 가해주면 된다. 환경 변수 설정까지 끝마쳤다면 이제 프로젝트를 빌드 할 준비가 어느 정도 끝난 것이다. 하 지만 아직 몇 가지 과정이 남았다. 이 문서에서 사용한 엔진은 3.0 beta2 버전인데 아직 beta버 전이라서 그런지 프로젝트를 생성하고 일부 파일이 생성된 프로젝트에 전달이 안 된다. 따라서 빌드할 때 에러가 나기 때문에 다음과 같은 작업을 해줘야 한다. “Cocos2d-x가 설치된 디렉터 리”\cocos2d-x-3.0beta2\cocos\2d\platform\android\java\src\org\cocos2dx에 있는 lib 폴더 를 복사한 후 “내 프로젝트가 있는 디렉터리” \proj.android\src\org\cocos2dx에 복사한 lib 폴더를 붙여 넣기 해준다. 그 다음으로는 “Cocos2d-x가 설치된 디렉터 리”\cocos2d-x-3.0beta2\cocos2d-x-3.0beta2\tools\project-creator\MyGame\proj.android\jni에 들어가 Android.mk 파일을 실행시킨다. 그림 2-10 그림 2-10과 같은 화면이 나타날 텐데 여기에 LOCAL_SRC_FILES := hellocpp/main.cpp \ ../../Classes/AppDelegate.cpp \ ../../Classes/HelloWorldScene.cpp 이 부분에 새로 추가한 cpp파일의 경로를 써주어야 한다. AppDelegate와 HelloWorldScene은 이미 내장된 cpp 파일로 만약 새로 추가한 cpp파일이 없다면 그냥 진행하면 된다. 여기까지 진행했다면 마지막으로 남은 것은 프로젝트를 빌드하는 것뿐이다. 아래와 같은 경로를 들어간 후 cmd창을 연다. “Cocos2d-x가 설치된 경로”\cocos2d-x-3.0beta2\cocos2d-x-3.0beta2\tools\project-creator\MyGame\proj.android 그 후 build_native.py 파일을 cmd창으로 끌고 온다. 그림 2-11 그림 2-11와 같은 화면이 나타나면 enter키를 눌러 실행시키면 빌드가 시작된다. 빌드가 끝난 후 이클립스로 들어가 package explore에서 오른쪽 버튼을 누른 후 import를 클릭 하면 그림 2-12와 같은 창이 나온다. 그림 2-12 Next를 누르고 그림 2-13과 같은 화면이 나오면 Root Directory에 Browse를 누른 후 “Cocos2d-x가 설치된 디렉터 리”\cocos2d-x-3.0beta2\cocos2d-x-3.0beta2\tools\project-creator\MyGame2\proj.android 이 디렉터리를 찾아 Browse한다. 그림 2-13 모든 작업이 끝나면 package explore에 새로 생성된 프로젝트가 보일 텐데 이 프로젝트를 Run As 탭의 Android Application으로 Run하면 된다. 마지막으로 아래의 디렉터리를 찾아가면 그림 2-14처럼 .apk 파일이 보일 것이다. “Cocos2d-x가 설치된 디렉터 리”\cocos2d-x-3.0beta2\cocos2d-x-3.0beta2\tools\project-creator\MyGame2\proj.android\bin 그림 2-14 이로써 안드로이드 포팅까지 해서 모든 과정은 끝이 난다. Chapter 3 Cocos2d-x 엔진 구조 분석 3.1 Cocos2d-x - 엔진 이란? Cocos2d가 원래 아이폰 용으로 제작된 엔진이기 때문에 윈도우에서 작업하고 안드로이드 앱으 로 포팅하는 과정이 만만치 않다. 정상적으로 포팅 하는데 까지 성공했다면 이미 Cocos2d-x의 반 은 알았다고 할 수 있다. 정도이다. 설치과정은 복잡했지만 정작 엔진을 사용하는 방법은 어렵지 않다. 그렇다면 ‘엔진’ 이란 애초에 무엇이고 왜 사용하는 것 인지 알면 좀 더 엔진을 쓰는데 흥 미를 가져볼 수 있다. 우리가 어떤 주인공과 적이 싸우고 있는 일련의 게임을 만든다고 생각해보 자. 주인공이 움직이는 경로가 있 고, 적이 싸우는 경로가 있다. 또한 주인공과 적이 싸울 때 움 직이는 속도와, 무기를 맞닿았을 때 에 임의의 이벤트를 호출하 는 게임을 만든다면, 누군가는 다음과 같은 생각을 할 수 있다. “적이 움직이는 함수를 모두 구 현해주어야 하는가?” 혹은 “충돌 시 나는 소리를 위해서 소리처 리 함수를 모두 구현해야 하는 가?” 정답은 엔진이 있다면 그럴 필요는 없다는 것이다. 게임에 그림 3-1 필요한 애니메이션, 움직임 처리, Device 터치, 이미지 파일의 처리 등이 클래스 화 되어 라이브 러리에 저장 되어 있고, 게임을 제작하는데 필수적인 기능을 담고 함수가 구현되어 있는 라이브 러리의 집합을 이른바 ‘엔진’이라고 할 수 있다. 이때 Cocos2d-x는 2D 게임을 만드는데 각종 라 이브러리를 담고 있는 ‘2D 게임엔진’ 인 것이다. 뒷부분에 게임을 직접 제작해보며 프로젝트를 분 석해보며, 자세한 함수를 분석하겠지만, 앞서 cocos2d-x 엔진의 구조를 파악 하여, 어떠한 원리로 게임 프로그래밍을 해야 하는지에 대해서 분석해보도록 하자. 그럼 먼저 엔진의 기본적인 클래스 구조를 살펴보도록 하자. 3.2 Cocos2d-x 엔진 – 기본 클래스 구조 기본 클래스 구조에 대해서 알아보자, 가장 핵심이 되는 클래스로써 cocos2d-x 엔진의 주축을 이 루는 Director, Scene, Layer, Node 들의 클래스에 대해서 분석해보자. 1)Director 클래스 게임을 만드는 개발 엔진 프로그램 cocos2d, 과연 어떻게 프로그램이 어떻게 관리되는 것인지 생각해볼 필요가 있다. 먼저 이해를 돕기 위해서 게임을 한편의 영화라고 생각을 해보자. 우리는 흔히 영화를 만들 때에는 ‘영화 감독’ 이 필요하다는 것을 알고 있다. 영 화를 기획하고 어떤 Scene을 넣고 어떻게 영화를 표현해 나갈 것인지 에 대한 일종 의 영화를 제작하는 총 관리자 역할을 하 는 것을 영화감독 이라고 지칭한다. 그림 3-2 cocos2d-x 는 이러한 ‘영화감독’을 대리자로 두어 Scene을 관리하게 된다. 클래스이름도 ‘제작 자’ 라는 의미에 Director 이다. 이러한 제작자 라는 대리인을 두어 이른바 Scene을 관리하는 cocos2d-x는 다음과 같은 계층도를 가지고 움직이게 된다, 오른쪽에 나오는 CCDirector 라는 명 칭은 cocos2d-x 2.x 버전에서 사용하는 클래스의 이름으로, 3.0 이상의 버전에서는 Director와 CCDirector 모두 사용할 수 있게 되어있다. 잠시 전체적인 개념을 말하자면 Director가 Scene을 관리해줄 수 있게 해준다는 것이다. 그림 3-3 다음으로 각각의 Scene 들은, 각각의 Layer들을 관리하며, 이러한 Layer 들은 각 씬(Scene) 에 여러 개가 있을 수 있게 된다. 이러한 Layer들은 또한 Sprite 라는 다양한 사진파일과 일명 ‘Node’ 들을 관리할 수 있는, 요소들을 관리할 수 있는 Sprite클래스로 나누어지게 되는 것이다. 자세한 설명은 이후 각 클래스별 내용정리에 기록하였다. 감독은 게임을 만드는데 배우, 촬영장소등과 같은 각종 요소를 선택한다. 마찬가지로 게임을 만 드는 데에도 필요한 각종 요소들이 있을 것이다. 예를 들어, 움직이는 캐릭터와, 움직이는 화면, 움직이는 미사일, 정적인 키패드 판 과 같은 것들을 각각 다른 틀에서 움직이게 되는데, 이런 것 들을 총괄적으로 관리해주는 역할을 해주는 것이 Director 클래스 인 것이다. 어떠한 프로그램을 만든다고 할 때에도 이러한 각종 모듈들을 정해놓은 후 모듈 간에 상호작용을 해주는 역할을 하 는 관리자를 만드는데, Cocos2d-x에서 는 Director 클래스가 해주게 되는 것이다. 실제 Cocos2d-x 엔진에서 Director 클래스에 관한 부분이다. 오른쪽에 보이는 함수들을 보면 대 부분 Scene을 관리하는 부분이 대부분이며, 함수에 관한 구현도 Scene을 인자로 받을 것 이라고 생각하였는데, 정말 인자를 Scene으로 받고 있었다. 이러한 Director는 하나의 객체를 공유하는 Singleton 기법으로 구현되어 있다. 이때 SingleTone 이라는 개념을 잠시 설명하자면, 시스템 내부에 특정 객체가 항상 하나만 존재하도록 만들어진 객체를 ‘싱글톤’ 객체라고 하게 된다. 객체가 하나만 존재하기 때문에, 객체가 가진 각종 멤버 변 수 데이터들은 일정한 방법과 패턴을 가지게 되며, 전역 변수처럼 활용하고 싶은 전역 객체가 있 을 때 이 방법을 사용 할 수 있으므로, 어떤 시스템의 유연성을 높일 수 있게 된다. 2)Scene 클래스 Scene(CCScene) 은 하나의 독립장면이라고 할 수 있다. 즉 Director가 관리하는 하나의 객체라 고 볼 수 있는 것이다. 모든 실행파일 및 앱은 Scene을 가지고 있는데, 사용자는 정해진 시간 동 안 오직 하나의 Scene만을 볼 수 있게 된다. 가령, 게임이 시작되면, Intro 부분이 있을 것이고, Menu를 선택하는 부분이 있을 것 이며, 게임이 진행되는 부분이 있을 것 인데, 이에 대해서, 다 루어지는 부분을, 각각의 Scene에서 관리하게 될 수 있는 것이다. Director를 통해서 유동적으로, 게임이 이기거나 질 경우, 다른 Scene으로 들어갈 수 있게 되는데, 이처럼 Scene은 연결하기 나 름이 된다. 다음의 그림은, 씬 들의 순서도를 대략적으로 나타낸 구조이다. 그림 3-4 cocos2d의 scene 객체는 레이어들을 갖게 되는데, Director 구조가 scene을 여러 개 갖는 것과 비슷한 원리로, 각각의 scene은 여러 개의, Layer를 갖고 있게 된다. 이러한 Layer들을 모두 겹친 것이 하나의 Scene 이라고 할 수 있다. 위 캡쳐 화면은 Scene 클래스를 찾아 캡쳐 해놓은 사진이다. Scene을 보면, 클래스가 Node를 상속받는 것을 알 수 있는데 이후 Node 개념은 뒷부분에 설명이 담겨져 있다 Scene 의 경우Transition 종류의 함수를 통해서 다른 Scene 객체를 함수인자와 함수 매개변수 로 받아 두 개의 Scene간에 화면 전환을 할 수 있다. 이 부분은 Director에 의해서 관리 되며, 위에서 설명하였으므로, 자세한 내용은 생략한다. Scene의 세부적인 부분은 뒤에서 설명하겠지만 Node 의 서브클래스 이기 때문에, Action을 사 용하거나, 수동으로 변형 시킬 수 있게 된다. Scene의 클래스 내부에서는 프로젝트에서 설명할 addChild() update() 와 같은 함수들이 virtual로 가상화 되어있는 것을 볼 수 있다. 가상화로 인해, Scene 클래스 자체에는 addChild() 와 update 와 같은 멤버함수들이 구현되어 있지는 않고, Layer 와 Sprite 부분에서 자세하게 구현되어 있을 것 이라는 것을 예측 할 수 있다. 3)Layer 클래스 다음으로, Layer는 전체화면에서 일정 영역을 갖게 되며, 어떻게 그려질지를 알고 그리게 되는 객체라고 할 수 있다. Scene은 일종의 논리적, 가상적 공간인 반면, Layer는 모니터에 그려지는 백 지이다. Sprite, Menuitem, Label 등 Node들을 가질 수 있다. 실제 프로젝트를 만들 때 가장 많은 시간을 할애하는 부분이기도 한다. 조금 있다가 살펴볼 테지만 보통 class를 만들고 Layer 클래스 를 상속하여 Layer 클래스의 Init() 함수를 오버라이딩한다. 그리고 만든 class의 Init() 함수 내부를 수정하는 식으로 프로젝트가 진행되게 된다. Scene 하나에 Layer 하나를 두는 게 일반적이지만 필 요에 따라 여러 개의 Layer를 두기도 한다. Layer는 반투명, 즉 일정부분만을 보이거나 보이지 않 게 할 수 있다. 이러한 Layer는 외형과 행동을 정의하고 있어, cocos2d 프로그래머들은 Layer를 코딩하는데 대부분 시간을 사용하게 되는 것이다. 그림 3-5 Layer는 이벤트 핸들러를 정의하고 사용하게 되는데, 상위에 있는 Layer에서 아래 Layer로 어떤 Layer가 이벤트를 인식하고 처리할 때까지 계속해서 전달되게 된다. 그리고 개발하다 보면, Layer 를 커스터마이징할 때가 필요한데, cocos2d에서 미리 정의된 유용한 Layer를 제공한다. 간단한 메 뉴를 제공하는 Menu 클래스가 있고, 색을 자유자재로 조절할 수 있는 ColorLayer가 있다. Layer는 Sprite 객체와 Label 객체, Layer 객체까지 담을 수 있게 된다. 계속해서 나오게 되는 개념이지만, 하나의 Scene 안에 다중 Layer 의 층이 있다는 것을 유념해 두어야한다. 이에 맞추어 코딩을 해주어야 하기 때문이다. 다음은 Layer 클래스의 캡쳐본이다. Layer는 Node의 서브클래스 이기 때문에, Action을 사용하거나 수동으로 변형 할 수 있다. 뒤에 서 잠시 다루겠지만, Layer에서 터치 이벤트를 처리하게 된다. TouchBegan, TouchBegan 등 같은 함수에 사용자의 동작에 반응하게 된다. 터치 이벤트가 발생하면 씬 안에 존재하는 모든 레이어 에게 이벤트가 전달이 되는데 이중 특정 레이어를 선택해 주는 것이 프로그래머의 역할이다. Layer 클래스 단위로 코딩해주어 할 부분은 무척이나 많다. 위에서도 말하였지만, 게임프로그래 밍의 60~70% 가 Layer 단위로 코딩 한다고 하여도 과언이 아니다. 메뉴를 만들고, 각각 메뉴에 대한 이벤트를 지정해주고, 또한 배경과 각각의 이미지 객체, 바로 뒤에서 배우게 될 Node 들을 모두 코딩 해주는 부분이 Layer 클래스 이다. 또한 각종 충돌, 액션 에 관한 함수도 Layer에서 해주는데, 이유는 충돌이라는 액션에 필요한 개체들도 결국 Layer에 포함되는 Sprite 들이기 때문이다. 위와 같은 특성 때문에 Layer 함수는 게 임 프로그래밍뿐만 아니라 cocos2d-x에서 중요한 기능을 가지고 있는데, LayerClipping 방법과, Touch동작에 대한 분석을 심화부분에서 다루어 보도록 한다. 3)Node클래스 이에 관하여 지금까지는 가장 큰 형태의 클래스 Director, Scene, Layer 클래스에 대해서 알아보았다. 각 클래 스는, 상속관계를 항상 직접적으로 갖고 있다고 볼 수는 없다. 이러한 크게 5가지의 틀로 프로그 램이 진행되게 되며, 각 요소들을 다루는 라이브러리의 각 함수와 전환을 통해서 게임을 진행 시 킬 수 있는 것이다. Scene 이라는 각 Scene 들이 Director 에 의해 전환이 관리된다면 각 Scene, Sprite 들을 총괄적으로 관리하여 주는 클래스는 없을까? cocos2d-x에서는 이러한 개념을 Node 라는 클래스를 통해서 설명 된다 그림 3-6 Node는 일반적으로 화면에 보이는 그림과 같은 객체를 대부분 CCNode 라고 한다. 통상적으로 Node라고 칭한다. CCScene, CCLayer, CCSprite 등은 모두 CCNode를 상속받고 있으므로, 이들을 Node 라고 할 수 있다. 위에서 참조한다 라는 표현을 사용했지만, 이는 '붙는다'라고 이해 할 수 있다. CCLayer에 계속해서 Node를 붙여나감으로써 Layer를 완성해 나가는 것이다. Node가 클래 스적으로는 Scene, Layer 클래스들을 상속하고 있지만 실제로는 Layer안에 있는 것들이라고 할 수 있다. 이러한 Node는 Cocos2d-x에 최상위 클래스이다( 위치, 색상, 투명도, 크기, 회전,가시성 여 부, 카메라, 그리드 등을 다 담고 있다. 자식노드를 가질 수 있다. 다른 노드에 대해서 컨테이너처 럼 사용하는 것이 가능하며, 객체들의 계층구조를 만들 필요가 있을 때 유용하다. 또한 스케줄러 함수를 지니고 있어 일종의 스케줄을 관리해주는 역할을 할 수 있게 된다. 먼저 Node에서 가장 많이쓰이는 Sprite 클래스는, 이동, 회전 크기조절 애니메이션 등을 2D 로 줄 수 있는 이미지이다. 각각의 Sprite는 다른 Sprite를 자식으로 가질 수 있는데 부모클래스 Sprite가 변형되면, 모든 자신 Sprite 들도 변형된다. Sprite도 역시 Node 서브 클래스 이기 때문 에, Action을 사용하거나 수동으로 변형 할 수 있게 된다. 다음으로 이러한 Node들의 움직임, 동 선등을 관리하는 Action 클래스는 CCNode 객체에 어떠한 명령을 내리는 클래스 이다. 이는 보통 회전, 위치시키기, scale 등 과 같은 객체의 속성을 변경시켜 주는 것이다. 그리고 일정한 시간 동 안, 이러한 속성들을, 변경시키고 싶다면, IntervalAction() 이라는 함수를 호출 할 수 있다. 반대로 속성을 변경시키고 싶으면, InstantAction을 사용할 수 있다. 이제 결론적인 이해를 담기 위해서 MoonWarriors 라는 cocos2d-x의 기본 프로그램에 대한, 프로 그램 구조를 간단하게 Diagram을 통해서 그려보았다. 그림 3-7 Director라는 Singletone 객체를 하나 두며, Director는 Scene을 2개를 갖게 된다. 각각의 Scene 은 Menu 와 Game 진행으로 나뉘게 되며, Menu는 하나의 Layer를 갖게 되며, 각 Layer 는 여러 가지의 Sprite를 가지며, Menu와같은 Costomize 객체를 갖기도 한다. GameScene에서 Layer 들은, Sprite들을 가지는데 이에 해당하는 것은 아군기, 적기, 총알 등이 된다. 3.3 Cocos2d-x 심화 LayerClipping cocos2d-x 로 게임을 만들어가면서 여러 가지 함수를 찾아보고, 실행해보던 중에, 이미지 확대 라는 기능에 대해서 문득 떠올랐다. “이미지를 확대 해주는 방법은 없을까?”, 다른 화면에서 일정 부분만 보일 수 있는 방법은 없는 걸까? 라는 고민을 문득 해보았다. 가령, FPS게임을 생각해보면, 저격총을 쏠 때 확대를 하면, 일정 부분만 확대하여 보여 지게 되며, 이외의 화면은 걸러지게 된 다거나 일정 게임이 진행되고 있는 다른 곳의 상황을 작은 화면으로 보여주는 기법에 대해서 생 각해보았다. 실제로 이는 게임에서 ‘Clipping’ 이라는 개념으로 사용되고 있으며, 보여지는 부분을 일정한 틀에 의하여 조각내어 볼 수 있는 기능을 하는 개념이었다. 그림 3-8 위와 같은 방법으로 Clippingwindow 라는 틀에 의해서 P1, P2, P3, P4, P5, P6, P7을 차례대로 연 결한 물체를 하나씩 점점 잘라 냄으로써, 틀에 의한 부분만 보여 질 수 있는 원리였다. 모든 게임 분야에서 이러한 Clipping 기법이 사용되며, 특히 3D 분야에서 많이 사용되는 기법이었다. 이와 같은 생각으로 cocos2d-x 내부에서 찾아본 결과 clipping을 구현할 수 있는 부분이 있었다. 이는 ClippingNode 라는 class로, 이에 해당하는 부분이 clip 이라는 객체를 만들어 clip 이라는 부분이 Layer Clipping 화 시켜주는 원리였다. 이제 Clipping을 하는 소스코드를 만들어보자. 먼저 Clip을 해주기 위해서는 기준점이 필요한데, 이번 분석에서는 분석을 일반화시키기 위해 다각형 형태인 ‘Polygon’ 의 원리로 Layer를 Clipping 해보는 방법을 분석하도록 한다. 주어진 파일은 기 본파일 HelloWorld.png 파일을 이용하여 실험해 보았다. 먼저 첫 번째 부분을 보자. 위의 코드를 먼저 분석해보면, DrawNode 라는 ClippingShape 라는 변수를 하나 만들어준다 이는 create() 함수를 통해 실체화 되어 만들어 지게 된다. 다음으로 Point를 담는 배열 3가지를 지정해주었는데, 3개를 담는 이유는 기준점을 3개잡기 위함 이다. 이번에 구현하기 위한 코드는 창의 가운데 아랫부분, 오른쪽 위, 왼쪽 위 부분을 기준점으 로 잡고 Clipping 화 시킬 것 이므로, 그에 대한 기준점을 각각 [0],[1],[2] 로 잡아준 것이다. 위의 코드를 보면 알 수 있는데 visiblesize 란 Layer의 이름을 말하며, 첫 변수는 기준점 origin (x,y) 로 부터 가로의 반만큼 더해주고, y축으로는 더해주지 않았다. 이를 생각해보면, 밑면의 가운데 부분 을 의미 하게 된다. 다음 변수로 는 기준점에 각각 그림의 width, height를 모두 더함으로써 오른 쪽 위의 좌표를 의미 하게 된다. 마지막 pts[2] 부분도 왼쪽 위를 의미하는 것을 알 수있다. 이제 이렇게 저장된 변수들을 어떻게 처리해줄 것인가? 위와 같은 형태로 drawPoloygon 이라는 함수로 보내준다. pts는 배열의 주소를 넘겨준 것이며, 뒤에 3의 경우, 배열의 크기를, 나머지 값들은 Clipping 된 후에 처리색깔을 담은 것이다. 이는 ClippingShape 변수로부터 호출 될 수 있는데, ClippingShape가 위에서 본 것과 같이 DrawNode 클래스 변수로 해주었기 때문이다. 그렇다면 먼저 DrawNode가 어떻게 생겼는지 찾아보자. 위와 같이 Node 클래스를 상속받고, 그에 따라서 각종 draw종류의 함수들이 있는 것을 알 수 있 다. 각각의 함수들은 Point 와 Color 같은 타입의 매개변수들을 인자로부터 전달 받게 된다. 우리 가 구현하고자 했던 부분은 drawPolygon 이므로, drawPolygon 으로 들어가보자. drawPolygon 구현에 관한 소스코드이다. typedef로 인해 다소 복잡한 부분이 있지만, 할 수 있는 한 분석을 시도해 보았다. 먼저 주목해서 볼 부분은 매개변수의 Point *verts 의 부분이다. 그에 따른 부분으로 count는 verts의 크기를 의미하는 것 같았다. 함수가 시작되면 구조체 ExtrudeVerts를 만들고, 구조체 안에 는 Vertex2F 와 offset, n을 담아주었다. 다음으로 extrude 라는 구조체형 포인터 객체를 만들어 동적 할당을 해주는데 이때 동적 할당의 사이즈는 count 즉 verts 의 크기이다. 여기서 ExtrudeVerts 라는 것은 함수로 전달 받기 전, pts 들의 새로운 정보를 담는 구조체라고 생각해 볼 수 있었다. memset을 통해서 일단은 먼저 extrude 부분을 0으로 set 해준 것을 알 수 있다. 다음으로 이제 for문을 돌며, 새로운 정보를 추출해낸다 먼저 Vertex2F라는 변수는 생소하기 때문 에, 무슨 의미를 하는지 찾아보았다. 위에 보았던 사진처럼, 그저 structure 안에 float 형 변수2가지를 만들어 준 것을 볼 수 있었다. GLfloat 은 별다른 의미 없이 float을 typedef 해준 것이다. OpenGL에 연동하기 위해서 형 변환 해준 것이라고 추측되었다. 이제 다시 반복문으로 돌아와 drawPolygon 함수를 분석해보자.각각의 verts 값들은 for문이 돌 때마다 v0, v1, v2 값이 달라지게 된다 I= 0 일 때 v0 ,v1, v2 는 verts[2] , verts[1], vert[0] I= 1일 때 v0 ,v1, v2 는 verts[0] , verts[1], vert[2] I= 2 일 때 v0 ,v1, v2 는 verts[1] , verts[2], vert[0] 으로 분석 될 수 있었다. 이는 다음 n1,n2 라는 변수는 normallize 함수를 이용한 것을 볼 수 있 는데 정규화시키는 것, 즉 하나의 v1,v0 점을 잇는 하나의 vertor를 만들어주는 것이다. 즉 점이 3 개이므로 V자 모양으로 vertor를 2개를 생성해주는 셈이었다. 이후 offset 변수에 두 vertor를 더 해준 값과 내적 해준 값을 전달 해주었는데, 왜 offset 이었는지는, 생각해내기가 힘들었다. 이렇 게 두 vertor를 구현한 후에, border 부분과 fill 부분으로 나누어 색깔을 넣어주게 되는데, 이에 관 한 소스코드 역시 위에서 구현한 vertor 들을 기준으로, for문을 돌면서, 색깔을 한 Point 단위로 찍어주는 구조라고 예측되었다. 위에서 보는 것에 borderColor 에 관한 부분은 색깔을 지정해주기 위해서 함수의 매개변수로 건 너 받은 값이며, Color4B라는 것은 색깔을 담당해주는 클래스이다. 시뮬레이션에서 넣어준값은 black 인 검정색을 넣어주었다. 이제 다음의 함수가 drawPolygon 함수가 return 된 후에, 실행되 는 부분에 대하여 다시 분석을 시작하도록 하자. 앞서서 분석했던 코드는 일종의 Shape 라는 모양을 만드는 것 이였다면, 이제 모양을 Clip에 넣 어 실제로 잘라볼 차례이다. ClippingNode 라는 clip을 하나 만들게 된다. 마찬가지로 create를 통 해서 실제적 변수를 채워 넣어 주었다. 이제 clip의 위치를 정할 차례이다. setAnchorPoint 라는 함수를 통해, 화면의 정중앙의 위치하는 0.5, 0.5 지점에 point를 잡아주었다. setAnchorPoint 와 cocos2d-x 좌표계에 대한 설명은 뒷 프로젝트에서 하도록 한다. 마찬가지로 setPosition으로 기준 점을 정해주었다. 그리고 가장 중요한 함수인 ‘setStencil’ 이라는 함수이다. 이는 앞서 만들었던 ClippingShape를 매개변수로 받게 된다. 이를 매개변수로 받게 되면 결과적으로 ClippingShape 모양 clip이 만들어진다. 이제 마지막 부분에 this->addchild(clip)을 통해서 실제적으로 화면에 clip 을 추가 시키도록 하였다. 주어진 Clip에 Sprite를 띄워주면 Clip모양의 Sprite가 출력이 된다. 왼쪽에 보는 그림이 기존의 기본출력이라면, 오른쪽의 화면이 기존에 화면에서 Clipping 하여 출력된 결과이다 3.3 Cocos2d-x 심화 Touch System과 Message Loop 개념분석 게임 프로그래밍에서 Touch란 매우 중요하다. 사용자의 어떤 일종의 ‘요구’ 이며, 게임에서는 이 러한 ‘요구’에 ‘응답’을 하게 된다. User 와의 상호관계가 매우 중요한 게임 프로그래밍에서 Touch 란 매우 중요한 점이기 때문에, 이번 3.3절 에서는 ‘게임 프로그래밍’과는 다소 거리가 있다 하더 라도 이 부분에 대해서 평소에 궁금하였던 이론적인 부분까지 첨가하여, 완벽히 알아보고자 한다. cocos2d-x 로 게임 프로젝트를 하나씩 만들어보며, 이런 생각을 하였다. “화면 혹은 마우스를 클 릭할 때, 게임 요소들이 변하게 되는데 , 무엇이 Argument인 걸까?” 그 동안 배웠던 프로그래밍 에 있어서 어떤 함수를 호출 한다는 건 메인함수에서 순차적으로 진행이 되다가, 값이 나와 그 값이 함수로 들어가게 때문이었다. 하지만 cocos2d-x 에서의 main 구조를 잠시 살펴보자 아무런 함수의 call 이 없었다. 예제 시연 분석에서 다루겠지만, init 함수로 들어가서 일련의 초기 화 함수들이 진행된다는 점이었다. 하지만 touch는 분명히 함수였고 호출을 한다는 명시는 어디 에도 없었다. 그에 대해서 찾아본 결과 그 이유는 Message Loop 였다. Cocos2d-x 또한 대부분의 게임이 그러하듯 API(Appliction Program Interface) 들이 내부에 구현 되어있다. API의 정의로는 프로그램 또는 애플리케이션이 운영 체제에 어떤 처리를 위해서 호출 할 수 있는 함수의 집합이다. 윈도우 API 의 경우 C, C++, 파스칼 등과 같은 언어에서 윈도우를 만들고, 파일을 여는 것과 같은 처리를 할 수 있도록 1000여개 이상에 함수로 구성되어 있다. 명 령어의 집합으로 애플리케이션 프로그램에서 오퍼레이팅 시스템의 기본적인 기능을 사용할 필요 가 있을 때에 여기에서 명령어를 호출하게 된다. (참조: 네이버 백과사전 ‘API’) 여기서 말하는 각종 명령어들은 MS-DOS에서 시스템 콜에 해당하는 부분이다. 즉, 시스템 콜 함수를 불러, Interface 해주는 형태가 되는 것이다. API를 만들고 유용하게 만들게 되면 각종 Device를 새롭고, 자유롭게 사용 할 수 있게 된다. 이와 같이 cocos2d-x 역시 window에서 c++을 통해 cocos2d-x를 사용할 경우 기본적으로 윈도 우 창에서 돌아가는 원리를 따르기 때문에, 기본적으로 API 체제이다. 이번 절에서는 cocos2d-x 프로그래밍을 하면서 궁금하였던, API구조의 ‘Message Loop’ 라는 개념에 대해서만 간단히 분석해 보도록 한다. 이는 Windows API 가 돌아가게 되는 가장 기본적이고, 핵심적인 원리로써, 가장 중 요한 요소이다. MessageLoop 에 대한 이해에 앞서, 프로세스 라는 개념이 필요하므로, 간단히 설명하도록 한 다. 그림 3-9 그림 3-10 위에 보여지는 형태는 Process 내에서 존재하는 Thread(이하.쓰레드) 라는 개념이다. 시간이 흐 르고 있을 때 CPU가 처리하는 Procedure의 단위를 말하게 된다. 이러한 쓰레드 게임 프로그래밍에 있어서 매우 중요하게 다루어진다. 애초에 온라인 게임 상에 서 User들을 server로 받을 때 유저를 한 명 처리하고 남은 한 명을 처리한다거나 하면 게임이 진행이 될 수 없기에, 멀티 프로세스 기법을 이용한다. 이처럼 OS는 Process를 다중으로 생성하 여, 각각의 처리를 돌릴 수 있다. 쉽게 말해 프로그램을 여러 개 돌릴 수 있다는 의미로 해석될 수 있다. Process 안에 두 개의 쓰레드가 있다고 할 때 첫 번째 쓰레드에 무한루프가 발생하여, 응답 없 음이 떠도 프로세스가 다운되는 것이 아니라 다른 하나의 Thread가 문제없이 실행되면 작업에 대 한 결과를 출력할 수 있게 된다. 실제로 API 프로그램에서는 WinMain 이라는 함수와 WndProc 이라는 함수가 동시에 돌아가게 되는데 WinMain 은 실제 콘솔 창에서 main 함수와 같은 역할을 하며, 프로그램의 창을 띄우기 위한 순차적인 코드를 의미 하게 된다. 하지만 WndProc 이라는 윈 도우 프로시져로 윈도우에서 발생하는 메시지를 처리하는 부분은 다르다. 실질적으로 모든 API 와 운영체제 간에 상호작용은 WndProc에서 하는 것이다. 이번 절에서는 API에 관한 깊은 설명보 다는, API체제의 Message Loop 가 어떻게 돌아가는지에 대한 설명을 하기 위함이므로, WinMain 에 대한 자세한 설명을 생략 하도록 한다. 메시지 구동시스템(Message Driven System) 이라고 하 며 이점이 도스와 가장 뚜렷한 대비를 특징이 된다. 도스에서는 프로그래머에 의해 미리 입력된 일련의 명령들을 순서대로 실행하는 순차적 실행방법을 사용한다. 윈도우즈는 이와 다르게 프로 그램의 실행순서가 명확하게 정해져 있지 않으며, 상황에 따라 실행 순서가 달라지는데 여기서 말하는 상황이란 어떤 메세지가 주어졌는가를 말한다. 메시지란 사용자나 시스템 동작에 의해 발 생된 일체의 변화에 대한 정보를 말한다. 예를 들어 사용자가 마우스의 버튼을 눌렀다거나 키보 드를 눌렀다거나 윈도우가 최소화 되었다거나 하는 변화에 대한 정보들의 메시지 인 것이다. 메 시지가 발생하면 프로그램에서는 메시지가 어떠한 정보를 담고 있는가를 분석하여 어떤 루틴을 호출할 것인지를 결정한다. 즉 순서를 따르지 않고, 주어진 메시지에 대한 반응을 정의하는 방식 으로 프로그램이 실행된다.(출처- Win32 API 연구 사이트) 그림 3-11 위와 같이, 메시지 구동시스 템 이라는 것에 대해 깊게 생각 해볼 필요가 있다. 왼쪽에 나오는 MessageLoop 철저하게 MessgaLoop 그림은 개념에 대해서 설명하고 있다. 함수는 일정주기 로 계속해서 돌면서 Message Queue 에 있는 부분을 캐시해 와 되는 callback함수를 것이다. 호출시키게 이것이 바로 Message Loop System 구조이다. 메시지는 크게 메시지 큐로 들어가는 큐 메시지와 큐에 들어가지 않고 곧바로 윈도우 프로시저 로 보내지는 비큐 메시지로 구분된다. 큐 메시지는 주로 사용자의 입력으로부터 발생되는데, 큐 메시지는 발생 직후 시스템 메시지 큐에 저장되어 스레드 메시지큐로 보내져 최종적으로 윈도우 프로시저에 의해 입력된 순서대로 처리되게 되는 것이다. 큐메시지는 입력된 순서대로 큐에 쌓여 있다가 차례대로 처리된다는 점이 있는데, 입력된 키를 바로 처리하지 못하는 일이 종종 발생할 때 처리하지 못한 키를 대기시키기 위한 완충장치로 메시지 큐가 존재하는 것이다. 운영체제는 하나의 시스템 메시지 큐를 관리하여 또한 각 스레드별로 하나씩 메시지 큐를 생성 하게 된다. 시스템 메시지 큐는 시스템 전체에 유일한 메시지이며, 사용자가 마우스를 조작하거나 키보드를 두드리면, 이 입력은 디바이스 드라이버에 의해 메시지로 변환되어 시스템 메시지 큐에 넣어지게 되는 것이다.(출처-http://printf.egloos.com/ ‘프로시저’) 위와 같은 MessageLoop개념이 cocos2d-x에서만 작동 하는 것이 아니라 각종 프로그램에서 적 용된다. 이는 cocos2d-x 내부에 MessageLoop 가 내부에 존재하여, 구현되어 있을 것이다. 게임프 로그래밍과 직접적으로 관련이 있는 것은 아니지만, touch 라는 개념에 대한 배경지식이 넓어 질 수 있다고 생각된다. 이제 본격적으로 cocos2d-x에서 사용되는 touch에 대해서 알아보자. 먼저 Cocos2d-x에서 사용 되는 터치 종류를 크게 표준 터치와 타켓 터치로 나누어 볼 수 있다. 1) 표준 터치 이벤트 방식 보다 저 수준의 터치 의 호출 혹은 이벤트에 대하여 프로그램이 받는 방식이다. Chapter 4 예제 분석 시연으로 쓰일 프로젝트는 아주 간단하지만 꼭 필요한 기능들을 담고 있다. 일단 씬을 두 개로 나눠 메뉴를 선택하는 StartScene과 게임을 직접 플레이하는 GameScene을 구분하였다. StartScene에서 메뉴를 선택함으로써 GameScene으로 이동할 수 있다. GameScene에서는 Sprite, Action, Touch등을 구현하여 케릭터가 화면에 보여지고 touch를 통해 이동하게 된다. 이 프로젝트 를 통해 소스 코드의 기본적인 진행 흐름과 개발 방법을 알 수 있다. 우선 클래스들을 살펴보자. 그림 4-1 4.1 Main 헤더파일과 소스파일들을 살펴보면 그림 4-1과 같다. Win32 폴더에 main.cpp와 main.h가 있고 Classes 폴더 안에는 AppDelegate.h와 AppDelegate.cpp, StartScene.h와 StartScene.cpp 그리고 GameScene.h와 GameScene.cpp가 있다. Main과 AppDelegate는 보통 프로젝트를 생성할 때 제공해주는 대로 사용하면 된다. 이번 프로 젝트에서도 몇 가지 설정을 바꾸고 기본적으로 생기는 주석을 제외한 부분 외에는 건들지 않았다. 주의 깊게 봐야 할 부분은 StartScene와 GameScene이다. StartScene은 게임이 처음 시작된 후 ‘Start’ ‘Exit’ 메뉴를 선택하는 씬이고 GameScene은 본격적으로 게임이 실행되는 StartScene에서 GameScene으로 넘어갈 수 있다. 이제 하나 하나 천천히 분석해보자. 씬이다. 일단 가장 먼저 실행되는 main 함수이다. 그림 4-2 그림 4-2는 main.h 파일이다. 라이브러리를 include하는 것 외에는 별로 하는 것이 없다. 라인 9에 CCStdC.h를 include 하였다. 아까 설명했듯이 Cocos2d-x 엔진은 내부 헤더 파일이나 클래스 에 접두어로 Cocos의 약자인 CC를 붙인다. 즉 CC 접두어 유무를 보고 이것이 엔진 내부 클래스 인지 아닌지를 쉽게 알 수 있다. 그림 4-3 그림 4-3은 main.cpp 파일이다. 라인 1~2: main.h과 AppDelegate.h를 include하였다. 라인 4: USING_NS_CC는 내부적으로 using namespace cocos2d를 define한 형태이다. Cocos2d-x 의 대부분의 파일들은 cocos2d라는 namespace안에 들어있기 때문에 이 문장을 종종 사용하곤 한다. 라인 7~19: 처음 프로젝트를 생성할 때 원래 쓰여있는 코드지만 CCLog라는 함수를 통해 콘솔 창에 문장을 출력하려면 이 코드들을 지워야 한다. 그 대신 라인 22~28을 추가한다. 라인 22~28: 가장 먼저 실행되는 메인 함수이다. AppDelegate와 EGLView 클래스의 객체를 만 들었다. EGLView의 객체는 init 함수를 호출하여 윈도우 창을 띄운다. 그리고 Application의 객체는 run 함수를 호출하고 run 함수는 뒤에 나올 applicationDidFinishLaunching이라는 함수를 호출한 다. 여기서 AppDelegate는 CCAplication이라는 클래스를 상속 받은 클래스로 앱의 실행과 종료 혹 은 중단을 제어한다. EGLView 클래스는 인자를 보고도 알 수 있듯이 생성될 윈도우 창의 이름과 크기를 설정해준다. 역시 single ton 클래스이다. *위에서 CCLog에 대해서 언급했었다. CCLog란 콘솔 창에 문장을 출력해주는 함수인데 디버깅하 거나 오류를 찾아 줄 때 꼭 필요한 함수이다. 그러나 라인 7~19를 그대로 두고 코딩을 하게 되면 콘솔 창 없이 윈도우 창만 뜨게 되어 CCLog를 사용해도 콘솔 창에 나올 문장을 볼 수 없게 된다. 따라서 라인 7~19 대신에 라인 22~28에 있는 메인 함수를 쓰는 것이다. 하지만 또 한가지로 콘 솔 창을 뜨게 하려면 그림 4-4와 같은 작업을 추가로 해주어야 한다. 그림 4-4 프로젝트의 속성에서 그림 4-4와 같은 경로에 들어가 ‘하위 시스템’을 ‘콘솔(/SUBSYSTEM: CONSOLE)로 바꿔주어야 한다. 4.2 AppDelegate 그림 4-5 그림 4-5는 AppDelegate.h이다. 라인 6: AppDelegate라는 클래스를 정의하는데 AppDelegate는 cocos2d namespace 안에 Application이라는 클래스를 상속받았다. 라인 9~10: 생성자와 소멸자 함수이다. 라인 12: applicationDidfinishLaunching()이라는 함수이다. AppDelegate는 Application을 상속받 고 Application은 ApplicationProtocal을 상속받는다. applicationDidfinishLaunching()이라는 함수는 ApplicationProtocal이라는 클래스에 순수 가상 함수로 정의되어있다. 라인 14와 16에 있는 함수 들 역시 마찬가지이다. virtual이라는 키워드는 안 써도 상관없지만 명시적으로 써주었다. 이 함수 는 app이 실행될 때 호출된다. 아까 메인 함수에서 Application의 run 함수를 통해 호출하였다. 내부 정의는 cpp 파일에서 해주었다. 라인 14: applicationDidEnterBackground() 함수는 전화가 오거나 홈 키를 눌러 앱이 중단될 때 호출되는 함수이다. 라인 16: applicationWillEnterForeground() 함수는 앱이 다시 실행될 때 호출되는 함수이다. 다음은 AppDelegate.cpp이다 그림 4-6 라인 17~18: Director와 EGLView 모두 single ton 클래스이다. Director는 Object 클래스를 상속 받은 클래스이다. 말 그대로 관리자로써 게임 씬, 프레임, 터치, 윈도우 창 등 많은 것을 관리한다. 왼쪽에 보면 auto라는 키워드를 볼 수 있다. 이 키워드는 대입연산자 오른쪽의 데이터 타입에 따 라 알아서 데이터 타입을 정해준다. Director::getInstance()는 single ton 패턴으로 제작 되었으므로 Director 포인터 타입을 반환하게 되는데 auto라는 키워드가 알아서 director 변수를 Director*로 바꿔준다. 어떤 형도 될 수 있다는 점에서 Void*와 비슷한 개념이다. 역시 auto 키워드로 정의한 eglView 역시 EGLView* 타입으로 정의된다. 라인 20: cocos2d-x는 OpenGL을 기반으로 제작된 엔진이다. 라인 22: setDisplayStats() 함수는 프레임 숫자 등 개발할 때 필요한 정보를 보여줄지 말지 설정 하는 함수이다. 인자로 true를 넘기면 정보가 보여진다. 개발이 끝난 후에는 false로 해주어 프레 임이 보이지 않도록 한다. 라인 24: setAnimationInterval() 함수는 인자로 프레임 양을 결정해준다. 1/60을 보내주면 1초에 60프레임이 재생된다. 라인 26: StartScene 클래스의 createScene() 이라는 함수를 호출해서 scene이라는 변수에 저장 한다. createScene()은 직접 제작한 함수로 cocos2d namespace의 Scene*를 리턴값으로 갖는다. createScene() 함수에서 다음 내용이 진행된다. 뒤에 나올 StartScene 클래스의 createScene() 함수 에서 진행될 내용이 꽤 길으므로 프로젝트의 진행 위치를 주의하길 바란다. 라인 28: runWithScene() 이라는 함수는 화면에 어떤 Scene을 보여줄지 정하는 함수이다. Director가 Scene을 관리하기 때문에 아까 만든 Director의 포인터 객체로 접근하였다. 인자값으로 아까 생성한 scene이라는 변수를 사용했으므로 StartScene의 씬이 보여진다. applicationDidFinishLaunching() 함수안에서 호출 됐으므로 앱이 실행되면 가장 먼저 이 씬이 보 여진다. 라인 30: 함수 호출이 성공했다는 의미로 true를 반환한다. 라인 35: stopAnimation()이라는 함수는 메인 루프를 멈추어서 더 이상 아무것도 그려지지 않게 된다. 라인 40: startAnimation()이라는 함수는 다시 메인 루프를 활성화시켜 이전에 저장되었던 프레 임 수에 맞게 씬을 그려준다. 4.3 StartScene 아까 StartScene 클래스의 createScene()이라는 함수를 호출했었다. 그러므로 다음에 살펴볼 파일 은 StartScene.h이다. StartScene의 주 역할은 배경과 Menu등을 그려내고 Start 메뉴 버튼을 누르 면 GameScene으로 이동시켜 주는 것이다. 그림 4-7 라인 5: 클래스를 생성하며 cocos2d namespace의 Layer 클래스를 상속받았다. 라인 9: 객체 없이 접근 가능한 static 함수 createScene()을 만들었다. 라인 11: 프로젝트의 거의 모든 내용이 들어있는 init() 함수이다. 가상 함수로 재정의하여 사용 한다. 대부분의 게임 로직은 이 함수 내에서 정의된다. 라인 13: 매크로이며 하는 일은 클래스의 오버라이딩된 create() 함수가 호출되면 역시 오버라 이딩된 init()함수를 호출한다. 라인 17~19: 메뉴를 누르면 호출되는 콜백 함수들을 선언한다. 다음은 StartScene.cpp이다. 그림 4-8 라인 8: auto 변수 타입으로 scene이라는 변수를 만들고 Scene클래스의 static 가상 함수 create() 함수를 호출하여 리턴 값을 대입하였다. Scene 클래스의 Create() 함수는 리턴 값이 Scene*이다. Cocos2d-x 에서는 single ton 클래스가 아닌 객체를 만들 때 대부분 이렇게 만든다. 앞으로 많이 사용하게 될 sprite, layer, action 등등이 모두 저 방식을 따르게 된다. Create() 함수는 그려지는 물체들의 최상위 클래스인 Node에서 처음 가상함수로 정의된 것으로 말 그대로 객체를 생성 하는 역할을 하게 된다. 내부적으로는 Layer 클래스의 static 함수 init()를 호출한다. 라인 10: 제대로 Scene이 만들어졌는지 오류 처리가 끝나고 나면 라인 10으로 가서 layer 객체 를 만든다. StartScene이 Layer 클래스를 상속받았으므로 layer 변수의 타입은 Layer*가 된다. 아까 설명했다시피 StartScene 클래스의 create 함수를 만나면 프로그램의 흐름이 CREATE_FUNC으로 넘어가서 StartScene의 init()함수를 호출한다. 라인 19~22: Layer의 init()를 호출하고 실패했으면 false를 리턴 라인 24~25: Cocos2d-x에서는 사용자의 편의를 의해 x와 y 값을 갖고 있는 Size 클래스와 Point 클래스를 정의해 놓았다. Director를 통해 Window 창의 크기와 원점을 얻어 사용한다. 라인 29~34: Sprite를 생성하는 내용이다. Create() 함수에 png 파일 이름을 인자 값으로 줌으로 써 파일을 사용했다. Cocos2d-x 에서 리소스를 사용하는 방법은 매우 간단하다. 일단 프로젝트가 생성된 폴더로 가서 Resource 폴더를 들어간다. 그 후 사용할 파일들을 옮겨 준다. 그림 4-9 그 다음으로는 아까 생성한 Sprite* 변수를 이용하여 setPosition() 함수를 호출함으로써 윈도우에 출력될 위치를 정해준다. Point(50,0)를 인자로 주었다. 약간 오른쪽에 위치하게 된다. Cocos2d-x에 서 사용되는 좌표계는 그림 4-10과 같다. 그림 4-10 setAnchorPoint() 함수를 호출하였다. AnchorPoint란 정규화된 모델 좌표계라 할 수 있다. (0, 0)부 터 (1, 1)까지의 값을 가지며 default로는 (0.5, 0.5) 즉, 그림의 정 중앙이다. 그림 4-11 그림 4-11처럼 Anchor Point를 사용하면 그림의 위치를 바꿀 수도 있다. Anchor Point는 케릭터 의 체력바 같은 것을 만들 때 유용하게 사용된다. 리소스를 이용하여 케릭터를 만들고 위치를 정해주는 작업이 끝나면 addChild() 함수를 이용하 여 Layer의 자식으로 추가해준다. Layer에 추가해주었으므로 이 Sprite는 화면에 나타나게 된다. 라인 40~57: 게임을 시작하는 MenuItem과 Label을 생성해주었다. MenuItem는 차례대로 ‘가만 히 있을 때 이미지 파일’, ‘눌렸을 때 이미지 파일’, ‘눌렸을 때 호출될 함수’, ‘함수가 위치하는 클래 스’를 인자 값으로 갖는다. Label은 텍스트를 출력해준다. 인자 값은 차례대로 ‘출력할 내용’, ‘글씨체’, ‘크기’이다. 라인 53에서 메뉴를 생성하였다. MenuItem은 특이하게 Menu를 생성하여 인자로 객체를 전달 한 뒤 Menu 자체를 Layer에 추가하는 방식으로 작동된다. Point::ZERO는 Point(0,0)과 같다. addChild() 함수를 호출하는 부분을 보면 아까와는 달리 인자 값을 두 개 주었는데 이 두 번째 인자는 아까 설명한 Z-order이다. 값이 클수록 앞쪽에 보이게 된다. 라인 65~72: 방금 MenuItem에서 인자 값으로 준 함수이다. 앱을 끝내는 역할을 한다. 라인 74~80: 역시 아까 인자 값으로 준 함수이다. 씬을 바꿔주는 역할을 한다. 그림 4-12 프로젝트를 실행하면 그림 4-12와 같은 화면이 출력되고 Start 버튼을 누르면 다음 씬으로 이 동한다. 다음 씬은 GameScene이다. 4.4 GameScene 그림 4-13 GameScene은 실제로 게임이 실행되는 씬이다. 라인 15~19: 터치를 사용하기 위해 Listener를 생성하고 콜백 함수를 등록한 부분이다. onTouchBegan과 onTouchEnded는 콜백 함수로 각각 터치가 눌렸을 때 손가락이 떼어졌을 때 호 출된다. 오버라이딩하여 사용해야 하기 때문에 함수 이름이나 리턴 값이 바뀌어서는 안된다. 라인 26~27: 클래스 함수들 내에서 공유할 수 있도록 Sprite*를 private 변수로 선언하였다. Me 라는 Sprite는 플레이어가 조종할 비행기이며 Game_MoveButton은 이 비행기를 조종할 때 사용 되는 Button이다. 그림 4-14 라인 별로 설명하기에 앞서 총체적으로 설명하자면 일단 우주 Sprite를 배경으로 두며 자신의 케릭터인 비행기 역시 Sprite로 생성하고 움직이는 버튼을 Sprite로 생성한다. 그리고 Touch Listener를 사용하여 버튼이 눌러졌을 경우 비행기가 action을 취해 움직이도록 하는 것이다. 라인 37: 위에 내용은 대부분 아까와 다름 없으므로 쉽게 이해할 수 있을 것이다. 버튼 스프라 이트를 생성할 때 Rect을 주었는데 Rect 생성자의 인자 값은 차례대로 ‘x 좌표’, ‘y 좌표’, ‘넓이’, ‘높 이’이다. Rect을 사용하게 되면 그림 파일의 전부를 사용하는 게 아니라 Rect으로 잘라낸 부분만 사용하게 되는 것이다. (x 좌표, y좌표를 좌상단으로 잡고 오른쪽 아래 방향으로 넓이, 높이만큼 잘 라낸다.) 게임 리소스들을 보면 여러 가지 sprite나 button등을 한 파일에 몰아 넣는 경우가 있는 데 그럴 때 사용하면 좋다. Animation을 사용할 때도 유용하게 사용된다. Me Sprite의 원본 파일 은 그림 4-15와 같다. Rect을 이용해서 하나만 잘라주었기 때문에 화면에는 하나만 출력되게 될 것이다. 그림 4-15 라인 57~65: 터치 리스너를 생성해 준다. 리스너에 콜백 함수들을 등록해주고 디렉터에서 관리 하는 디스패처에 리스너를 추가한다. 라인 73~89: 라인 75에서 가상의 Rect 공간을 만들었다. 화면 내에 아까 생성한 버튼이 위치하 는 공간이다. 콜백 함수에 인자로 들어 온 touch 변수로 getLocation()이라는 함수를 호출했는데 여기서 리턴되는 값은 터치가 인식된 Point이다. 라인 79에서 아까 만든 가상의 Rect 공간이 touch가 인식된 위치와 같다면 if 내부를 실행하도록 하였다. 라인 81에서 MoveBy라는 액션 클래스를 사용하였다. MoveBy는 액션의 일종으로 현재 위치에서 상대적으로 지정한 위치만큼 간다는 뜻이다. 인자 값은 차례대로 ‘액션이 실행될 시간’, ‘상대적으 로 Move할 좌표’이다. 즉, 비행기는 0.2 초 동안 y 방향으로 10만큼 이동하게 된다. 라인 83에서 repeatForever 클래스를 사용하여 인자 값으로 아까 만든 Action을 주어 그 액션을 영원히 실행하도록 하고 rep 변수에 대입하였다. Me이라는 아까 만든 비행기 Sprite가 rep 액션을 실행한다. 라인 91~105: 라인 73~89와는 별반 다를 게 없지만 Action을 추가하는 부분에서 Point(0, 10)이 아닌 Point(0, -10)을 주었다. 즉 터치가 떼어지면 onTouchEnded 함수가 호출되게 되고 함수 내부 Sprite에 아까 와는 반대의 Action을 주어 마치 Action이 끝난 것처럼 보이게 하는 것이다. 프로젝트를 실행하면 그림 4-15와 같은 화면이 나타난다. 그림 4-15 위쪽 방향만 터치를 구현하였으므로 비행기는 위쪽으로만 움직인다. Chapter 5 마치며 5.1 맺음 이로써 Cocos2d-x 엔진을 설치하고 안드로이드로 포팅 하는 법, 기본적인 클래스 및 프로젝트 구조 등을 살펴보았다. 기능들에 대해서 많이 설명하진 않았지만 프로그램이 돌아가는 원리만 안 다면 잡다한 기능들은 쉽게 익힐 수 있다. 엔진에서 제공한 함수를 사용하는 것보다 엔진 내부 구조를 살펴보는 게 게임 프로그래밍을 하는데 더욱 도움이 될 것 같다. 앞으로 게임을 효율적으로 만들기 위해 OOP개념을 적극 활용하고 엔진 내부 구조를 살펴 메모 리 관리에 대해 배우거나 Box2D, Tiled 등 Cocos2d-x에서 제공하는 부가적인 기능들을 공부할 것 이다. 참조 그림 2-1 사진 출처: http://www.cocos2d-x.org/ 그림 2-2 사진 출처: http://www.cocos2d-x.org/download 그림 3-1 사진 출처: http://file.thisisgame.com/upload/nboard/2006/11/29/20061129161040_4382.jpg 그림 3-2 사진 출처: http://cfile9.uf.tistory.com/image/156D2D404FF64C0A0DBF02 그림 3-3 사진 출처: http://4.bp.blogspot.com/-uL3JKOm61Es/UPUbtPJaTxI/AAAAAAAAAB4/jXJ2RHhGIO4/s1600/ccdirect or-hierarchy.png 그림 3-4 사진 출처: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:basic_concepts#director 그림 3-5 사진 출처: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:basic_concepts#director 그림 3-6 사진 출처: http://singleton.tistory.com/343 그림 3-7 사진 출처: http://htmlfive.co.kr/html5/xe/tip/8574 그림 3-8 사진 출처: http://graphics.ethz.ch/teaching/viscomp11/downloads/part2_11_clipping.pdf 그림 3-9 사진 출처: http://cfs14.tistory.com/upload_control/download.blog 그림 3-10 사진 출처: http://blog.naver.com/kater102?Redirect=Log&logNo=122081480 그림 3-11 사진 출처: http://xoax.net/cpp/crs/win32/lessons/Lesson2/ 그림 4-10 사진 출처: http://blog.naver.com/s_time00?Redirect=Log&logNo=50192205886 그림 4-11 사진 출처: http://blog.naver.com/s_time00?Redirect=Log&logNo=50192205886
© Copyright 2024