반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 받은 트랙백

글 보관함


오랜만에 코로나 튜토리얼을 번역해 봅니다.

거르지 않고 꼬박꼬박 해 오다가 이번 TDD 프로젝트에 참여하면서 두달넘게 공부를 하지 못했네요.

오랫동안 모바일 앱을 만들지 못했는데... 이제 모바일 앱 하나 만들고 싶습니다.

 

----------------------------------------------------

 

Posted on . Written by

 

지난주의 "Goodbye Globals!" 튜토리얼에 이어서 오늘은 non-global 메소드 안에서 scene들 사이에서 오디오 파일들을 어떻게 관리할지에 대해 얘기 나누도록 하겠습니다. 대부분의 앱에서 특정 오디오 파일들은 전체 앱내에서 필요로 하는 경우들이 많습니다. 그리고 어떤 오디오 파일은 특정 scene 에서만 필요로 하는 경우도 있구요. 특정 scene에서 오디오 파일이 로드 돼서 다른 scene 에서도 overlap 되는 상황도 필요할 때가 있습니다. 이런 경우 scene 내의 객체들을 cleanup 하는 것과 별도로 오디오 파일을 관리해야 해서 좀 복잡하게 됩니다.

 

이런 경우 어떻게 관리 해야 할까요? main.lua 에서 common sound를 로드하고 그 사운드들에 global 로 처리하도록 할 겁니다. 그러면 어떤 scene 에서도 해당 사운드를 플레이 할 수 있죠. 그런데 왠만하면 이 global 한 객체들을 사용하지 않는게 좋습니다. 그리고 실제로는 글로벌이 아니면서 여러분만의 global space를 생성하는 방법을 알려 드린 지난주 튜토리얼을 이런 경우에도 활용할 수 있습니다.

 

 

 

Creating the “sfx.lua” Module

 

첫번째로 할 일은 오디오를 처리할 모둘 이름을 sfx.lua 로 해서 만듭니다. 처음 시작하는 몇 라인은 아주 기본적인 겁니다. 약간의 세팅과 데이터가 들어있는 myData.lua 모듈을 사용합니다. 이 내용은 지난주에 자세히 다뤘었습니다. 그리고 나서 변수들을 담을 namespace를 생성합니다. 

 

local myData = require( "mydata" )
local sfx = {}  --create the main Sound Effects (sfx) table.
return sfx

 

루아의 required 는 처음에 한번만 실행된다는 것을 기억해 두세요. 처음 시작할 때 어떤 세팅이나 configure를 core code 에서 할 수 있다는 얘기입니다.

 

예를 들어

 

sfx.boomSound = audio.loadSound( "audio/explosion2.wav" )
sfx.bigBoomSound = audio.loadSound( "audio/explosion.wav" )
sfx.killMeSound = audio.loadSound( "audio/idied.wav" )
sfx.missileSound = audio.loadSound( "audio/missile.wav" )
sfx.blasterSound = audio.loadSound( "audio/scifi048.wav" )
sfx.bossSound = audio.loadSound( "audio/scifi026.wav" )
sfx.shieldsSound = audio.loadSound( "audio/shields.wav" )


이제 간단하게 sound에 대해 처리 하는 부분을 만들겁니다. 그리고 오디오 API 에게 각각의 사운드를 로드하도록 합니다. 그리고 그 handle 을 sfx 테이블에 저장합니다. 루아에서는 이 테이블을 dot 이나 bracket syntax로 구현할 수 있도록 제공합니다. sfx.boomSound 나 sfx["boomSound"] 이렇게 두가지 방법으로 처리할 수 있습니다. 이렇게 하면 나중에 sound를 참조할 handle name을 사용할 수 있습니다. 예를 들어 어떤 scene에서 sfx.lua 가 필요하면 아래와 같이 해당 오디오 파일을 플레이 시키면 됩니다.

 

audio.play( sfx.boomSound )

 

더 나아가서 이 메소드를 extend 해서 추가적인 기능을 부여할 수도 있습니다. 아래와 같이 추가적으로 오디오 파라미터들을 셋업 할 수 있는거죠.

 

audio.reserveChannels( 5 )
masterVolume = audio.getVolume()
audio.setVolume( 0.80, { channel = 1 } )  --music track
audio.setVolume( 0.66, { channel = 2 } )  --boss sound
audio.setVolume( 1.0,  { channel = 3 } )  --voice overs
audio.setVolume( 1.0,  { channel = 4 } )  --alien voice
audio.setVolume( 0.25, { channel = 5 } )  --weak explosion

 

sfx 테이블에서 call 할 수 있는 init 함수를 간단히 생성할 수 있습니다.

 

sfx.init = function()
   audio.reserveChannels(5)
   sfx.masterVolume = audio.getVolume()  --print( "volume "..masterVolume )
   audio.setVolume( 0.80, { channel = 1 } )  --music track
   audio.setVolume( 0.66, { channel = 2 } )  --boss sound
   audio.setVolume( 1.0,  { channel = 3 } )  --voice overs
   audio.setVolume( 1.0,  { channel = 4 } )  --alien voice
   audio.setVolume( 0.25, { channel = 5 } )  --weak explosion
