Programming Android

(Android) NDK 1.6 R1을 이용하여 C/C++로 작성된 고전 3D 게임 포팅하기

steloflute 2012. 6. 23. 00:06

출처

http://www.androidpub.com/83227


지난 토요일 오전에 구형 노트북에 최신 버전인 SDK 2.1과 NDK 1.6을 설치하였습니다.
취미로 게임 포팅을 해보려고 시작한 일인데, 토요일/일요일 몽땅 잡아먹더군요..
전문가들이 보기에 그 결과는 별거 아닐수도 있지만, 저같은 초보에게는 나름 큰 의미가 있었습니다.
제가 삽질했던것들을 정리하는 목적도 있습니다만, 그 과정을 저같은 초보분들과 나누고자 글을 이어갑니다.

참고로 1인칭 슈팅 게임(FPS:First Person Shooting)의 원조격인 Wolfenstein 3D는 1992년 PC용으로 발매되어 엄청난 성공을 이루며, id Software라는 회사가 급성장하는데 결정적인 역할을 합니다. 몇년 뒤 id Software의 오픈소스 정책덕분에 수많은 기기에 포팅이 됩니다.

오드로이드에 키 매핑은 다음과 같이 해보았습니다.  아래 그림을 클릭하면 좀 더 잘 보이네요.
odroidaa.jpg 
볼륨키(L-R)로 좌우 난사를 만들수 있을것 같았는데 아직 내공이 부족해서 안되네요... 현재 공부중입니다.


아래 링크 파일은 SDK 2.1과 NDK 1.6 이 설치된 컴퓨터에서 제가 직접 컴파일하고 오드로이드라는 실제 개발용 기기에서 테스트된 소스 코드입니다. 최초 원본은 id Software사에서 릴리즈 된것을 리눅스 커뮤니티에서 X/SDL로 포팅한 것이며, 이후 2004년 즈음 GP32이라는 게임기에 포팅이 되었습니다. 2009년 초반에 Vladimir Silva 씨가 안드로이드에 포팅을 하였습니다. 이 때만해도 NDK가 나오기 전이라, 리눅스 머신에서 C를 컴파일하고 윈도우에서 자바와 링크시키는 구조였습니다. 비교적 최근 NDK가 나오면서 개발환경이 많이 개선된 것입니다.
여기서 직접 올리려고 했는데, 파일첨부 제한이 1MB라 링크를 걸었습니다.

소스코드 <=== 여기 클릭 !!


하여간 이렇게 구한 소스 코드를 분석하여 최신 SDK과 최신 NDK에서 컴파일이 되도록 수정하였고, 여기에 오드로이드의 키맵에 맞게끔 입력 부분을 수정하고, 화면 해상도와 Orientation을 가로 모드에 적합하도록 변경한것이 전부 입니다. 그 다음은 게임을 즐겼을 뿐입니다.
이 모든 것이 이틀만에 이루어졌다면 포팅의 의미는 정말 큰것이겠죠?


잡소리가 좀 길었습니다. 소스 설치해서 컴파일이나 해보죠... ㅋㅋㅋ
파일(Wolf3D.zip)을 받아, 아래 위치에 풀어줍니다. 첫번째 강좌에서 말씀드린 NDK 설치 경로와 같다고 가정합니다.
D:\android\android-ndk-1.6_r1-windows\android-ndk-1.6_r1\app\




아래와 같은 구조로 디렉토리가 생성되어야 합니다.
좀 다르더라도 자바는 잘 컴파일 되지만, JNI쪽 C는 컴파일이 쉽지 않습니다. 전 이것때문에 한시간을 삽질했습니다.

3dwolf_dir.PNG 

그리고 cygwin을 실행하여 C 코드쪽을 먼저 컴파일 합니다. (물론 나중에 해도 됩니다...)
아래 명령으로 설치된 ndk 폴더로 이동하여 컴파일하고 그 결과를 확인합니다.
$ cd /cygdrive/d/android/android-ndk-1.6_r1-windows/android-ndk-1.6_r1

$ make APP=Wolf3D
Android NDK: Building for application 'Wolf3D'
Install        : libWolf3D.so => apps/Wolf3D/project/libs/armeabi


컴파일이 무사히 끝났다면, apps/Wolf3D/project/libs/armeabi 아래에 방금 생성된 libWolf3D.so 가 설치되었다고 표시됩니다.

이 .so 라이브러리의 소스 코드는 apps/Wolf3D/jni 폴더에 들어있습니다. C 코드들과 헤더를 합치면 대략 30,000라인 정도됩니다. 
대충 한번 훑어 보시길 바랍니다. 그런데 이 jni 폴더 내부에 꼭 알아두어야 할 파일이 두개가 있습니다. 
바로 Android.mk라는 Makefile과 jni_wolf.c라는 JNI 핵심 코드입니다.

위에서 빌드할때 make APP=Wolf3D라고 간단히 입력했지만, 이렇게 실행시킨 빌드 스크립트 과정 중 핵심은 apps/Wolf3D/jni/Android.mk에 들어 있습니다. 
gcc-arm으로 컴파일할 파일들의 목록, 컴파일 옵션, 모듈 이름같은 중요한 정보들이 들어있습니다

