ボクセルで何か作りたかったので、世界を掘って弾にするゲームを作った
- 8 分で読めます - 3648 文字きっかけはかなり雑で、「ボクセルで何か作りたい」だった。
立方体が積み上がった地形を眺めたり、掘った場所がへこんだり、崩れたりするものを一度作ってみたかった。
最初は、それだけで終わるつもりだった。
しかし、掘った素材を回収できるようにして、その素材を投げられるようにして、着地点へ積み直せるようにしたところ、「地形を弾薬に変えて炉心を守るゲーム」になっていた。
そうしてできたのが、ブラウザで遊べる短編アクションローグライト「砕界炉 // SAIKAIRO」である。
15×15の地形の中央にある炉心を守りながら、全5ウェーブを戦う。
1プレイ約5分ほど。
三つの面を描くと箱に見える
このゲームのプレイ画面は3Dに見えるが、Three.jsやWebGLは使っていない。
描画先はCanvas 2Dで、各ボクセルの上面、左面、右面を三つの多角形として塗っている。
ワールド座標を画面座標へ移す基本式は、次のようになっている。
実際のコードでは、この結果に画面揺れの量も加えている。
screenX = originX + (x - y) * tileWidth * 0.5;
screenY = originY + (x + y) * tileHeight * 0.5 - z * voxelHeight;x - yを横方向、x + yを縦方向へ使うと、地面の格子がひし形に並ぶ。
そこからzの分だけ上へずらし、同じ座標を基準に三面を描けば、アイソメトリック風の斜め見下ろし画面になる。
土壌、石材、晶体は、それぞれ上面と左右面に少し違う色を持つ。
光源計算を行わなくても、面ごとの明るさの差だけでそれらしく見える。

そのため、ゲーム本体のdependenciesに入っているパッケージは静的Webページを構成するためのAstroだけである。
TypeScript、Vitest、ESLint、Prettierなどは開発用時のみ使用される。
採掘した素材を弾と地形へ戻す
箱には、地形を採掘できる役割を持たせた。 左クリックで地形の一番上にあるボクセルを取り除くと、土壌、石材、晶体のいずれかが所持素材へ加わる。
回収した素材は、右クリックで投げられる。 投射した素材は敵への攻撃に使え、地形へ着弾すればその場所へ積まれる。
役割のつながりは単純である。
地形 → 所持素材 → 投射物 → 再配置した地形ゲーム内の見出しでは、これを「地形=資源=弾薬」と呼んでいる。 厳密に同じ一個のボクセルを追跡し、個数を保存する物理シミュレーションではない。 ボクセルという共通の表現と素材種が、地形、所持素材、弾、防壁の間を行き来する設計である。
クリックを0.8秒押し続けると、同じ操作が強化される。 左の長押しは十字範囲の採掘と敵部位への連鎖攻撃、右の長押しは十字状の建築と周囲への衝撃波になる。
使うボタンを増やさず、短押しと長押しへ役割を重ねた。
掘る、壊す、投げる、積むというゲームの中心は、左右のマウスボタンから外れない。

積み直した地形を消耗品の防壁にする
右クリックで積んだ地形は、見た目を変えるだけではない。 敵弾は前のフレーム位置から現在位置までの区間で地形との衝突を調べ、最初に当たったボクセルで止まる。
敵弾を受けたボクセルは壊れ、同じ素材の回収物になる。
防壁は攻撃を一度受け止め、壊れた後はまた拾って使える。
ただし、壁を積めば永久に安全になるわけではない。
敵は次のマスが二段以上高くて進めないと、進路上の一番上のボクセルを掘る。
敵弾を止めた場合とは違い、敵に掘られたボクセルは回収できない。
壁は地形であり、敵弾を一度止め、敵が掘り進むまでの時間を作る。 採掘と建築を同じ盤面へ置いたことで、防御にも素材の使いどころが生まれた。
敵の形を壊すと能力も止まる
敵も一つのHPを持つ塊にはしなかった。 脚、武器、装甲、核という部位の集まりとして作り、HPは各部位が持つ。
部位の色と機能は対応させている。
- 黄色の脚:壊した割合に応じて移動速度が下がる。
- シアンの武器:すべて壊すと遠距離射撃が止まる。
- 灰色の装甲:残っている間は、核を狙った攻撃を近くの装甲が受ける。
- 赤い核:壊すと敵を撃破する。

