最近競技プログラミングネタばっかりだったので、久々にゲームをつくります。ゲームのルールですが、自機を操作して領域を囲い込み、全体の75%を囲い込んだらクリアです。敵が自機や自機の軌跡に触れたらミスとなります。ただし自機の軌跡であっても囲い込まれた領域の外周であればミスにならないというルールにします。

ちょっと言葉では説明しにくいですが、こんな感じのゲームです。

基本的な仕様

自機は静止することができず、つねに上下左右4方向のいずれかに移動しつづける。

8ピクセルごとに格子点を設置し、自機がここにいるときのみ方向転換可能とする。

自機が移動したら格子点間を赤い線で結ぶ。線で閉路が構成されたときは閉路を構成する赤い線を白い線に変更し、内部の領域を青く塗りつぶす。自機や敵は青い領域内に進入することはできない。

自機や赤い線に敵が触れたときはミスとする。白い線に触れた敵は破壊される。

青い領域が全体の75%を超えたらステージクリアとする。

だいたいこんな感じです。では作っていきましょう。

HTML部分

HTML部分を示します。

style.css

グローバル定数

グローバル定数は以下のとおりです。

index.js

Vertexクラスの定義

格子点を管理するためにVertexクラスを定義します。

Playerクラスの定義

自機の移動や描画処理をするためにPlayerクラスを定義します。

Enemyクラスの定義

敵の移動や描画処理をするためにEnemyクラスを定義します。

Sparkクラスの定義

爆発時の火花の移動や描画処理をするためにSparkクラスを定義します。

Gameクラスの定義

Gameクラスを定義します。

初期化に関する処理

コンストラクタと関連する関数を示します。

最初にコンストラクタを示します。

格子点を初期化する処理を示します。自機が移動できる範囲に CELL_SIZE ピクセルごとに格子点を置き、座標からそのオブジェクトを取得できるようにしておきます。

GetVertex関数は座標からVertexオブジェクトを取得するための関数です。

ゲーム開始の処理

ゲーム開始時の処理を示します。

自機の移動・方向転換

自機の移動したり方向転換するための処理を示します。

SetPlayerDirct関数は自機の次の方向をセットします。自機が格子点(方向転換が可能な場所)に到達したときに実際に方向転換の処理がおこなわれます。

MovePlayer関数は自機を移動させ、自機が方向転換可能な位置にいる場合は方向転換の処理をおこないます。また自機が格子点にいるときは現在セットされている方向にこれ以上移動できないかもしれません。その場合は移動可能な方向を取得してそれをランダムにセットします。

また自機が格子点にいる場合、赤い線と白い線で新たに囲い込まれた領域が存在するかもしれません。そこでまず、自機が最後に訪問した格子点と新たに訪問した点がつながっていないならつなぎます。これによってかつて訪問した格子点や囲い込まれている領域の境界とつながったのであれば、閉路が形成された ⇒ 新たに囲い込まれた領域が存在するということなので、OnCreatedCycle関数(後述)を呼び出して囲い込まれた領域を調べます。

閉路検出の処理

自機が格子点に到達したとき、これによって閉路が形成されたら囲い込みの処理をおこないます。

深さ優先探索でつながっている格子点をたどっていき、閉路があるならこれを取得します。閉路は複数存在するので、すべて取得します。

取得された閉路の内部はすでに囲い込まれた領域として認識されている部分もあるし、新たに囲い込まれた領域も含んでいます。そこで差分を調べます。

格子点と格子点が繋がっている場合、両者のあいだに線分を描画しなければなりませんが、その色が赤なのか白なのかを調べる処理を示します。

ステージクリア判定

ステージクリア判定の処理を示します。

囲い込まれた領域が全体の 75%以上であれば、一時的に描画を停止して、ボーナス点を加算したあと自機と敵の状態をリセットしているだけです。

敵の移動

敵を移動させる処理を示します。

1秒おきに敵の数が 5 + ステージ数 より小さければフィールドの端に敵を出現させ移動させます。そしてフィールドから外にでたら取り除きます。

火花の生成と移動

火花を生成し移動させる処理を示します。

生成の処理は引数の座標に 16 個のオブジェクトを生成して初速をランダムに設定します。移動は速度に応じて移動させ、Life が 0 になったらリストから削除します。

当たり判定

当たり判定の処理を示します。

敵が自機や赤い線に接触しているかどうかを調べています。

自機死亡時の処理

自機死亡時の処理を示します。

自機死亡時に残機があれば残機 1 を減らしてすでに囲い込まれた領域が存在しないときは中央に、存在するときは領域の境界線上からランダムに位置を選択してその位置に自機を復活させてゲームを継続します。同時に赤い線をすべて消します。自機がない場合はゲームオーバーの処理をおこないます。

更新処理

更新処理を示します。前回の更新から 16 ms 以上経過している場合のみ更新処理をおこないます。

描画処理

自機、敵、境界線、自機の軌跡を描画する処理を示します。

スコアランキングへの登録

スコアランキングに登録する処理を示します。

ページが読み込まれたときの処理

ページが読み込まれたときの処理を示します。

以下はレンジスライダーでボリュームコントロールができるようにするための定番の処理です。