end

 

이제 masterVolume sfx 테이블에 저장됐습니다. 그리고 나중에 이런 다른 action들이 필요 하면 그에 맞는 액션을 사용할 수 있습니다.
그리고 다른 trick으로는 여러분 앱의 sound on/off 세팅을 담당할 audio.play 의 버전을 만들 수 있다는 겁니다. 예를 들어 아래와 같이 필요한 사운드를 필요할 때 사용할 수 있습니다.

 

if ( settings.soundOn ) then
   audio.play( "beep" )
end

 

sfx.play 라는 함수를 생성해서 audio.play 와 비슷하게 작동하도록 할 수 있습니다. 이  방법을 사용하면 sound setting을 좀 더 편하게 할 수 있습니다.

 

sfx.play = function( handle, options )

   if ( myData.settings and myData.settings.soundOn == false ) then
      --your settings dictate NOT to play this sound
      return false
   end

   --otherwise, one of three things is true:
   --1. myData.settings is nil. You haven't set up control, so play the sound.
   --2. myData.settings.soundOn is nil. You have settings, but not a soundOn flag.
   --   So, play the sound since you haven't set up control.
   --3. soundOn is true, which means you want to play the sound. So, play it!
   audio.play( handle, options )

end

Loading Scene-Specific Sounds

Storyboard 와 함께 오디오를 사용할 때 만날 수 있는 어려움들에는 아래와 같은 것들이 있습니다.


• 해당 scene의 main chunk에서 사운드를 로딩하면 문제가 발생할 수 있습니다.왜냐하면 reload를 하려면 scene을 remove 한 다음에 recreate을 시켜야 하기 때문이죠.


createScene 이벤트는 scene이 로드될 때마다 로드될 필요가 없습니다. 그리고 큰 사운드가 로드되면 transition이 delay 되게 될 겁니다.


enterScene 이벤트는 매번 fire 됩니다. 하지만 스크린에 해당 scene이 완전히 로드되기 이전에는 사운드가 로드되지 않는다는 특징이 있습니다.

 

위의 enterSceneexitScene은 한 쌍으로 발생됩니다. enterScene은 scene-specific sound를 로드할 최적의 장소일 것입니다. 그리고 나서 exitScene 이벤트에서 audio.dispose()를 사용해서 그것들을 처리하면 됩니다. (이 때 객체를 nil 처리하는 것을 잊지 마세요.)

 

그런데 만약에 앱이 새로운 scene으로 갈 때 sound 가 갑자기 끊기지 않고 계속 play 되어야 하는 상황이면 어떨까요? 다음 scene에서는 이 audio handle에 접근할 수 있는 방법이 더 이상 없습니다. 이 문제점을 해결 하려면 sfx테이블로 sound 를 로딩하시면 됩니다. 그리고 onComplete phase에서 anonymous 함수를 사용해서 dispose 시키면 됩니다.

 

아래 코드를 보세요.

 

local sfx = require( "sfx" )

-- forward declare the handle
sfx.longsound = nil

function scene:createScene( event )
   local group = self.view

   local background = display.newRect( 0, 0, display.contentWidth, display.contentHeight )
   background.x = display.contentCenterX
   background.y = display.contentCenterY
   group:insert( background )

   local function leaveScene(event)
      if ( event.phase == "ended" ) then
         storyboard.gotoScene( "b" )
      end
   end

   local button = display.newRect( 100,100,100,100 )
   group:insert( button )
   button:setFillColor( 255,0,255 )
   button:addEventListener( "touch", leaveScene )

end

function scene:enterScene( event )
    sfx.longsound = audio.loadSound("audio/mirv_missiles_online.wav")
audio.play( sfx.longsound, { onComplete = function()
                                      audio.dispose( sfx.longsound )
                                      end } )

 

end 

 

루아에서는 onComplete 이벤트가 call 할 수 있는 anonymous 함수를 사용할 수 있도록 제공합니다. 여러분은 이 메소드를 오디오 파일이 끝났을 때 dispose 하도록 할 때 사용하실 수 있습니다. sound 가 실제로 사용되기 전에 sfx table에서 sound handle 하도록 미리 선언해둬야 한다는 것을 잊지 마세요.

 

여러분은 모듈에서 오디오를 처리하는 것의 장점을 아실 겁니다. 특히 Storyboard나 다른 scene manager utility를 사용할 때 말이죠. 오디오는 조심스럽게 그리고 제대로 관리 되어야 한다는 사실을 명심 하셔야 합니다. 물론 그 오디오 파일을 dispose 시키는 것도 말이죠. 그렇게 함으로서 메모리 누수를 방지하고 다른 이상한 문제점들을 방지할 수 있습니다.

 

반응형

Comment