phina.js が面白そうなのでお勉強中　その１３

[phina.js が面白そうなのでお勉強中　その１１](https://qiita.com/hirotyankoutei/items/8a7e0177d502ed54a22c)

#ゲームのBGMを流す
ゲーム中に音楽を流します。BGMです。

##音楽を流す方法を調べてみる
phina.js で音楽を流す方法を調べてみました。
効果音を流す時に SoundManager を使ったので、同じような感じかな？

[ほろほろりドットコム](https://horohorori.com/)の[［phina.js］ サウンドの操作について](https://horohorori.com/memo_phina_js/about_soundmanager/)に、SoundManagerのメソッド、プロパティ一をまとめてくださっていたので参考にしました。
ただ、音を流しているとか流していないとかってどう判断しているのか解らなかったので、結局[SoundManagerのソース](https://github.com/phinajs/phina.js/blob/develop/src/asset/soundmanager.js)見たりしました。
で、とりあえず以下のことが判りました。

・音楽を流す場合は、SoundManager.playMusic(曲名) を使えば良い。デフォルトでループしてくれる。
・音楽を止める場合は、SoundManager.stopMusic() を使えば良い。
・音楽のボリュームは、SoundManager.setVolumeMusic(音量)で設定できる。デフォルト0.8っぽい。ってことは0～1で設定するのかな？
・音楽を流しているかどうかは、SoundManager.currentMusic が null かどうかで判断できそう。（stopMusic()の処理内でnullを設定しているから。）

ここまで解れば、以下のことができそうです。
・タイトル、ゲームオーバー時は音楽を流さない。
・ゲーム開始時にメイン曲を流す
・ボス敵時にボス曲を流す

##作曲する
音楽ファイルはmp3とかwavファイルが使えるようです。
ってなわけで、mp3とかwavファイルが用意できればOKです。

が、音楽も自分で作ってみたくてちゃちゃっと作曲しました。
使ったツールは、[世界樹](https://openmidiproject.osdn.jp/Sekaiju.html)っていうフリーのMIDIシーケンサーです。
ピアノロールで音階と長さを指定するだけで簡単に音が鳴る所がお気に入り。凝ったことはできないけど、とにかく手軽に音楽が作れるので愛用しています。

MIDIをwavファイルに変換するのが少々やっかいで、私はMIDIの音源に[VirtualMIDISynth](https://coolsoft.altervista.org/en/virtualmidisynth)を使っているので、VirtualMIDISynthの機能でMIDI→wav変換しましたが、割とMIDIの知識が必要です。
ま、この辺はネットで調べるといろいろ方法が出てきますね。

wavファイルの音の調整は[Audacity](https://www.audacityteam.org/)を使いました。
ファイルサイズを減らすために、かなり音質を下げました。

そんなわけで、以下の2ファイルを作成しました。
[MaineMusic.wav](http://hirotyan.my.coocan.jp/phinajs/Shooting/011/MaineMusic.wav)
[BossMusic.wav](http://hirotyan.my.coocan.jp/phinajs/Shooting/011/BossMusic.wav)


##BGMを流す
BGMを流すように実装していきます。

まず、アセットの音に、wavファイルを設定します。
mainemusic と bossmusic を追加しました。

```javascript
	// 音
	sound: {
		'shoot': 'shoot.wav',
		'bom': 'bom.wav',
		'mainemusic': 'MaineMusic.wav',
		'bossmusic': 'BossMusic.wav',
	},
```


MainScene の init でボリュームを設定しました。
元のファイルの音量がけっこう大きいので、音量を下げています。

```javascript
		// 音量の設定
		SoundManager.setVolumeMusic(0.5);

```

MainScene の update　で音楽を鳴らします。
一番最後で音楽を鳴らしています。

```javascript
	update: function () {
		
		// GAMEOVERの場合
		if (this.isGameOver && !this.gameOver.visible) {
			// 自機を非表示
			ship.hide();
			// GAMEOVER表示			
			this.gameOver.show();
			// GAMEOVERの処理
			this.gameOver.tweener.wait(5000).call(
				function() {
					// タイトルへ戻る
					mainScene.exit({score: mainScene.score});
				}
			).play();
		}
		
		// ランダムで星を生成する
		if (Random.randint(1, 3) == 1) {
			Star(this.group.backgroungGroup, Random.randint(0, this.width), 0);
		}
		
		// シナリオ関連の処理
		// 面数が配列の最大を超えた場合は最初に戻す
		if (this.stage >= scenarioArray.length) {
				this.stage = 0;
				this.scenarioFrame = 0;
				this.scenarioNo = 0;
				this.scenarioNowFrame = 0;
				// 周回数を増やす
				this.scenarioRoopCount++;
		}

		// シナリオ配列から面数のシナリオを取得
		var scenarioStageArray = scenarioArray[this.stage];
				
		// シナリオ数が配列の最大を超えた場合はシナリオの処理を行わない
		if (this.scenarioNo >= scenarioStageArray.length) {
			return;
		}

		// シナリオのフレームを進める
		this.scenarioFrame++;
		// シナリオのフレーム数が次のシナリオフレーム数を超えた場合
		if (this.scenarioNowFrame　+　scenarioStageArray[this.scenarioNo].frame <= this.scenarioFrame) {
			
			// 現在のシナリオフレーム数を更新する
			this.scenarioNowFrame　+=　scenarioStageArray[this.scenarioNo].frame;
			// シナリオを実行する
			doScenario(scenarioStageArray[this.scenarioNo]);
			// 次のシナリオに進む
			this.scenarioNo++;
			
			// 次のシナリオが最後の場合は音楽を止める
			if (this.scenarioNo == scenarioStageArray.length) {
				SoundManager.stopMusic();
			}
		}
		
		// 音楽を鳴らす
		if (SoundManager.currentMusic == null && !this.isGameOver) {
			if (this.scenarioNo == scenarioStageArray.length) {
				// シナリオ最後の場合はボス曲
				SoundManager.playMusic("bossmusic");
			} else {
				// それ以外はメイン曲
				SoundManager.playMusic("mainemusic");
			}
		}
	},
```

SoundManager.currentMusic が null の場合、音楽を鳴らしています。
また、シナリオが最後になった場合、ボスの音楽を鳴らせるようにしています。
シナリオが最後になった段階で、一旦音楽を止めます。（SoundManager.currentMusic が nullになる）
その後、音楽を鳴らす処理で、シナリオの最後の場合はボスの曲を流すようにしています。

音楽を止める処理なのですが、
まず、ゲームオーバーになった際に音楽を止めます。
Ship クラスの shootDown（自機がやられた時の処理）でゲームオーバーと判定された段階で音楽を止めています。

```javascript
	shootDown : function () {
		this.shotdownFrameCount++;
		// 点滅させる
		this.alpha = this.alpha == 1 ? 0 : 1;
		
		// 100フレームを過ぎた場合、再配置処理に移る
		if (this.shotdownFrameCount > 100) {
			
			//　GAMEOVERの判定
			if (mainScene.remainingShip == 0) {
				// GAMEOVERにする
				mainScene.isGameOver = true;
				// 音楽を止める
				SoundManager.stopMusic();
				return;
			}
			
			// 自機を減らす
			mainScene.remainingShip--;
			mainScene.remainingShipLabel.setValue(mainScene.remainingShip);
			
			// 再配置処理
			mainScene.relocation();
		}
	},	

```

次に、ボスを撃破した時に音楽を止めています。
Bossクラス（Bossの基本クラス）の update で硬さが0になった時に音楽を止めています。

```javascript
	update: function(app) {
		// 硬さが0の場合
		if (!this.isExplosion && this.hardness <= 0) {
			// 音楽を止める
			SoundManager.stopMusic();
			// 爆発処理
			this.isExplosion = true;
			// カウントをリセット
			this.count = 0;
			// スコアを加算
			mainScene.score += this.score;
			mainScene.scoreLabel.setValue(mainScene.score);
		}
		
		// 爆発処理
		if (this.isExplosion == true) {
			// 爆破処理
			this.count++;
			// 爆発を起こす
			if (this.count % 10 == 1) {
				var tmpX = Random.randint(0, this.width);
				var tmpY = Random.randint(0, this.height);
				// 爆発を表示
				Explosion(this.left + tmpX, this.top + tmpY).addChildTo(mainScene.group.explosionGroup);
				// 爆発音を出す
				SoundManager.play('bom');
			}
			
			// 爆発終了処理
			if (this.count >= 100) {
				// 自機を1機追加
				mainScene.remainingShip++;
			    mainScene.remainingShipLabel.setValue(mainScene.remainingShip);
				// 次の面へ
				mainScene.stage++;
				// シナリオを初期化
				mainScene.scenarioFrame = 0;
				mainScene.scenarioNo = 0;
				mainScene.scenarioNowFrame = 0;
				// 削除する
				this.remove();
				delete this;
				return;
			}
			return;
		}
		
		// 自機との当たり判定
		collisionShip(this);
		
		// 動かす
		this.move();		
	},
```

これで完了です。
ゲームのBGMが流れるようになりました！

##今日の成果
今日の成果をここに上げました。
ゲームを開始すると音楽が流れます。
ボス敵で音楽が変わります。
ゲームオーバーで音楽が止まります。
http://hirotyan.my.coocan.jp/phinajs/Shooting/011/index.html

敵とかボスとかも4面分追加してます。