2010년 3월 4일 목요일

아이폰 게임사운드 구현(FMOD 이용) - 3

실제 구현이 되고 있는 FmodIphoneSupprot.m파일을 살펴보자..

아래 코드들은 설명을 위해 적절히 수정을 하며 적는 코드라 동작을 완전히 보장은 못하다는 것을

명심해주시기 바란다. 구현 형태면에서 봐주셨으면 하는 바램이다..



헤더파일을 임포트하고 게임앱델리게이트 헤더를 임포트하고 있다.

게임앱델리게이트 사용을 위해 gameApp이라는 변수도 extern 선언을 해주고 있다.

#import "FmodIphoneSupport.h"

#import "GameAppDelegate.h"

extern  GameAppDelegate *gameApp;



클래스 구현 시작 부분이다. FmodIphoneSupport의 구현임을 알린후

이 클래스 전반에 에러체크를 위해 사용할 ERRCHECK을 만들었다...이것은 Fmod에서 사용되는 API함수가 FMOD_RESULT

타입의 리턴값을 전해주는데 이값을 가지고 에러를 판단한다...이 함수는 Fmod api 다운 받아보면 포함된 예제 코드에 포함된 함수이다. 에러 발생시 에러내용을 디버깅 창에 출력하며 어플을 종료한다. 그 다음은 init함수를 재정의한다. 실제 Fmod초기화 부분은 initializeFMOD에서 해준다.

@implementation FmodIphoneSupport

void ERRCHECK(FMOD_RESULT result)

{

    if (result != FMOD_OK)

    {

        fprintf(stderr, "FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));

        exit(-1);

    }

}


- (id)init

{

if (self = [super init]) {

[self initializeFMOD];

}

return self;

}



초기화를 담당하는 함수다.. initFMOD 에서 실제 Fmod시스템에 대한 초기화를 해준다.

LoadGroups는 이전에 Fmod Designer에서 설정햇던 Group단위로 로딩을 해준다. 주의하여 보아야 할 부분은 bExternalAudioPlaying변수.. 이변수는 이미 아이팟으로 음악이 플레이중일때 처리를 위한 변수로 자세한 설명은 아래서 다시 하도록 하겠다. LoadGroups다음은 Fmod처리를 위한 update함수를 루프시키기 위해서 타이머 설정이 필요한데 타이머 설정을 해주고 있다. 그 다음은 bExternalAudioPlaying을 이용해서 이미 음악이 플레이중이면 음악을 계속해서 게임사운드와 같이 플레이할지 묻는 팝업창을 뛰우는 부분이다.

- (void)initializeFMOD

{

bool bExternalAudioPlaying = FALSE;

    [self initFMOD:FMOD_IPHONE_SESSIONCATEGORY_AMBIENTSOUND externalAudioPlaying:&bExternalAudioPlaying];

[self LoadGroups];

timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(timerUpdate:) userInfo:nil repeats:YES];

if(bExternalAudioPlaying == TRUE)

{

[gameApp stopTimer];

UIAlertView *resultView = [[[UIAlertView alloc] initWithTitle:@" iPod music Settings"  message:@"Your mp3 is playing. Do you want to continue playing your music?"

delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"cancel", nil] autorelease];

[resultView show];

}

}



Fmod사용을 위한 리소스들을 로딩하고 초기화하는 부분이다. EventSystem_Create로 이벤트시스템을 생성하고

FMOD_IPHONE_EXTRADRIVERDATA 구조체를 초기화하여 eventSystem->init의 인수로 넘기고 초기화를 마친다. 이때 init을 거치면서 bpExternalAudioPlaying의 주소가 가르키는 변수에는 이미 플레이중인 사운드가 있는지를 알수 있는 bool값이 셋팅되게 된다. eventSystem->loadiPhoneGame.fev에 이벤트관련정보를 로딩한다.

- (void)initFMOD:(FMOD_IPHONE_SESSIONCATEGORY)sessionCategory externalAudioPlaying:(bool *)bpExternalAudioPlaying

