第17回 人界剣「悟入幻想 -Easy-」 (4)

今回はいよいよ弾を撃たせてみます。これでスペルは完成します。

要旨

それではいってみましょう。

コメントアウト

では、残りの弾幕を完成してしまいましょう。

と...その前に、今回は妖々夢でやってる通りに作るだけなので調整する必要ないのですが、弾幕を色々と調整するには最初の動作が長すぎると思います。なので、弾を追加する前に、最初のあたりの動きは全部すっ飛ばして、いきなり左上に移動するように一時的に変更しておきましょう。

この変更は簡単で、standBy から slash までを実行しなければいいだけです。ただ、これらの処理を消してしまうと、また復帰させるのが面倒です。「消したいけど、簡単に復帰したい」ということを実現するには、これらをコメントにしてしまえばOKです。

// メインタスク
task TMain {
    yield;

/*
    standBy;
    levitate;
    concentration;
    slash;
*/
    ready;

    loop {
        move;
        shot;
    }
}

第12回で少しだけ話したように、/**/ で囲んだところはコメントになります。このように、コメントにすることである部分を実行しないようにすることを、コメントアウトと言います。一時的に実行しないようにする他、いくつかの処理を試したいときに1つずつ有効にしてみたり、作業領域代わりにしたりすることもあります。

自機狙い 3-way 弾& 6-way 弾

それでは、弾を撃たせてみましょう。先ずはボスに弾を撃たせるサブルーチン shot を実装します。

この弾は自機を狙った 3-way 弾と、自機の反対側に放たれる 6-way 弾になります。弾は7回発射され、それぞれ速度が違います。3-way 弾の角度間隔は速い方から遅い方までで 36〜42 度まで 1 度ずつ変化します。一方、6-way 弾の角度間隔は 30 度固定です。

n-way 弾のルーチンは、前のものを少し改造したものを使えばいいでしょう。

// n-way 弾を撃つ関数
//   v     : 発射速度
//   dir   : 弾を撃つ方向
//   way   : 弾の数
//   span  : 弾の間隔(角度)
//   delay : 発射遅延時間
function nway(v, dir, way, span, delay) {
    let angle  = dir - (way - 1) / 2 * span;    // 発射角

    loop(way) {
        CreateShot01(GetX, GetY, v, angle, RED02, delay);
        angle += span;
    }
}

少し引数が変わってますが、本質は特に変わっていません。

で、この n-way 弾を撃つわけですが、引数 dir に渡す値が問題になります。これは敵から自機への方向なのですが、これはどうやって取得すればいいのでしょうか?

ここで、第10回を思い出してみましょう。

具体的には、atan2 はある点 (x1, y1) から別の点 (x2, y2) を見た時の角度を取得する場合によく使われます。先ず、(x1, y1) を原点と考えると、もう1つの点の座標は (x2 - x1, y2 - y1) となります。原点と (x2 - x1, y2 - y1) を結んだ線分の傾きは (y2 - y1)/(x2 - x1) なので、点 (x1, y1) から点 (x2, y2) を見た時の角度は atan2(y2 - y1, x2 - x1) になります。

というわけで、

atan2(GetPlayerY - GetY, GetPlayerX - GetX)

とすれば、ボスから自機への方向を取得できることになります。ただ、自機狙い弾はよく作られるので、ボスから自機への方向を取得する専用の関数 GetAngleToPlayer が用意されています(内部的には同じようにして方向を取得していると思います)。

というわけで、shot は次のようにすればいいわけです。

// way 弾を発射
sub shot {
    let base    = GetAngleToPlayer;      // 基準角
    let angle   = 36;                    // 自機狙い 3-way 弾の間隔
    let nShot   = 7;                     // 各弾を撃つ回数
    let wShot   = 5;                     // 弾を撃つ間隔
    let wBefore = 60 - wShot * nShot;    // 発射後に待つ時間
    let v       = 1.5;                   // 発射速度
    let delay   = wShot * nShot;         // 発射遅延時間

    setGraphicPose;
    wait(wBefore);

    loop(nShot) {
        nway(v, base + 180, 6, 30, delay);
        nway(v, base, 3, angle, delay);
        wait(wShot);

        angle += 1;
        v     -= 0.15;
        delay -= wShot;
    }
}

仕様が少し複雑なので少々ややこしいですが、大まかな流れは「弾を撃つ → パラメータを変化 → 弾を撃つ → パラメータを変化 → ……」という風になっています。waitをパラメータを変化させる前に置くか後に置くかは好きにして構いません。

以上をまとめたプログラムをここに置いておきます。実際に実行してみて、自機狙い弾を撃ってくることを確認してみてください。

ランダム弾

最後に、下からほぼ真上に放つランダム弾を作りましょう。ボスの動きと無関係に常に発射されるので、マイクロスレッドを使ってボスの動きとは並列的に撃たせるといいでしょう。

// メインタスク
task TMain {
    yield;

/*
    standBy;
    levitate;
    concentration;
    slash;
*/
    ready;

    TShotToAir;

    loop {
        move;
        shot;
    }
}

// 下から上へ常に発射される対空弾
task TShotToAir {
    let wShot = 8;    // 発射間隔

    loop {
        CreateShot01(rand(GetClipMinX, GetClipMaxX),
                     yLaser, 1, rand(-92, -88), BLUE11, wShot);
        wait(wShot);
    }
}

8 フレーム毎にほぼ真上(-90 度)に弾を撃っているだけです。発射点は、剣戟のある位置のどこかになります。

これで弾を撃つ部分は完成です。確認してうまくいってることが分かれば、TMain のコメントアウトを解除すれば、弾幕は完成です。

完成したプログラムをここに置いておきます。本物に近い感じで遊べると思います。

要旨

今回で弾幕は完成しましたが、次回はもう少しだけスクリプトに手を加えたいと思います。

第16回 | 第18回 | 目次へ戻る

この講座はロベールが作成しました。
inserted by FC2 system