두번째로 꼭 탐구해야할 파일은 jni_wolf.c 입니다. 게임 화면과 게임 사운드를 렌더링할 수 있는 기능(Method)을 만들어 JAVA쪽으로 밀어낼수 있게 통로를 만들고, 키나 터치 입력 이벤트를 JAVA쪽으로 부터 받을 수 있게 합니다. 이 파일만 잘 이해하면 JNI의 Native쪽은 거의 다 배운 것입니다. 


이제 Java쪽을 알아보겠습니다. Eclipse를 실행시키고, 새로운 안드로이드 프로젝트를 생성합니다. 
이때 Create project from existing source를 선택하고 Location은 아래와 같아야 합니다.

D:\android\android-ndk-1.6_r1-windows\android-ndk-1.6_r1\apps\Wolf3D\project

Build Target은 Android 1.5로 했습니다.
프로젝트 생성이 잘 되었다면, 다음과 같은 디렉터리 구조와 LibWolf3D.so가 포함되어야 합니다.
3dwolf_so.PNG 

위 그림에서와 같이 11개의 자바 파일이 있습니다. 각 파일에 대해 간단히 설명드리겠습니다. 

<1> ControllerListener.java 와 <2> SNESController.java 는 가상키보드용 파일입니다. 오드로이드에서는 사용하지 않습니다. 추후 키보드가 없는 모토로이나 N1 같은 제품을 위해서는 필요할 수도 있겠죠.

<3> WolfLauncher.java 는 게임의 기동관련 준비를 하며, 화면 해상도를 설정하고 키 이벤트 핸들러를 만듦니다. C로 만든 so 라이브러리도 여기서 로딩을 합니다. 결국에는 게임의 Main Activity class로 main thread를 돌립니다.

<4> AudioClip.java <5>AudioManager.java <6>SoundNames.java 는 모두 사운드 처리를 위한 파일들입니다. 안드로이드는 Native에서  오디오 장치를 직접 제어할수 기능이 없기 때문에 JAVA에서 안드로이드 미디어 플레이어를 틀어 놓고, 그 곳에 믹싱 시키는 구조로 만들어야 합니다. 추후에 OSS/ALSA를 제어할수 있는 기능이 생기면 좋겠습니다. 가능성은 없겠지만..

<7> Natives.java은 C로 만든 Native 라이브러리와 양방향으로 데이터를 주고 받는 역할을 합니다. 모든 native 함수를 포함하고, C Native 라이브러리에서 자바로 데이터를 넘겨주는 Callback 기능도 구동합니다.

<8> DialogTool.java 는 메뉴/경고 메시지를 뿌리거나 다이얼로그 박스를 만드는데 사용합니다.

<9> LibraryLoader.java는 main activity에서 C Native 라이브러리를 로딩할때 사용됩니다.

<10> ScanCodes.java 키 입력 스캔 코드값을 적절히 변환하는 컨버터입니다. C 언어 쪽 코드 변경을 최소화 하기 위해 사용하는것 같습니다.

<11> WolfTools.java 라이브러리 이름등등 여러 명칭이 선언되어 있으며, 게임 파일 로딩하는 기능도 여기에 있습니다.


3일간 삽질한  소감은 다음과 같습니다.
1. 아이폰 개발에 비해 부족한것은 다소 있지만,  NDK 1.6으로 대부분의 기능 구현에는 문제가 없어 보인다
2. 게임을 만들려면 사운드가 중요한데, Open AL같은 오디오 라이브러리나 스트리밍 기능이 없어 다소 불편합니다. 편법은 편법일뿐...
3. 아이폰에는 없는 멀티태스킹때문에 좀 골치아플때가 있다. 여러 프로그램이 동시에 돌아갈때 동영상 재생 프로그램이나 비쥬얼이 찬란한 3D게임을 돌릴때 CPU의 MIPS 부족으로 동작이 부드럽지 못한 경우가 발생한다. 그렇지만 모바일 장치에서 멀티태스킹은 정말 멋지다.
4. Native와 Java 사이를 왔다갔다 하면서 디버깅이 되는것 같은데, 정확히는 잘 모르겠다. 이부분을 좀 공부해 봐야 겠다.


사견이지만 진정한 프로 개발자들은 NDK를 사용하지 않는것 같습니다. 최신 NDK 1.6 이라도 컴파일러 버전이 4.2.1으로 낮은 편입니다. 
오드로이드나 넥서스원에 탑재된 최신 프로세서인 Cortex-A8의 주요 장점인 Thumb-2, VFP나 NEON SIMD를 제대로 사용하기 위해서는 가능하면 4.4 이상을 사용해야 합니다. 그래서 리눅스를 깔고 그 위에 복잡한 개발환경을 구축하는것 같네요.  구닥다리(벌써?) ARM11 폰에대한 호환성은 좀 문제가 되겟지만요.

다음 번에는 이쪽으로 한번 도전해보고 그 결과를 공유하도록 하겠습니다. 얼마나 걸릴지는 모르겠지만.. ㅎㅎㅎ

즐거운 앤드로이드 개발 되소서...


추신: 아래 동영상은 비교적 최근 Wolfenstein 3D 가 PlayStation3 에도 포팅된것을 촬영한 것입니다.

"