{

FMOD_RESULT result      = FMOD_OK;

char        buffer[200] = {0};

result = FMOD::EventSystem_Create(&eventSystem);

ERRCHECK(result);

    

FMOD_IPHONE_EXTRADRIVERDATA driverdata;

memset(&driverdata, 0, sizeof(FMOD_IPHONE_EXTRADRIVERDATA));

driverdata.sessionCategory = sessionCategory;

if(bpExternalAudioPlaying)driverdata.otherAudioPlaying = bpExternalAudioPlaying;

result = eventSystem->init(FMOD_SIMULTANEOUS_PLAY_NUM, FMOD_INIT_NORMAL | FMOD_INIT_ENABLE_PROFILE, &driverdata, FMOD_EVENT_INIT_NORMAL);

ERRCHECK(result);

[[NSString stringWithFormat:@"%@/iPhoneGame.fev", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];

result = eventSystem->load(buffer, NULL, NULL);

ERRCHECK(result);

}




LoadGroups에서 그룹객체를 얻어온다. freeFMOD에서는 eventSystem해제.

timerUpdate는 타이머를 통해서 루프를 돌게 해주는 함수이다. 이안에 eventSystem->update함수를 위치시키면 된다. dealloc은 객체소멸시 처리...

- (void)LoadGroups

{

FMOD_RESULT result      = FMOD_OK;

if(eventSystem)

{

result = eventSystem->getGroup("iPhoneGame/BGM", false, &bgmGroup);

ERRCHECK(result);

result = eventSystem->getGroup("iPhoneGame/SE", false, &envGroup);

ERRCHECK(result);

}

}

- (void)freeFMOD

{

[timer invalidate];

    

if (eventSystem)

{

eventSystem->release();

eventSystem = NULL;

}    

}

- (void)timerUpdate:(NSTimer *)timer 

{

FMOD_RESULT result  = FMOD_OK;

result = eventSystem->update();

ERRCHECK(result);


//메모리 쌓임을 방지하기 위해 일정 메모리 넘어가면 해제

if(memoryCurrent > FMOD_LIMIT_MEM_SIZE)

[self stopAllSE];

}

- (void)dealloc

{

[self freeFMOD];

[super dealloc];

}


설정된 그룹,이벤트 등을 얻어오는 함수들...

- (FMOD::EventSystem *)getSystem

{

return eventSystem;

}

- (FMOD::EventGroup *)getSEGroup

{

return seGroup;

}

- (FMOD::EventGroup *)getBGMGroup

{

return bgmGroup;

}

- (FMOD::Event *)getBGMEvent

{

return bgmEvent;

}



그룹별 사운드볼륨 설정하고 설정값 얻어오는 함수들..

- (float)getBGMVolume

{

return bgmVolume;

}

- (void)setBGMVolume:(float) vol

{

bgmVolume = vol;

if(bgmEvent!=nil)bgmEvent->setVolume(bgmVolume);

}

- (float)getSEVolume

{

return seVolume;

}

- (void)setSEVolume:(float) vol

{

FMOD_RESULT result      = FMOD_OK;

seVolume = vol;

}



아래 함수들은 구룹별로 각각 음악을 플레이 해주고 정지 시킬 수 있는 함수들이다...실제 사운드 제어는 이 함수들을 이용해서 하면 되는 것이다. BGM의 경우는 하나의 이벤트만 동작하게 되므로 하나의 이벤트를 정지 해주면 되게끔 되어져 있고 SE의 경우는 여러개의 소리가 동시에 출력되고 있을 수가 있으므로 재생을 정지하려면 모든 SE이벤트에 대해서 정지하도록 만들었다.

- (void)playBGM:(int)bgmEventNum

{

if(bgmEventNum<0)

{

return;

}

FMOD_RESULT result      = FMOD_OK;

result = bgmGroup->getEventByIndex(bgmEventNum, FMOD_EVENT_DEFAULT, &bgmEvent );

ERRCHECK(result);

if(bgmEvent)

{

result = bgmEvent->setVolume(bgmVolume);

ERRCHECK(result);

result = bgmEvent->start();

ERRCHECK(result);

}

}

- (void)stopBGM

{

FMOD_RESULT result      = FMOD_OK;

// 라인을 넣으면 사운드 데이타 해제

result = bgmGroup->freeEventData();

ERRCHECK(result);

}

- (void)playSE:(int)seEventNum

{

FMOD_RESULT result      = FMOD_OK;

FMOD::Event *event;

if(seEventNum<0)

{

return;

}

result = seGroup->getEventByIndex(seEventNum, FMOD_EVENT_DEFAULT, &event );

ERRCHECK(result);

result = FMOD::Memory_GetStats(&memoryCurrent, &memoryMaximum);

    ERRCHECK(result);


if(event)

{

result = event->setVolume(seVolume);

ERRCHECK(result);

result = event->start();

ERRCHECK(result);

}

}


- (void)stopAllSE

{

FMOD_RESULT result      = FMOD_OK;


result = seGroup->freeEventData();

ERRCHECK(result);

}


이상으로 허접한 강좌?(강좌라기 보단 개인적인 정리라고 하는게 나을지도...)를 마칠까 한다.
아무튼 이렇게 코드를 구성하면 간단하게 Fmod를 이용해서 아이폰에서 멋진 멀티게임 사운드를 구현해낼 수 있다.
Fmod가 유료라는 점에서 더 자세하게 들어가면 3D사운드의 구현이라던지...실시간으로 패러미터를 조정하여 같은 리소스
의 음원을 다른 느낌으로 플레이 한다던지 하는 것이 가능하다..응용의 범위는 굉장히 넓다고 할 수 있을 것이다.
Fmod사용이 적지 않은 비용이 든다 해도...다운받아서 구현해보는 데는 비용이 들지 않는다..게임을 출시할 경우에 라이센스 비용을 지불하는 식이라 일단은 다운받아서 한번 해보라고 권하고 싶다...

이걸로 마칠까 한다. 모두 즐거운 아이폰 프로그래밍 하시길~~

혹여나 궁금한 사항이 있으신 분은 nakniner@gmail.com이나 nakniner트위터로 연락을 주셔요~