敵によって脚の本数は異なり、最終ウェーブの巨像(ボス)は脚が八本、武器が三基ある。
巨像では、残っている脚と武器を見て、移動を遅らせるか、射撃を止めるかを判断できる。
(というコンセプトではあるが、実際にはそれどころではないと思う…)
ボクセルは見た目の都合だけではない。 敵を機能ごとに分け、狙った場所を実際に取り除ける形にするためにも使っている。
高低差を作ると地形が敵へ落ちる
地形を自由に掘れるなら、極端な段差も作れる。
そこで、上下左右に隣接する地形との高低差が閾値を超えたとき、高い列の一番上にあるボクセルを最も低い列へ移す規則を加えた。
これがゲーム内でいう「崩落」である。
物理エンジンで地形全体を計算しているのではなく、隣接する列の高さを比べ、移動回数に上限を設けて順番に処理している。
崩落先に敵がいれば、落ちたボクセルが部位へダメージを与え、短時間動きを止める。 石材が落ちた場合は、追加ダメージも入る。

地面を掘る行為は、素材集めだけではなくなった。
崖を作るために地面を掘り、その崖を敵の頭上へ落とせる。
が、攻撃した方が圧倒的に早い。
物足りなくなって極太ビームを足す
最初は採掘と投射ができれば十分だと思っていた。
しかし、ウェーブを越えても戦い方が変わらず、物足りなかった。
そこで、ウェーブ終了後に三枚の「構造式」(強化要素)を提示し、一つ選んで強化する仕組みを加えた。
採掘範囲、崩落ダメージ、回収距離、石材の威力といった通常強化に加え、一回だけ取得できる四種類の が候補へ混ざる。
- 双撃因果核:左右クリックによる対敵ヒット数を2倍にする。
- 終端断層砲:断層波を、敵、敵弾、地形を貫く極太ビームへ変える。
- 死核連鎖:敵を倒したとき、周囲の敵へ連鎖攻撃を放つ。
- 位相断裁:ダッシュの軌跡を多段攻撃する斬撃帯へ変える。

特に「終端断層砲」は、ゲームバランスをかなり崩壊させる。
射線上の敵を連続で攻撃し、敵弾を消し、基盤層、炉心のセル、プレイヤーがいるセルを残して全て破壊する。

効果音もファイルではなく波形から作る
効果音も、録音済みの音源ファイルを再生する構成にはしなかった。
Web Audio APIのオシレーターとゲインを使い、操作のたびに短い波形を生成している。
例えば、ボクセルが壊れる音は素材ごとに周波数と波形を変えている。
土壌は低め、石材はさらに低い周波数にし、晶体には高い周波数のサイン波を使うことで、同じ破壊処理でも素材の違いが耳に残る。
採掘、投射、命中、被弾、ウェーブ開始、断層波、 OVERDRIVE 、極太ビームも、すべてコード上の周波数、長さ、波形、音量から鳴らす。
絵も音も、ほぼコードから出ている。
最後に
作り始めたときに決まっていたのは、ボクセルを描きたいということだけだった。
地形を掘れるようにし、採掘した素材を投げられるようにし、着地点へ積み直せるようにした。 積んだ地形が敵弾を止め、掘った段差が崩れ、敵も部位ごとのボクセルへ分かれた。
それぞれを別の仕組みとして足したというより、同じボクセル表現をどこまで使い回せるか考えた結果である。
ボクセルで何か作りたかった。
箱を掘れるようにして、投げられるようにして、積み直せるようにしたら、いつの間にかゲームになっていた。