Apple Engine

Apple, iPhone, iOS, その周辺のことについて

iOS で SceneKit を試す(Swift 3) その81 - シーンに音楽や効果音をつける。

正直なところ、Core Audio など iOS 標準機能が使用できるので、 SceneKit のオーディオ再生機能を使用する必要はないけどご紹介。

SceneKit でのオーディオ再生の特徴としては、VR や HoloLens と同様に 3D 空間の位置に対して音源を再生(ミキシング)させることができる。

 

SceneKit では音を鳴らす方法は2つ。

  • ノードの addAudioPlayer に SCNAudioSource (AVAudioNode) を設定した SCNAudioPlayer で再生する
  • SCNAction の playAudio で SCNAudioSource を指定して再生する

 

使用するクラス

  • SCNAudioSource
  • SCNAudioPlayer

 

SCNAudioSource

プロパティ名 デフォルト値 説明
init?(named: String) なし オーディオファイル名の指定
init?(fileNamed: String) なし main bundle からのオーディオファイル名の指定
init?(url: URL) なし URL からのオーディオファイル名の指定
isPositional true オーディオの音量やリバーブなど距離によって自動的に変化させる。false にすると場所に関係なく一定で再生される
load() なし 事前読み込み用メソッド。shouldStream プロパティの値が true の場合は無効
volume 1.0 音量を設定する
rate 1.0 再生スピード。値を大きくすると再生速度が速くなる
reverbBlend 0.0 リバーブ (お風呂場のような音響効果)を付加する
loops false 繰り返し再生するか否か。デフォルトは false で1回のみの再生
shouldStream false true の場合はソースファイルから直接読み込み、オーディオバッファデータにロードを行わない。

 

SCNAudioPlayer

プロパティ名 説明
init(source: SCNAudioSource) SCNAudioSource を設定し初期化する
init(avAudioNode: AVAudioNode) AVAudioNode を設定し初期化する
audioSource SCNAudioSource を設定する
audioNode AVAudioNode を設定する
willStartPlayback SCNAudioPlayer 再生前に呼ばれる。ループの場合は最初に戻った際毎回呼ばれるので注意
didFinishPlayback 再生が止まった時呼ばれる

 

コードを書いてみる

いつも通り、Xcode の Game テンプレートで SceneKit 選択して、GameViewController.swift を開き、viewDidLoad() の下のところに以下のコードを書く。

あと、適当なオーディオファイルをプロジェクトに追加して、ファイル名を変更すること。

m4a ファイルなどインポート時にターゲットのチェックが外れるものがある。ターゲットが外れていると設定していてもファイルが見つからず音が鳴らないため注意。

let audioNode = SCNNode()
audioNode.name = "audioNode"
audioNode.position = SCNVector3(0,0,0)
scene.rootNode.addChildNode(audioNode)

let audioSouce = SCNAudioSource(named: "audio.wav")
audioSouce?.loops = true

let audioPlayer = SCNAudioPlayer(source: audioSouce!)
audioNode.addAudioPlayer(audioPlayer)

audioPlayer.willStartPlayback = {
    print("willStartPlayback")
}

audioPlayer.didFinishPlayback = {
    print("didFinishPlayback")
}

 

画面タップで音を止める

いつも通り、func handleTap(_ gestureRecognize: UIGestureRecognizer) の中身を変える。

func handleTap(_ gestureRecognize: UIGestureRecognizer) {
    let scnView = self.view as! SCNView
        
    let audio = scnView.scene?.rootNode.childNode(withName: "audioNode", recursively: true)!
    
    audio?.removeAllAudioPlayers()
}

 

シーンから名前をつけた audioNode のノードを探し出し、全ての SCNAudioPlayer を消す。

今回、設定していないが SCNAudioPlayer は複数設定でき、個別で SCNAudioPlayer を消すことができる。

 

SCNAction で設定する

使用するのは playAudio。

waitForCompletion が true の場合は再生持続時間が SCNAction の長さになる。
false の場合は再生後すぐ完了され、他のアクションがあれば再生中でも移行される。

let audioSouce = SCNAudioSource(named: "a.wav")

node.runAction(SCNAction.playAudio(audioSouce!, waitForCompletion: true))

 

Action なので、他の Action との併用、
順番に行う sequence や全て同時に実行する group が使用できる。

 

今回はここまで。