Three.jsで3D描画をするのであれば

Scene
レンダラー
ライト
カメラ

が必要です。

幅は600、高さは400とします。それからスコアや自機、ボスのlifeが表示される部分は2Dになるのですが、両者を混在させる方法がわからなかったのでcanvasを3つ作りました。真ん中にあるのが3D描画をする部分です。

現在ゲームはどのような状態にあるのか、対ボス戦なのか、通常の戦闘なのか、ゲームオーバーになっている状態なのか。それを格納するのがGameStatusという変数です。ページにアクセスしたらいきなりはじまるのはおかしいので、最初は Status.GameOver;にしています。

最初は第一ステージからはじまります。

自機オブジェクトと自機から発射された弾丸、敵と敵が発射した弾丸、爆発の火球を格納する配列、背景色を格納する変数を用意します。

ページが読み込まれたら初期化の処理をおこないます。またキー操作に対応できるようにしておきます。

では初期化の処理をみてみましょう。init()関数で初期化の処理をおこないます。上下のCanvasのコンテキストを取得してメインのCanvasのレンダラーを作成します。そしてカメラと光源を生成します。

それから3Dっぽさを演出するために緑色の直線を縦横に描画します。そして自機オブジェクトを作成しますが、Sceneにはまだ追加しません。ゲームが開始されたときに追加し、自機が爆破されたら取り除きます。

3Dっぽさを演出する(?)ための直線を描画するための関数です。

直線を描画するためには

var line_geometry = new THREE.Geometry();

といった処理が必要だよと多くのサイトに書かれているのですが、これだとエラーになってしまいます。これはTypeScriptの側に問題があるようです。そこで前の行に //@ts-ignore をつけています。これでうまくいきます。

次にアニメーション関連の処理です。ここではオブジェクトの移動や当たり判定をおこなっています。

MoveLines()は、フィールドに描画されている縦横の直線を手前に向かって移動させ自機が前に移動しているように見せかけるための関数です。

CheckMyBurretsHit関数は自機から発射された弾丸は敵に命中したかを判定するものです。オブジェクトの中心同士の距離とオブジェクトの幅の半分の合計を比較することで当たり判定をしています。敵に命中した場合は敵のlifeをひとつ減らし、自作関数のBurretHit関数を呼び出して敵の種類や状態によって適切な処理をおこないます。

CheckEnemyBurretsHit関数とCheckJikiCollidedEnemis関数は敵の攻撃が自機にダメージを与えるかを判定します。ダメージをうけたときはJikiDamageという自作関数で処理をおこないます。

以下の関数はオブジェクトの移動にかんするものです。なにもしないと当たらなかった弾丸はどこまでも飛び続けます。描画する必要がないところまで飛んでしまった弾丸はSceneと配列からはずします。また撃墜された敵や爆発がおわって不要になった火球も取り除きます。

新しい敵をつくるための関数を示します。ただボス戦がはじまる直前やボスを倒した直後は一時的に敵の出現をとめます。isIntervalフラグがセットされているときは新しい敵はつくられません。

新しいステージがはじまってtickCount > BeginBossTickCountとなり、ザコ敵もいなくなったらしばらくのインターバルの後にボスが出現して対ボス戦がはじまります。

メインのCanvasの描画処理がおわったらスコアや自機のlifeなどの情報を上下のCanvasに表示させます。以下はそのための関数です。

以下は自機から発射された弾丸が命中した場合の点数加算の処理と爆発の処理に関する関数です。どんな敵に命中したのか、敵がうけたダメージによって爆発の規模を変えます。

自機が被弾したときの処理に関する関数を示します。被弾時に立て続けにやられないように一時的に「無敵状態」にします。また背景を一瞬赤くします。そして自機のlifeが0になったらゲームオーバーです。

ゲームオーバーになったらその旨画面上に表示します。

次にキーが押されたときの処理に関する関数を示します。

方向キーが押されたらその方向に移動できることを示すフラグ(Jikiクラス内にある)をセットし、離されたらクリアします。また弾丸発射はスペースキー、ゲームスタートはSキーです。

あとはコンパイルして.jsにしてHTMLファイルに書けば完成です。

生成されたjsは次回示します。