본문 바로가기

게임개발팁

cocos2d-X 게임 만들기 6편


cocos2d-x 게임만들기 5편에 이어 이번 편에서는 게임 사운드 재생을 구현해 보겠습니다.  cocos2d와 cocos2d-x 에서 사운드 재생은 SimpleAudioEngine 을 사용하여 아주 쉽게 처리할 수 있습니다. 단 한줄의 코드로 배경음악과 효과음을 재생할 수 있죠.

cocos2d-x 에서 눈여겨 봐야할 점은 SimpleAudioEngine 이 아이폰과 안드로이드 두 플랫폼간에 동일 코드로 사운드를 재생한다는 점입니다. cocos2d-x로 게임을 작성할 때 크로스플랫폼 성격을 가져야 하는 코드는 SimpleAudioEngine의 코드를 참고하시면 많은 도움이 되실 것입니다.

사운드 재생을 위해서 아래의 사운드 압축 파일을 받습니다



위의 사운드 파일을 받은 다음 XCode로 NinjaGame 프로젝트를 열어 Resources 그룹에 복사합니다.


이제 GameScene.cpp 파일을 열어 SimpleAudioEngine.h 파일을 포함합니다.

#include "GameScene.h"
#include "SimpleAudioEngine.h"  //: 사운드엔진을 사용합니다.

씬을 초기화 하는 init 메서드 내에서 배경음악을 재생합니다. 아래 코드에서 굵게 표시한 코드를 입력합니다.

// 레이어를 초기화합니다.
bool GameScene::init()
{
    // CCLayerColor 의 initWithColor 초기자를 호출해 
    // 배경화면을 하얀색으로 변경한다.
    // 색상 설정은 ccc4(R, G, B, A) 매크로를 사용한다.
    if( !CCLayerColor::initWithColor(ccc4(255, 255, 255, 255)) )
    {
        // 초기화에 실패하면 false를 반환합니다.
        return false;
    }
    
    //: 배경음악을 재생합니다.
    CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic(
                    "background-music-aac.wav", //: 재생할 사운드 파일이름
                    true);                      //: 반복재생 여부
    
    //: 가변 리스트 인스턴스를 만듭니다
    _targets = new CCMutableArray< CCSprite * >();
    _projectiles = new CCMutableArray< CCSprite * >();
    
    
    //: 터치를 하면 총알을 쏘기 위해서
    //: 레이어가 터치 입력을 받도록 설정합니다.
    this->setIsTouchEnabled(true);
    
    //: 플레이어 스프라이트를 화면에 추가합니다.
    this->setPlayerSprite();
    
    //: 게임 로직을 만든다
    this->schedule(schedule_selector(GameScene::gameLogic), 1.0);
    this->schedule(schedule_selector(GameScene::checkCollision));
    return true;
}

이제 표창을 던질 때, 표창 던지는 효과음 재생을 추가합니다. 표창을 던질 때이므로 표창 스프라이트를 화면에 추가하는  addProjectileWithTouchLocation 메서드에  아래 굵게 표시한 코드를 입력합니다.

void GameScene::addProjectileWithTouchLocation(cocos2d::CCPoint location)
{
    //: 표창던지는 효과음을 재생합니다.
    CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(
                               "pew-pew-lei.wav"); //: 재생할 사운드파일이름

    ...
}

이제 아이폰에서 컴파일하고 실행해 보면 사운드가 잘 재생됩니다. 안드로이드도 build_native.sh 로 컴파일하고 이클립스로 빌드하여 실행하면 사운드가 잘 재생됩니다.

이번 튜토리얼의 구현 내용은 무척 짧았지만 SimpleAudioEngine는 많이 살펴봐야 하는 코드입니다. 따라서 SimpleAudioEngine 이 어떤 방식으로 구현되어 있는지 살펴보겠습니다.

구현 방식을 알아보기 위해서 NinjaGame/libs/CocosDenshion 폴더로 이동합니다.


하위 폴더가 많이 있는데요. 각 플랫폼별로 SimpleAudioEngine을 구현해 놓은 것입니다.

우선 cocos2d-x는 C++로 작성된 SimpleAudioEngine이라는 동일한 인터페이스를 여러 플랫폼에서 사용합니다. 하위 폴더 중 include 폴더를 보면 Export.h 파일과 SimpleAudioEngine.h 파일이 있습니다.

이 중 SimpleAudioEngine.h 파일이 SimpleAudioEngine 을 사용할 때 사용하는 최전방 인터페이스입니다. 이 인터페이스를 필두로 하고 실제 구현체는 플랫폼마다 따로 두는 것입니다.

즉 위의 게임에서 SimpleAudioEngine을 사용하기 위해서 포함한 SimpleAudioEngine.h 파일이 바로 이 파일입니다. 파일 내용은 길기 때문에 직접 열어 보시길 바랍니다 :)

그럼 실제 구현체들은 어디에 있는 것일까요? 바로 위의 폴더 그림에서 각 플랫폼별 폴더에 담겨 있습니다.  아래는 아이폰과 안드로이드 플랫폼 폴더가 담고 있는 파일입니다.



