【입문】iOS 앱 개발 #3【Sound 재생하기】

소리



이번에는 게임중의 사운드를 흘리는 처리를 작성하고 싶다. SpriteKit은 간단한 사운드 제어도 제공합니다. '팩맨' 정도라면 이 범위에서 실현될 수 있을 것 같다.

사운드에는 효과음(Sound Effect)과 BGM(BackGround Music)이 있다. 이 2 종류를 실현하는 클래스를 작성한다.

Sound Manager 클래스 만들기



효과음을 재생하는 API는
playSE(.EatDot)

한다.

BGM을 재생하는 API는
playBGM(.BgmPower)

한다.

이러한 API를 가지는, 다음의 CgSoundManager 클래스를 작성한다.

CgSoundManager 클래스


/// Sound management class plays sound with SpriteKit.
class CgSoundManager {

    // Kind of sound items to play back.
    enum EnKindOfSound: Int {
        case EatDot = 0
        case EatFruit
        // ・・・略・・・
        case Intermission
    }

    // List of sound files to load.
    private let table_urls: [[(resourceName: String, typeName: String, interval: Int)]] = [
        [ ("16_pacman_eatdot_256ms", "wav", 256) ],
        [ ("16_pacman_eatfruit_438ms", "wav", 438) ],
        // ・・・略・・・
        [ ("16_pacman_intermission_5200ms", "wav", 5200) ]
    ]
    private var view: SKScene?
    private var actions: [SKAction] = []
    private var table_playingTime: [Int] = []

    // Adjustment time for processing to play sound.
    private let triggerThresholdTime: Int = 16 //ms

    /// Create and initialize a sound manager object.
    /// - Parameters:
    ///   - view: SKScene object that organizes all of the active SpriteKit content.
    init(view: SKScene) {
        self.view = view
        table_playingTime = Array<Int>(repeating: 0, count: table_urls.count)

        for t in table_urls {
            appendSoundResource(resourceName: t[0].resourceName, typeName: t[0].typeName)
        }
        // ・・・略・・・
    }

    /// Append sound resources to SpriteKit.
    /// - Parameters:
    ///   - resourceName: File name for sound resource.
    ///   - typeName: Type name for sound resource.
    private func appendSoundResource(resourceName: String, typeName: String) {
        let fileName = resourceName+"."+typeName
        let sound: SKAction = SKAction.playSoundFileNamed(fileName, waitForCompletion: false)
        actions.append(sound)
    }

    /// Play back a specified sound.
    /// If the specified item is playing back, it will not be played back.
    /// - Parameter number: Kind of sound items to play back.
    func playSE(_ number: EnKindOfSound) {
        guard soundEnabled && number.rawValue < actions.count else { return }

        let _number = number.rawValue
        if table_playingTime[_number] <= triggerThresholdTime {
            let table = table_urls[_number]
            table_playingTime[_number] = table[0].interval
            view?.run(actions[_number])
        }
    }

enum 으로 정의된 값과, 재생하는 사운드 파일의 table_urls 테이블을 대응시켜 둔다.

클래스 초기화시에, 이러한 파일을 SKAction의 오브젝트로서 생성해 둔다.

또한 사운드 파일의 재생 시간을 값으로 관리합니다. 이것은 동일한 효과음이 복수 중첩되지 않게 하기 때문에, 어느 효과음이 재생중에 동일한 것을 재생하는 경우에는 재생하지 않게 한다.

또한 BGM을 재생할 때는 지정된 사운드 파일의 재생이 종료되었음을 알고 동일한 것을 반복 재생하기 위해 사용한다. 자동으로 시작하기 위한 트리거는, update 메소드내에서 실시한다.

사운드 파일



이번 팩맨에 사용하는 사운드 파일은 15개로, 포맷은 WAV 형식, 16bit, 모노럴, 샘플링 레이트 22050Hz로 작성했다.

8bit라고 하면, 조금 소리가 담긴 느낌이 되기 때문에, 16bit로 해 처리의 무게를 신경쓰고 샘플링 레이트를 22050Hz에 떨어뜨렸다.

재생으로 쁘띠 푸치 노이즈가 나오지 않도록, 선두는 FadeIn, 종단은 FadeOut 처리를 해 둔다.

사운드 파일의 재생 시간은 편집 툴로 확인해 둔다.



[Sound Files]
*16_pacman_eatdot_256ms.wav 11,372bytes
*16_pacman_eatfruit_438ms.wav 19,360bytes
*16_pacman_eatghost_544ms.wav 24,040bytes
*16_pacman_miss_1536ms.wav 67,788bytes
*16_pacman_extrapac_1952ms.wav 86,166bytes
*16_credit_224ms.wav 9,634bytes
*16_BGM_normal_400ms.wav 17,852bytes
*16_BGM_power_400ms.wav 17,750bytes
*16_BGM_return_528ms.wav 23,366bytes
*16_BGM_spurt1_352ms.wav 15,592bytes
*16_BGM_spurt2_320ms.wav 14,212bytes
*16_BGM_spurt3_592ms.wav 26,272bytes
*16_BGM_spurt4_512ms.wav 23,018bytes
*16_pacman_beginning_4224ms.wav 187,054bytes
*16_pacman_intermission_5200ms.wav 229,388bytes

테스트 프로그램



GitHub 에 공개하고 있는 테스트 프로그램을 실행하면, 다음과 같은 화면이 표시되어, BGM이 5초마다 전환된다. 화면을 터치하면 효과음이 재생됩니다.



Sound Manager 클래스는 SoundManager.swift 파일에 코딩되어 있습니다. 코멘트 넣어 200행 미만이 되었다.
class GameScene: SKScene {

    private var sound: CgSoundManager!

    override func didMove(to view: SKView) {

        // Create and reset sound object.
        sound = CgSoundManager(view: self)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        sound.playSE(.EatDot)
    }

    private let bgm: [CgSoundManager.EnKindOfSound] = [.BgmNormal, .BgmSpurt1, .BgmSpurt2, .BgmPower, .BgmReturn]
    private var bgmIndex: Int = 0
    private var bgmTime: Int = 0

    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered

        // Play back BGM.
        if bgmTime == 0 {
            bgmTime = 16*60*5  // 5s
            sound.playBGM(bgm[bgmIndex])
            bgmIndex += 1
            if bgmIndex >= bgm.count {
                bgmIndex = 0
            }
        } else {
            bgmTime -= 16
        }

        // Update sound manager.
        sound.update(interval: 16 /* ms */)
    }
}

CgSoundManager 클래스는 SKView 객체를 매개 변수로 사용하여 객체를 생성합니다.

SKScene 로부터 오버라이드(override) 한 touchesEnded 이벤트의 메소드로, SE 를 재생한다.

또, 마찬가지로 update 이벤트의 메소드로, 5초마다 BGM을 전환해 재생한다.

참고


  • iOS Swift Game Development Cookbook: Simple Solutions for Game Development Problems
  • 【Swift】SpriteKit의 사용법. 백 음악과 효과음을 울립니다. (Swift 2.2, Xcode 7.3

  • 다음글



    【입문】iOS 앱 개발 #4【아키텍처의 설계】

    좋은 웹페이지 즐겨찾기