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

# 前回のおさらい

さて、前回は以下のようなDSLで、単発の自機狙い弾を発射しました。

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

今回はもう少し記述を追加して、より複雑なパターンの攻撃を作ってみましょう。

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

# 今回のテーマに入る前に

今回のテーマに入る前に、すこしコードの修正を加えましょう。
と言っても１箇所だけ。
アプリケーションのFPS（フレームレート）を60に上げます。

ソースコード17行目付近に以下のコードを追加してください。

~~~js
...
tm.main(function() {
    app = tm.display.CanvasApp("#app");
    app.fps = 60; // FPSを60にする ←new!!
    app.resize(320, 320).fitWindow();
    app.replaceScene(MainScene());
...
~~~

tmlib.jsではFPSのデフォルト値が30となっていますが、この状態のままですとBulletMLとしては少々遅いので２倍にしておきます。

# 発射方向を変化させる

前回の例では、*fire*関数に*bullet*のみを与えていました。
実は、これは発射の方向と速度を省略したかたちとなっています。
この状態ではデフォルトとなる、自機狙い弾を速度１で発射するアクションになります。

もう少し凝った弾を発射するには、例えば以下のように書きます。

~~~js
var DANMAKU0 = new bulletml.Root({
    top: action([
        repeat(100, [
            fire(direction(23, "sequence"), bullet()),
            wait(1)
        ])
    ])
});
~~~

### repeatアクション

topアクションの直下階層に*repeat*という関数が追加されています。
*repeat*関数は、あるアクションを複数繰り返すときに使います。
第１引数に繰り返し回数、第２引数にアクションまたはアクションの配列を指定します。
この例では、繰り返し回数１００回、繰り返すアクションは*fire*と*wait*となっています。

*fire*関数には*direction*が追加されています。
*direction*関数は発射する方向を生成する関数です。
*direction*関数にはtypeが指定できます。typeには以下の３種類があります。

 - **aim** 自機を狙った方向
 - **absolute** 自機の位置に依存しない絶対方向（真上を０度とします）
 - **sequence** 直前に撃った方向との相対方向

今回の場合は**sequence**ですので、直前に撃った方向との相対方向に発射します。
単位は度です。
直前から23度ずれた方向に弾を撃ちます。

### waitアクション

*wait*アクションは一定時間何もせずに待機するアクションです。
単位はフレームです。
今回の場合は１フレーム待機します。

### 渦巻弾または回転砲台

ここまでの記述で、

 - 直前から23度ずれた方向に弾を撃ち
 - 1フレーム待つ

という動作を100回繰り返す動作が定義できました。 

ブラウザで動かしてみましょう。
くるくると回転しながら弾をばらまく攻撃を行うのが確認できるはずです。
![ss2.png](https://qiita-image-store.s3.amazonaws.com/0/30568/3906043f-6192-e49d-1e08-9378886b7c68.png)

このような攻撃を、渦巻弾とか回転砲台と呼ぶようです。
怒首領蜂大往生２面ボス「百虎」等、弾幕STGではよく使われています。

# 弾にアクションを定義する

これまでの例では攻撃主体（敵キャラ）にアクションを定義してきましたが、弾にもアクションを実行させることが出来ます。

弾幕定義を以下のように変更してみましょう。

~~~js
var DANMAKU0 = new bulletml.Root({
    top: action([
        repeat(100, [
            fire(bullet(actionRef("explosion"))),
            wait(200)
        ])
    ]),
    explosion: action([
        wait(100),
        changeSpeed(speed(0), 20),
        wait(20),
        repeat(8, [
            fire(direction("$rand * 360"), bullet())
        ]),
        vanish()
    ])
});
~~~

### actionRefアクション

*bullet*関数に引数として*actionRef*アクションが加わっています。
*actionRef*アクションは、プロパティ名を指定してアクションを参照する時に使います。
ここではexplosionという名前のアクションを参照しています。

*bullet*関数の引数に*action*または*actionRef*が与えた場合、発射後の弾にアクションを実行させることができます。
explosionプロパティの中では、*wait*、*changeSpeed*、*wait*、*repeat*、*vanish*という順にアクションを定義しています。

### changeSpeedアクション

*changeSpeed*アクションは弾の速度を、指定した*speed*に指定フレームかけて変化させます。
今回の例では、20フレームかけてスピードを０にするよう記述しています。

### vanishアクション

*vanish*アクションは弾を消すアクションです。

### パラメータに式を指定

*direction*の引数に "$rand * 360" という文字列が指定されています。
このように、数値を指定する場所では文字列によって式を記述することも可能です。
$randは0から1までの乱数を発生させる変数です。
ここでは、ランダムな方向に弾を発射するような指定となっています。

### 炸裂弾

explosionプロパティは、全体として

 - 100フレーム何もしない
 - 20フレームかけて停止する（速度を０にする）
 - 20フレーム（速度が0になるまで）待機
 - 以下を８回繰り返す
  - ランダムな方向へ弾を発射する
 - 弾を消す

というアクションとなります。
![ss3.png](https://qiita-image-store.s3.amazonaws.com/0/30568/48745037-519f-f678-022d-2b13a1d147fd.png)

このように、弾自身が複数の弾に分裂するような攻撃を、炸裂弾と呼ぶようです。
使用例としては、怒首領蜂真ボス「火蜂」や怒首領蜂最大往生３面ボス「源武」などがあります。

# その他のアクション

この他、BulletMLには以下の様なアクションがあります。

 - **changeDirection** 弾の進行方向を変化させます。
 - **accel** 弾を加速します
 - **bindVar** 変数に値を代入します。bulletml.js独自拡張のアクションです。
 - **notify** 攻撃主体にイベントの発生を通知します。bulletml.js独自拡張のアクションです。

くわしいリファレンスは以下を参照ください。

 - [BulletML Reference](http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/bulletml_ref.html)（本家 ABAさんによるBulletMLのリファレンス）
 - [daishihmr/bulletml.js wiki](https://github.com/daishihmr/bulletml.js/wiki)（執筆中です…）

# まとめ

今回は、より複雑な弾幕パターンの定義の仕方と、利用可能なアクションを紹介しました。
これらを組み合わせることで、さらに複雑で奥深い弾幕パターンを構築することが出来ると思います。
みなさんも熱く激しい弾幕をガンガン作ってみてください。

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