아이폰 폴더에서 중요하게 보아야 할 파일은 SimpleAudioEngine.mm 파일과 SimpleAudioEngine_objc.h 그리고 SimpleAudioEngine_objc.m 파일입니다.

SimpleAudioEngine.mm 파일은 SimpleAudioEngine.h 에 C++ 를 사용하여 선언한 SimpleAudioEngine 클래스를 구현한 파일입니다.

그리고 SimpleAudioEngine_objc.h 와 SimpleAudioEngine_objc.m 파일은 아이폰에서 사용할 수 있는 사운드 출력 API를 사용하여 실질적인 사운드 출력 엔진을 구현한 파일입니다.

이 관계를 그림으로 표현해 보면 아래와 같습니다.


이제 안드로이드는 어떤 구조로 구현했는지 알아보겠습니다. 안드로이드도 위와 비슷하지만 C++에서 Java로 작성된 안드로이드용 사운드 API를 사용할 수 없습니다. 이 때 Java의 JNI를 사용하여 이를 가능하게 해 주어야 합니다.

위의 폴더 그림을 보면 SimpleAudioEngine.cpp 파일이 있고 jni 폴더안에 SimpleAudioEngineJni.h 와 SimpleAudioEngineJni.cpp 파일이 있음을 볼 수 있습니다. 파일의 성격은 아이폰것과 동일합니다.

SimpleAudioEngine.cpp 은 SimpleAudioEngine.h 파일의 인터페이스를 구현한 것이고 SimpleAudioEngineJni.h 와 SimpleAudioEngineJni.cpp 파일은 안드로이드에서 사용할 수 있는 사운드 출력 API를 사용하여 실질적인 사운드 출력 엔진을 구현한 파일입니다.

하지만 SimpleAudioEngineJni.cpp 파일을 열어보면 아래와 같이 메서드를 찾고 호출하는 것이 있을 뿐 실제적인 사운드 API 호출은 보이지 않습니다.

void playBackgroundMusicJNI(const char *path, bool isLoop)
{
	// void playBackgroundMusic(String,boolean)
	jmethodID playBackgroundMusicMethodID = 
getMethodID("playBackgroundMusic", "(Ljava/lang/String;Z)V");

	if (playBackgroundMusicMethodID)
	{
		jstring StringArg = env->NewStringUTF(path);
		env->CallStaticVoidMethod(classOfCocos2dxActivity, 
playBackgroundMusicMethodID, StringArg, isLoop);
		//env->ReleaseStringUTFChars(StringArg, path);
	}
}

위의 코드 내용은 ObjC와 다르게 C++에서는 Java의 API를 호출할 수 없으므로 Java로 구현된 Java클래스에서 필요한 메서드를 찾아 호출하는 과정입니다. 위에서 찾는 메서드들은 아래 파일에 구현되어 있습니다.

NinjaGame/android/src/org/cocos2dx/lib 폴더에 Cocos2dxMusic.java 파일과 Cocos2dxSound.java 파일입니다.


 다시 이 관계를 아이폰용과 함께 그림으로 나타내면 아래와 같습니다.




위의 그림에서 안드로이드가 아이폰보다 한 단계가 더 많은 것은 objc 가 C의 슈퍼셋이기 때문이고 objc++은 c++의 슈퍼셋이기 때문에 서로의 코드에서 서로의 코드를 호출할 수 있기 때문입니다.

하지만 java는 c++과 문법만 비슷한 것이지 서로 어떤 집합 포함 관계를 형성하는 것이 아니므로 서로가 서로의 코드를 사용할 수 없습니다. 이를 가능하게 해주는 것이 JNI입니다.

그러나 이점을 가지고 어떤 언어가 더 좋네~ 이렇게 얘기할 순 없지요. 단지 어떤 점에서 약간 불편하고 나은 것일 뿐입니다. :)

이러한 구현 방법을 체득하는 것은 무척 중요한 일입니다. 일례로 크로스 플랫폼 게임에 광고를 달아야 할 때 위의 구조처럼 구현하면 되기 때문입니다.

코드의 양이 많으므로 관련 코드는 직접 열어서 살펴보시길 바랍니다.

그럼 이번 튜토리얼은 여기서 마치고 다음 튜토리얼에서는 게임 종료 화면을 구현하고 cocos2d-x 로 게임만들기 튜토리얼을 마칠까 합니다.

참고로 JNI 사용법은 아래 문서를 참고하시길 바랍니다.

'게임개발팁' 카테고리의 다른 글

iOS OpenGL | ES 튜토리얼 5편  (2) 2012.02.11
iOS OpenGL | ES 튜토리얼 4편  (13) 2012.02.02
cocos2d-x Box2D 프로젝트 만들기  (10) 2011.11.21
cocos2d-X 게임 만들기 5편  (7) 2011.11.18
cocos2d-X 게임 만들기 4편  (4) 2011.11.09