マガジン

ローグライクの作り方:ダンジョン自動生成の基礎

遊びをつくる

執筆にClaudeを使用しています。

小さな迷路の壁を一か所取り外し、通り道を確かめる模型。
記事のテーマをもとに制作したAI生成イメージ。実際のゲーム画面ではありません。

ローグライクの中心にあるのは「毎回違うダンジョン」です。プレイヤーが何度遊んでも飽きない理由はここにあり、作り手にとっても最初に乗り越えるべき壁がここにあります。手書きのマップは一度作れば終わりですが、自動生成は「アルゴリズムがマップを書く仕組み」を作ることになるため、設計の考え方が根本的に異なります。 この記事では、ブラウザゲームとして実装できる範囲で「部屋と廊下を自動配置する」仕組みに絞って解説します。乱数の使い方から、部屋同士をつなぐ廊下の生成、プレイアブルな難易度に整えるための調整まで、手順と判断基準を順番に示していきます。

この記事の目次
  1. ダンジョン自動生成の「最小単位」を決める
  2. 部屋の配置:乱数と制約の組み合わせ
  3. 廊下の生成:部屋をつなぐ最小の仕組み
  4. スタートとゴール、そして難易度の調整
  5. 実装の進め方:段階的に動かす
  6. 次の行動
  7. 参考資料

ダンジョン自動生成の「最小単位」を決める

自動生成を始める前に、マップを何で構成するかを決める必要があります。もっとも扱いやすいのはタイルグリッドです。縦横を固定サイズのセルに区切り、各セルを「床」「壁」「扉」などの種別で管理します。

タイルグリッドを選ぶ理由は、処理が単純だからです。セルの座標は整数で表せるため、当たり判定も「同じ座標にいるか」という比較になります。描画もセルのインデックスに応じた画像や色を塗るだけです。多くのクラシックローグライクがこの方式を採用しているのは、計算コストが低く、モバイル・ブラウザでも動作しやすいためです。

グリッドの大きさは最初は小さく設定するのが安全です。40×30程度であれば、生成処理を1フレーム内で完了させやすく、デバッグ時に全体を目視できます。生成ロジックが安定したあとで拡張するほうが、バグの発見が格段に早くなります。

部屋の配置:乱数と制約の組み合わせ

部屋を置く手順は次のようになります。

  1. 最小・最大の部屋サイズ(例:幅3〜8、高さ3〜6)を定数として決める。
  2. 乱数で幅・高さを決め、グリッド内にランダムな座標を選んで仮配置する。
  3. すでに置いた部屋と重なるか、端からはみ出すかを判定する。
  4. 重なりがなければ確定。重なるなら棄却して再抽選する。
  5. 試行回数の上限(例:200回)に達するか、目標部屋数(例:8〜12室)に達するまで繰り返す。

この「試行して棄却する」方式は拒絶サンプリングと呼ばれる考え方で、単純ですが実用的です。試行回数の上限を設けるのはループが終わらなくなる無限ループを防ぐためです。上限に達したとき部屋が少ししか置けなかった場合は、グリッドを広げるか最小部屋サイズを小さくするか、いずれかを調整します。

失敗しやすい点は「部屋同士の間隔」です。重なり判定を「ぴったり接触」まで許可してしまうと、廊下を引けない配置が生まれます。判定を1セル分余裕を持たせる(部屋の外周1マスも壁として確保する)だけで、廊下を通すスペースを確実に確保できます。

廊下の生成:部屋をつなぐ最小の仕組み

部屋を配置したら、すべての部屋がプレイヤーから到達できるようにつなぐ必要があります。もっとも実装が簡単で、ゲームとして成立しやすい方式はL字廊下です。

部屋Aの中心座標と部屋Bの中心座標を取り、水平方向を先に掘ってから垂直方向を掘る、あるいはその逆を乱数で選びます。掘った経路上のタイルをすべて「床」に書き換えるだけで廊下になります。処理が1関数に収まり、交差点も自動的に床になるため、複雑な処理が不要です。

