WWDC 2017 の SceneKit サンプル Fox 2 を調べる その31
引き続き GameController クラスの残りの関数を見てゆく。
今回は鍵を開けるなどゲームでのアクションについて。
鍵を開ける
unlockDoor 関数も長いので中身を別で見てゆく。
func unlockDoor() { ... }
扉が開いているか調べる
friendsAreFree が true の場合はこの関数を実行しない。 開いても プレイヤーキャラクター Max が少し操作でき、この関数を呼ぶトリガーを弾いてしまうため。
if friendsAreFree { return }
シーンを止める
宝石のアイテム取得時の演出と同様に startCinematic() を使ってシーンを止める
startCinematic()
鍵を開ける音を鳴らす
playSound(AudioSourceKind.unlockDoor)
扉が開く演出
SCNTransaction に対して completionBlock が2つあるため、3段階でアニメーションされる。
以下、アニメーション内容。
1段階目
扉が開くトリガーが引かれると即時で setActiveCamera でカメラ移動のアニメーションが行われる
2段階目
- ドアのノード取得
- ドアのパーティクル配置用のノードを探しパーティクルを置く
- 扉が開いた際の音を鳴らす
- ドアを半透明にする
3段階目
- animateFriends() 呼び仲間を初期状態アニメーションさせる
- friendsAreFree を ture にして SceneRenderer のデリゲートで設定している歩行アニメーションの実行を許可にする
- DispatchQueue から showEndScreen() を呼び、クリア時の画面を表示する。
SCNTransaction.begin() SCNTransaction.animationDuration = 0.0 SCNTransaction.completionBlock = {() -> Void in let door: SCNNode? = self.scene!.rootNode.childNode(withName: "door", recursively: true) let particle_door: SCNNode? = self.scene!.rootNode.childNode(withName: "particles_door", recursively: true) self.addParticles(with: .unlockDoor, withTransform: particle_door!.worldTransform) self.playSound(.collectBig) //add friends SCNTransaction.begin() SCNTransaction.animationDuration = 0.0 self.addFriends(GameController.NumberOfFiends) SCNTransaction.commit() //open the door SCNTransaction.begin() SCNTransaction.animationDuration = 1.0 SCNTransaction.completionBlock = {() -> Void in self.animateFriends() self.friendsAreFree = true DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(1.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: {() -> Void in self.showEndScreen() }) } door!.opacity = 0.0 SCNTransaction.commit() } setActiveCamera("CameraCinematic02", animationDuration: 1.0) SCNTransaction.commit()
鍵のアイテムを表示する
鍵のアイテムを表示する処理をする関数。
- 鍵表示用のフラグを true にする
- 鍵のノードを探し、key に渡す
- 鍵を表示した時の音を鳴らす
- 鍵を表示する前のパーティクルを設定し再生
- 鍵のノードの透明度を 1 にして出現させる
- completionBlock から DispatchQueue で keyDidAppear() 呼び処理を終える。
func showKey() { keyIsVisible = true let key: SCNNode? = scene!.rootNode.childNode(withName: "key", recursively: true) playSound(AudioSourceKind.collectBig) addParticles(with: .keyApparition, withTransform: key!.worldTransform) SCNTransaction.begin() SCNTransaction.animationDuration = 1.0 SCNTransaction.completionBlock = {() -> Void in DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(2.5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: {() -> Void in self.keyDidAppear() }) } key!.opacity = 1.0 SCNTransaction.commit() }
鍵のアイテムを表示後の処理
鍵のアイテムを表示後、execTrigger でカメラを元の位置に戻し、 stopCinematic() を一時停止しているシーンを再生する。
func keyDidAppear() { execTrigger(lastTrigger!, animationDuration: 0.75) stopCinematic() }
鍵のアイテムが出現する事前処理
先ほどの showKey 関数が呼ばれる前に keyShouldAppear が呼ばれる。
startCinematic() を呼び、画面を一時停止。
SCNTransaction のアニメーションから setActiveCamera を呼び CameraCinematic01 のカメラに移動。
completionBlock で showKey 関数が呼ぶ。
func keyShouldAppear() { startCinematic() SCNTransaction.begin() SCNTransaction.animationDuration = 0.0 SCNTransaction.completionBlock = {() -> Void in self.showKey() } setActiveCamera("CameraCinematic01", animationDuration: 3.0) SCNTransaction.commit() }
アイテムを取る処理
physicsWorld のデリゲートで Max がアイテムに接触した際呼ばれる関数。
このノードの physicsBody がある場合処理開始。
引数で渡されるノード collectable の名前が "key" なら鍵、"CollectableBig" なら宝石。
鍵の取得
- keyIsVisible が true でなく表示されていない場合は処理を抜ける。(透明度 0 にしているだけなので物理判定は通るため)
- アイテム取得のサウンドを再生
- UI である overlay の didCollectKey 関数を呼び画面左上に鍵を表示する。
- collectedKeys を1増やす
宝石の取得
- collectedGems を1増やす
- アイテム取得のサウンドを再生
- UI である overlay の collectedGemsCount 関数を呼び画面左上に宝石を表示する。
- collectedGems が 1 の場合 DispatchQueue から keyShouldAppear() を呼ぶ。
その後の処理
collectable の physicsBody を nil にして物理判定を削除し、keyApparition のパーディクルを表示して、collectable ノードをシーンから消す。
func collect(_ collectable: SCNNode) { if collectable.physicsBody != nil { if collectable.name == "key" { if !self.keyIsVisible { return } // play sound playSound(AudioSourceKind.collect) self.overlay?.didCollectKey() self.collectedKeys += 1 } //the gems else if collectable.name == "CollectableBig" { self.collectedGems += 1 // play sound playSound(AudioSourceKind.collect) // update the overlay self.overlay?.collectedGemsCount = self.collectedGems if self.collectedGems == 1 { //we collect a gem, show the key after 1 second DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(0.5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: {() -> Void in self.keyShouldAppear() }) } } collectable.physicsBody = nil //not collectable anymore // particles addParticles(with: .keyApparition, withTransform: collectable.worldTransform) collectable.removeFromParentNode() } }
次回はプレイヤーキャラクターのコントロールをする関数を見てゆく。