tmlib.jsとbulletml.jsを使って弾幕STGを作る(1)

本記事は、弾幕記述言語BulletMLをJavaScript上で処理するためのライブラリ「bulletml.js」を使い、弾幕STGを作るためのチュートリアルです。

[今回作成するソースコードはこちら](https://github.com/daishihmr/bulletml.js-tutorial/archive/part1.zip)

# ライブラリ

まずは必要なライブラリから。
tmlib.jsとbulletml.jsの最新版をGitHubからダウンロードしてください。

 * [tmlib.js v0.3.0](https://github.com/phi-jp/tmlib.js/archive/develop.zip)
 * [bulletml.js v0.5.1](https://github.com/daishihmr/bulletml.js/archive/v0.5.1.zip)

ダウンロードした各アーカイブから、

 * tmlib.js
   * build/tmlib.js
 * bulletml.js
   * build/bulletml.js
   * build/plugins/tmlib.bulletml.js

を作業フォルダへコピーしてください。

# 雛形

ゲームの雛形を作成します。

作業フォルダにindex.htmlを作成し、以下のコードを記述します。

~~~html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
</head>
<body>

<canvas id="app"></canvas>

<script type="text/javascript" src="tmlib.js"></script>
<script type="text/javascript" src="bulletml.js"></script>
<script type="text/javascript" srC="tmlib.bulletml.js"></script>

<script>
tm.main(function() {
    app = tm.display.CanvasApp("#app");
    app.resize(320, 320).fitWindow();
    app.replaceScene(MainScene());
    app.run();
});

tm.define("MainScene", {
    superClass: "tm.app.Scene",

    init: function() {
        this.superInit();
        this.fromJSON({
            children: {
                enemy: {
                    type: "tm.display.CircleShape",
                    init: [50, 50],
                    x: 320 * 0.5, y: 320 * 0.1
                },
                player: {
                    type: "Player",
                    x: 320 * 0.5, y: 320 * 0.9
                }
            }
        });
    }
});

tm.define("Player", {
    superClass: "tm.display.TriangleShape",

    init: function() {
        this.superInit(25, 25);
        this.boundingType = "circle";
        this.raduis = 2;
        Player.SINGLETON = this;
    },
    update: function(app) {
        var kb = app.keyboard;
        this.position.add(kb.getKeyDirection().mul(4));
    }
});
Player.SINGLETON = null;
</script>

</body>
</html>
~~~

index.htmlをブラウザで開いてみてください。

![ss.png](https://qiita-image-store.s3.amazonaws.com/0/30568/8d60102d-4f81-8d20-959f-e3a45d145413.png)

上側の赤いマルが敵で、下側の緑の三角が自機です。

現状、自機をキーボードのカーソルキーで上下左右に動かすことができるはずです。

# 弾幕発射準備

攻撃パターンを敵に実行させる前に、いくつか設定を行います。

MainSceneのコンストラクタ内（ソースコード41行目付近）に以下の記述を追加します。

~~~js
var params = {
    target: this.player,
    createNewBullet: function(runner) {
        tm.bulletml.Bullet(runner).addChildTo(this);
    }.bind(this)
};
~~~

paramsというオブジェクトを作り、中にいろいろ書いています。

**target**

弾幕が狙うターゲットを指定します。ここでは自機を設定しています。

**createNewBullet**

弾が生成されるときに呼び出される関数です。*runner*という、フレームごとに更新される弾の位置を管理するオブジェクトが引数として渡ってきます。
ここでは単純に、*tm.bulletml.Bulletクラス*のインスタンスを生成し、シーンに追加しています。

# 最も簡単な弾幕（自機狙い弾を１発だけ）

いよいよ弾幕の実装に入ります。

スクリプトに以下のコードを追加します。

~~~js
bulletml.dsl();
var DANMAKU0 = new bulletml.Root({
    top: action([
        fire(bullet()),
    ]),
});
~~~

そしてMainSceneのコンストラクタに以下の記述を追加します。

~~~js
this.enemy.startDanmaku(DANMAKU0, params);
~~~

ここまでできたら、再度ブラウザで開いてみてください。

![ss1.png](https://qiita-image-store.s3.amazonaws.com/0/30568/c33fa2f0-88a3-9101-3967-2e083c1e38e3.png)

赤い弾が１発だけ、自機に向かって発射されると思います。

# bulletml.js DSLによる弾幕の定義

上記で追加したコード

~~~js
bulletml.dsl();
var DANMAKU0 = new bulletml.Root({
    top: action([
        fire(bullet()),
    ]),
});
~~~

について解説していきましょう。

最初の*bulletml.dsl();*はDSL用の関数をグローバルに展開する関数です。
グローバル領域を汚染したくない場合は、下記のように書くことも可能です。

~~~js
var $ = bulletml.dsl;
var DANMAKU0 = new bulletml.Root({
    top: $.action([
        $.fire($.bullet()),
    ]),
});
~~~

*bulletml.Root*クラスは弾幕定義の大枠となるクラスです。
このオブジェクトをスプライト等のオブジェクトに*startDanmaku*メソッドで渡すことにより、弾幕パターンの実行が始まります。

*top*プロパティは、弾幕定義の中で最初に実行されるアクション（後述）です。
最初に実行したいアクションが複数ある場合（並列実行したい場合）は、*top0*、*top1*、*top2*...というように定義することも出来ます。

*action*はいくつかの処理をひとまとめにした「アクション」というオブジェクトを生成します。
BulletMLはこのアクションの組み合わせで記述されます。
攻撃主体である敵キャラの他、弾にもアクションを実行させることも出来ます。

*fire*は発射処理オブジェクトを生成します。
*fire*関数は引数として弾（*bullet*）、方向（*direction*）、スピード（*speed*）等を取ることが出来ます。
それぞれの詳細は後述します。

*bullet*は弾オブジェクトを生成します。
*bullet*関数は引数としてアクション（*action*）、アクションへの参照（*actionRef*）等を取ることが出来ます。
それぞれの詳細は後述します。

BulletMLは本来、XML形式で記述される言語です。
bulletml.jsでは本来のXMLを読み込むことも出来ますが、ここではより簡単に弾幕定義を行えるよう、DSLを使っています。
JavaScriptで記述されたDSLを使うことで、BulletML断片の部品化が容易に行えるようになり、N-way弾やウィップ弾などの基本となる弾幕や、それらを組み合わせたより複雑な弾幕を自在に作成できるようになります。

# まとめ

今回はbulletml.jsをtmlib.js上で動作させるための最低限のプログラムと、最も単純な弾幕をDSLを使って定義する方法を紹介しました。

次回以降では、より複雑な弾幕パターンの作成方法について解説していきます。

[今回作成したソースコードはこちら](https://github.com/daishihmr/bulletml.js-tutorial/archive/part1.zip)