【Swift】SpriteKitの使い方。SKAudioNodeでサウンドを再生すると、リスナーとの相対距離や速度で音が変化する。(Swift 2.2、XCode 7.3)
SKAudioNodeとは
本記事ではSpriteKit Sceneファイルの編集画面(シーンエディタ)の部品一覧にあるSKAudioNode(以下、オーディオノード)について説明する。
オーディオノードとは、iOS9で新登場したサウンドを再生するノードである。サウンド再生の意味では過去に紹介した「AVAudioPlaer」や「PlaySoundFileNamedアクション」と一緒だが、オーディオノードは3D空間オーディオエフェクトという特徴的な仕様を持っている。
オーディオノードを使ってみる
実際にオーディオノードをシーンに配置してサウンドを再生してみよう。以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる方はご利用下さい。⇒「テスト用プロジェクト」
猿とスピーカーの2つのノードをシーンに配置し、ドラッグ&ドロップで運べるようにしておいた。この時点ではサウンドは再生されない。
下図赤枠のSKSファイルを選択してシーンエディタを開き、部品一覧からオーディオノードを配置したいところだが、オーディオノードを設定したあとに他のファイルを開くとXcodeがクラッシュして落ちる(Swift 2.2、XCode 7.3)。
プロジェクトの作り直しなど色々試してみたが必ず落ちてしまうので、今回はオーディオノードの配置にはSKSファイルは使わないことにする。なので下図はスルー。
TestScene.swiftを以下のコードに変更する。オーディオノードを作成し、スピーカーノードの子として追加した(青色箇所)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
// // TestScene.swift // import Foundation import SpriteKit class TestScene:SKScene { var monkey:SKSpriteNode! var speaker:SKSpriteNode! var targetNode:SKSpriteNode! //現在シーン設定時の呼び出しメソッド override func didMoveToView(view: SKView) { //SKSファイルに配置したノードを取得する。 monkey = self.childNodeWithName("monkey") as? SKSpriteNode speaker = self.childNodeWithName("speaker") as? SKSpriteNode //オーディオノードを作成し、スピーカーの子として追加する。 let audioNode = SKAudioNode(fileNamed: "car_sound.mp3") speaker.addChild(audioNode) } //画面タッチ時の呼び出しメソッド override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { //タッチした座標を取得する。 let location = touches.first!.locationInNode(self) //タッチしたノードを取得する。 if let node = nodeAtPoint(location) as? SKSpriteNode { if(node == monkey || node == speaker) { //タッチしたノードをターゲットノードに設定する。 targetNode = node } } } //画面タッチ移動時の呼び出しメソッド override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { //移動後の座標を取得する。 let location = touches.first!.locationInNode(self) //移動後の座標に移動するアクションを作成する。 let action = SKAction.moveTo(CGPoint(x:location.x, y:location.y), duration:0.1) //アクションを実行する。 targetNode.runAction(action) } } |
以下は実際のプレイ動画。スピーカーを画面左下に近づけると音が大きくなるのが分かっただろうか。
これは、シーン内にはlistener(以下、リスナー)と呼ばれるノードがいて、オーディオノードが発する音はリスナーが拾うためである。
リスナーを設定していない場合は、アンカーポイントがリスナーになる。そのため、画面左下のアンカーポイントにスピーカーを近づけると音が大きくなったということだ。
ちなみに、「リスナーの皆さん、こんばんは」のリスナーは視聴者という意味。
リスナーを猿ノードにしてみよう。
didMoveToViewメソッドにリスナーを設定するコードを追加する。
1 2 3 4 5 6 7 8 9 |
//現在シーン設定時の呼び出しメソッド override func didMoveToView(view: SKView) { ‥省略‥ //リスナーを猿ノードにする。 self.listener = monkey } |
以下は実際のプレイ動画。スピーカーを運んで猿に近づけると音が大きくなる。逆に、猿を運んでスピーカーに近づけても音が大きくなる。
もし、両耳イヤホンがあれば耳に付けて聞いてみてほしい。猿に向かって右側にスピーカーを置くとイヤホンの右(R)の方が音が大きくなり、左側にスピーカーを置くとイヤホンの左(L)の方が音が大きくなる。
スピーカーを素早く動かして猿とクロスさせた場合は、「フォフゥー!」と風邪を切るような音になる。クロスが右⇒左ならに音も右⇒左、クロスが左⇒右なら音も左⇒右に流れるのが面白い。
自動再生、ループさせないようにする
デフォルトでは、オーディオノードを子に追加した時点でサウンドのループ再生が開始する。
サウンドが自動でループ再生されないようにするには、autoplayLoopedプロパティをfalseにする。その後、start、stopアクションを実行することでサウンドが再生される。
以下は、スピーカーをドラッグするとサウンドが開始し、ドロップするとサウンドが終了するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
// // TestScene.swift // import Foundation import SpriteKit class TestScene:SKScene { var monkey:SKSpriteNode! var speaker:SKSpriteNode! var targetNode:SKSpriteNode! //オーディオノード let audioNode = SKAudioNode(fileNamed: "car_sound.mp3") //現在シーン設定時の呼び出しメソッド override func didMoveToView(view: SKView) { //SKSファイルに配置したノードを取得する。 monkey = self.childNodeWithName("monkey") as? SKSpriteNode speaker = self.childNodeWithName("speaker") as? SKSpriteNode //オーディオノードの自動再生をオフにしてスピーカーの子に追加する。 audioNode.autoplayLooped = false speaker.addChild(audioNode) //リスナーを猿ノードにする。 self.listener = monkey } //画面タッチ時の呼び出しメソッド override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { //タッチしたノードを取得する。 let location = touches.first!.locationInNode(self) if let node = nodeAtPoint(location) as? SKSpriteNode { if(node == monkey || node == speaker) { //タッチしたノードをターゲットノードに設定する。 targetNode = node } if(node == speaker){ //サウンドを再生する。 let playAction = SKAction.play() audioNode.runAction(playAction) } } } //画面タッチ時の呼び出しメソッド override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { //サウンドを停止する。 let stopAction = SKAction.stop() audioNode.runAction(stopAction) } //画面タッチ移動時の呼び出しメソッド override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { //移動後の座標を取得する。 let location = touches.first!.locationInNode(self) //移動後の座標に移動するアクションを作成する。 let action = SKAction.moveTo(CGPoint(x:location.x, y:location.y), duration:0.1) //アクションを実行する。 targetNode.runAction(action) } } |
以下は実際のプレイ動画
3D空間オーディオエフェクトは無効にできないのか
公式リファレンスには、オーディオノードのpositionalプロパティをfalseにすれば、リスナーノードとの相対的な距離や速度の影響を受けない普通の音になるようなことが記載されているが、検証ではこのプロパティをtrue、falseのどちらにしても距離や速度の影響を受ける結果になった(Swift 2.2、XCode 7.3)。何か分かったら追記する。
⇒公式リファレンス「SKAudioNode」
If YES, the audio mixer considers the position and velocity of the SKAudioNode relative to scene’s current listener node. The mixer applies distance attenuation, doppler shift, and pan effects to the sound. If NO, then the sound is played normally. The default value is YES.