[phina.js] サウンドの操作について
今回の記事は、phina.jsでのサウンド操作について書いていこうと思います。まだ改善の余地のあるコードかもしれませんが、使用例としてサンプルも載せておきますので、よろしければ一例としてお役立てください。もしphina.jsが分からない方は、[phina.js]基本 — テンプレートについてをご一読いただくことをおすすめいたします。
※2018年8月29日 加筆・修正を行いました。
※2018年9月26日 加筆・修正を行いました。
※2018年10月18日 加筆・修正を行いました。
※2020年5月25日 加筆・修正を行いました。
準備
まず、お好きなmp3ファイルなどのサウンドファイルをご用意ください。そして、(私自身がクロスドメインあたりの事がよくわからないので)ローカルサーバー上で試すことをおススメします。assetsにサウンドファイルを読み込ませます。
// アセット
const ASSETS = {
// サウンド
sound: {
"bgm1":"./bgm.mp3",
}
};
/*
* 中略
*/
phina.main(function(){
// アプリケーションを生成
var app = GameApp({
// MainSceneから開始
startLabel: "main",
// アセット
assets: ASSETS
});
// 実行
app.run();
});
一応雛型として置いておきます。
<script src='https://cdn.jsdelivr.net/gh/phi-jp/phina.js@0.2.3/build/phina.js'></script>
// グローバル領域に展開
phina.globalize();
// アセット
const ASSETS = {
// サウンド
sound: {
"bgm1":"載せたいサウンドのパス",
}
};
/*
* メインシーン
*/
phina.define("MainScene",{
// 継承
superClass:"DisplayScene",
// コンストラクタ
init: function(){
// 親クラスの初期化
this.superInit();
// ここに処理を書く
},
// 更新
update: function(){
},
});
/*
* メイン処理
*/
phina.main(function(){
// アプリケーションを生成
var app = GameApp({
// MainSceneから開始
startLabel: "main",
// アセット
assets: ASSETS
});
// fps表示
//app.enableStats();
// 実行
app.run();
});
SoundManagerのメソッド・プロパティ一覧
phina.jsでサウンドを操作するには、SoundManagerクラスを使います。
SoundManager.playMusic("bgm1");
という感じで使えます。
※追記:詳しい方から教えていただいたのですが、どうやら、SoundManagerはまだ未実装なものが多いそうです。
プロパティ
- volume
- play(name)メソッドで鳴らした効果音のボリュームの値が入ります。デフォルト値は0.8。
- musicVolume
ミュージックボリュームの値が入ってるんだと思います(いじっても変化が分からなかったのでよくわかりません)。デフォルト値は0.8。
後日追記: playMusic(name[,fadeTime,loop])メソッドで再生するミュージックのボリュームの値が入るようです。- muteFlag
- ミュート状態かどうか。真偽値が入ります。デフォルト値はfalse。
- currentMusic
- 多分、このプロパティにサウンドファイルを入れて、ここ経由でサウンドの操作をしているんだと思います(詳しいことはわかりません)。デフォルト値(つまりサウンドファイルを何も操作してない時)はnull。
メソッド
- play(name)
- サウンドを1回だけ鳴らします。効果音向き。
[引数]
name:アセットで指定した名前(雛型で言えば”bgm1″)を指定します。 - stop()
- 未実装
- pause()
- 未実装
- fade()
- 未実装
- setVolume(volume)
- ボリュームを操作できます。
[引数]
volume:数値を入れます。 - getVolume()
- 現在のボリュームを調べる時に使います。戻り値は数値で返されます。
- mute()
- ミュートする時に使います。
- unmute()
- ミュート解除する時に使います。
- isMute()
- 現在ミュート状態かどうか調べる時に使います。戻り値は真偽値で返されます。
- playMusic(name[,fadeTime,loop])
- サウンドを流す時に使います。
[引数]
name:アセットで指定した名前(雛型で言えば”bgm1″)を指定します。
fadeTime:1以上の値を入れれば、フェードインの度合いを指定できます。数値が大きいほどフェードインの度合いがゆっくりになります。省略可能です。
loop:ループさせるかどうかを真偽値で指定できます。省略可能です。省略した場合、ループします。 - stopMusic([fadeTime])
- サウンドを停止させる時に使います。
[引数]
fadeTime:1以上の値を入れれば、フェードアウトの度合いを指定できます。数値が大きいほどフェードアウトの度合いがゆっくりになります。省略可能です。 - pauseMusic()
- サウンドを一時停止させる時に使います。
- resumeMusic()
- サウンドが一時停止されていた場合、再開させる時に使います。
- setVolumeMusic(volume)
- ミュージックボリュームを操作する時に使うんだと思います(いじっても変化が分からなかったのでよくわかりません)。
[引数]
volume:数値を入れます。 - getVolumeMusic()
- 現在のミュージックボリュームを調べる時に使います。戻り値は数値で返されます。
サンプル
お借りした音楽素材:魔王魂
ソースコードです。
// グローバル領域に展開
phina.globalize();
// アセット
const ASSETS = {
// サウンド
sound: {
"bgm1":"載せたいサウンドのパス",
}
};
// 定数
const SCREEN_W = 640;
const SCREEN_H = 960;
/*
* メインシーン
*/
phina.define("MainScene", {
// 継承
superClass: "DisplayScene",
// コンストラクタ
init: function () {
// 親クラスの初期化
this.superInit();
// ここに処理を書く
this.backgroundColor = "black";
this.lightScreen = RectangleShape({
fill: "hsla(160,100%,78%,1.0)",
width: SCREEN_W,
height: SCREEN_H
}).addChildTo(this).setPosition(this.gridX.center(), this.gridY.center());
this.lightScreen.alpha = 0;
const self = this;
/*
* BGM再生・一時停止ボタン
*/
this.bgm1Btn = BGMPlayButton().addChildTo(this)
.setPosition(this.gridX.center(-7), this.gridY.center(-7));
this.bgm1Btn.setInteractive(true);
this.bgm1Btn.onpointend = function () {
if (SoundManager.currentMusic === null) {
self.myPlayMusic();
} else {
(this.label.text === "l l")
? self.myPauseMusic()
: self.myResumeMusic();
}
};
/*
* BGMタイトルラベルエリア
*/
this.bgm1Label = LabelArea({
text: "曲名",
fill: "white",
fontSize: 25,
width: 380,
height: 40
}).addChildTo(this)
.setPosition(this.gridX.center(2), this.gridY.center(-7));
/*
* BGMストップボタン
*/
const stopBtn = BGMStopButton().addChildTo(this)
.setPosition(this.gridX.center(-5.6), this.gridY.center(-7));
stopBtn.setInteractive(true);
stopBtn.onpointstart = function () {
this.fill = "hsla(160,100%,50%,1.0)";
this.stroke = null;
};
stopBtn.onpointend = function () {
this.fill = null;
this.stroke = "white";
self.myStopMusic();
};
/*
* ミュートボタン
*/
const mutebtn = Button({
text: "mute",
fontSize: 20,
fontColor: "white",
fill: null,
stroke: null,
cornerRadius: 25,
width: 50,
height: 50
}).addChildTo(this)
.setPosition(this.gridX.center(-4), this.gridY.center(-7))
.onpush = function () {
// ミュートしていたらミュート解除
if (SoundManager.isMute()) {
SoundManager.unmute();
this.fontColor = "white";
} else {
SoundManager.mute();
this.fontColor = "black";
}
};
/*
* ランダムノイズ
*/
const paths = [];
((SCREEN_H - 100) / 10).times(function (i) {
paths.push(Vector2(SCREEN_W / 2, i * 10 + 100));
}, this);
this.path = PathShape({
stroke: "white",
strokeWidth: 2,
fill:"white",
paths: paths
}).addChildTo(this);
this.path.alpha = 0.4;
// ウェーブ生成用カウント
this.waveCount = Math.randint(0, 30);
},
// 更新
update: function () {
if (!(SoundManager.currentMusic === null) && this.bgm1Btn.label.text === "l l") {
this.waveCount--;
if (this.waveCount < 0) {
this.waveCount = Math.randint(10, 40);
Wave().addChildTo(this).setPosition(Math.randint(0, SCREEN_W), Math.randint(100, SCREEN_H));
} else if (this.waveCount % 3 === 0) {
let x;
((SCREEN_H - 100) / 10).times(function (i) {
x = Math.randint(100, 500);
this.path.changePath(i, x, i * 10 + 100);
}, this);
}
}
},
// 再生
myPlayMusic: function () {
SoundManager.playMusic("bgm1", 600);
this.bgm1Btn.myPlay();
this.bgm1Label.update = function () {
this.scrollX += 2;
if (this.scrollX > 380) {
this.scrollX = -390;
}
};
this.lightScreen.tweener.clear().to({ alpha: 1 }, 200).play();
},
// 一時停止
myPauseMusic: function () {
SoundManager.pauseMusic("bgm1");
this.bgm1Btn.myPause();
this.lightScreen.alpha = 0;
},
// 再開
myResumeMusic: function () {
SoundManager.resumeMusic("bgm1");
this.bgm1Btn.myPlay();
this.lightScreen.alpha = 1;
},
// 停止
myStopMusic: function () {
SoundManager.stopMusic(1000);
this.bgm1Btn.myPause();
this.bgm1Label.scrollX = 0;
this.bgm1Label.update = function () { };
this.lightScreen.tweener.clear().to({ alpha: 0 }, 200).play();
((SCREEN_H - 100) / 10).times(function (i) {
this.path.changePath(i, SCREEN_W / 2, i * 10 + 100);
}, this);
}
});
/*
* BGM再生ボタン
*/
phina.define("BGMPlayButton", {
superClass: "RectangleShape",
init: function () {
this.superInit({
fill: null,
stroke: "white",
strokeWidth: 5,
cornerRadius: 1,
width: 50,
height: 50
});
this.label = Label({
text: "▶",
fontSize: 20,
fill: "white"
}).addChildTo(this).setPosition(0, 0);
},
// 再生
myPlay: function () {
this.fill = "hsla(160,100%,50%,1.0)";
this.stroke = null;
this.label.text = "l l";
},
// 一時停止
myPause: function () {
this.fill = null;
this.stroke = "white";
this.label.text = "▶";
}
});
/*
* BGM停止ボタン
*/
phina.define("BGMStopButton", {
superClass: "RectangleShape",
init: function () {
this.superInit({
fill: null,
stroke: "white",
strokeWidth: 5,
cornerRadius: 1,
width: 50,
height: 50
});
this.label = Label({
text: "■",
fontSize: 20,
fill: "white"
}).addChildTo(this).setPosition(0, 0);
}
});
/*
* メイン処理
*/
phina.main(function () {
// アプリケーションを生成
var app = GameApp({
// MainSceneから開始
startLabel: "main",
// アセット
assets: ASSETS
});
// fps表示
//app.enableStats();
// 実行
app.run();
});
参考にさせていただいたサイト・ページ一覧
[phina.js-Tips-38] 音の再生を中断・再開してみる
phina.jsで音ゲーを作ってみよう【前編】
phina.js/soundmanager.js at develop・phinajs/phina.js・GitHub
GitHub – phinajs/phina.js: phina.js is game library
本日 phina.js version 0.2 をリリースしました | phiary