つなぐ順番は「配置した順に隣の部屋とつなぐ」が最低限の方法です。ただしこれだけでは孤立する部屋は生まれないものの、ループのない一本道に近い構造になりやすいです。探索の楽しさを増やすには、ランダムに選んだ部屋ペアを数本追加でつなぎ、ループを意図的に作る工夫が有効です。追加する接続数はフロア面積や部屋数に応じて調整しますが、全部屋数の20〜30%を目安にすると過密にならずに済みます。

廊下の生成でよくあるミスは「壁を消しすぎる」ことです。部屋の外壁に沿って廊下を引くと部屋が拡張されたように見えてしまいます。掘る対象を「壁タイルのみ」に限定し、すでに床になっているタイルは変更しないという条件を入れておくと意図しない形状を防げます。

スタートとゴール、そして難易度の調整

マップが生成できたら、スタート地点とゴール(次の階段など)を配置します。よくある方法は「最も離れた2部屋の中心を選ぶ」です。全部屋ペアの距離を計算してもよいですが、単純に「配置順で最初と最後の部屋」にするだけでも、視覚的にはそれらしい配置になります。

難易度の調整は主に次の3つの変数で行います。

  • フロアの深さ(階数):深くなるほど敵の強さや数を増やす係数をかける。
  • 部屋の数と大きさ:深いフロアほど広い部屋・多い部屋にして探索量を増やす。
  • 視界の制限:タイルを「未探索・探索済み・視界内」の3状態で管理し、視界内だけを描画することで情報制限による難易度を作る。

視界の実装は難易度調整と同時にゲームの雰囲気を大きく変えます。完全な視線計算(Bresenhamアルゴリズムなど)は精度が高いですが計算コストもかかります。最初はプレイヤーを中心とした正方形の範囲を視界とする「タクシー距離」方式で十分で、後から差し替えを検討する順序が安全です。

マップを生成したあとに「プレイヤーがゴールに到達できるか」を自動検証する仕組みも重要です。幅優先探索(BFS)でスタートからゴールまでの経路が存在するか確認し、存在しなければマップを再生成するという処理を入れることで、詰んだマップがプレイヤーに渡るのを防げます。これを省くと、接続ミスに気づかないまま公開してしまうリスクがあります。

実装の進め方:段階的に動かす

自動生成はまとめて作ろうとすると、どこで壊れているかが分からなくなります。次の順で動作確認しながら進めることを推奨します。

  1. グリッドを全部壁で初期化し、1部屋だけ床に書き換えて描画する。
  2. 複数部屋をランダム配置して、重なり判定が正しく動くか確認する。
  3. 2部屋間のL字廊下を1本引いて、経路がつながるか目視する。
  4. 全部屋を順につなぎ、BFSで到達確認を取る。
  5. スタート・ゴールを配置してプレイヤーを動かせるようにする。
  6. 視界制限・敵配置・アイテム配置を追加する。

各ステップで画面に描画できる状態を保つことが大切です。マップをそのままHTML Canvasやdivグリッドで描画するデバッグ用の表示を最初から作っておくと、視覚的に問題箇所を特定しやすくなります。

自動生成ロジック自体をモジュールとして独立させ、ゲームの他のロジックから切り離して管理すると、アルゴリズムの差し替えや改良が後から容易になります。最初に作った「部屋置きと廊下」方式が安定したら、BSP分割やセルオートマトンなど別の手法へのリプレースも視野に入れられます。

次の行動

ここまでの手順をもとに、まず40×30のグリッドに3〜5部屋を配置してブラウザ上に描画することから始めてみてください。作ったブラウザゲームはHTMLファイルまたはZIPファイルとしてアソバースに投稿・公開できます。動くところまで作ったら、ほかのプレイヤーに遊んでもらいながら難易度と生成パラメーターを調整するサイクルに入るのが最短の改善方法です。

参考資料

  • Roguelike Development(RogueBasin Wiki)
  • Red Blob Games — Grids and Graphs
  • MDN Web Docs — Canvas API