ジャンル雑多なゲーム・ゲーム制作関連の色々な情報を取り扱っているブログ。最近はBlenderについてが中心。
[phina.js] サウンドの操作について

[phina.js] サウンドの操作について

[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();
});
Pocket

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください