Skip to content

ダンジョン生成

概要

Rift Survivors のダンジョンは毎回プロシージャル生成される。BSP(Binary Space Partitioning)アルゴリズムをベースに、部屋の配置と通路の接続を自動生成する。

生成アルゴリズム: BSP(Binary Space Partitioning)

アルゴリズム全体フロー

BSP分割の詳細

分割パラメータ

パラメータ説明
全体サイズ80×80 unitsダンジョン全体の大きさ
最大分割回数5回再帰の深さ上限
最小区画サイズ10×10 unitsこれ以下は分割しない
分割比率0.4〜0.6分割位置のランダム幅
分割方向交互(横→縦→横...)偏りを防ぐ

分割アルゴリズム(疑似コード)

typescript
interface BSPNode {
  x: number; y: number;
  width: number; height: number;
  left?: BSPNode;
  right?: BSPNode;
  room?: Room;
}

function splitBSP(node: BSPNode, depth: number): void {
  if (depth >= MAX_DEPTH) return;
  if (node.width < MIN_SIZE * 2 && node.height < MIN_SIZE * 2) return;

  // 分割方向を決定(面積比で判断、近い場合はランダム)
  const splitHorizontally = node.width < node.height
    ? true
    : node.width > node.height
      ? false
      : Math.random() > 0.5;

  if (splitHorizontally) {
    const splitY = node.y + lerp(node.height * 0.4, node.height * 0.6, Math.random());
    node.left  = { x: node.x, y: node.y, width: node.width, height: splitY - node.y };
    node.right = { x: node.x, y: splitY, width: node.width, height: node.y + node.height - splitY };
  } else {
    const splitX = node.x + lerp(node.width * 0.4, node.width * 0.6, Math.random());
    node.left  = { x: node.x, y: node.y, width: splitX - node.x, height: node.height };
    node.right = { x: splitX, y: node.y, width: node.x + node.width - splitX, height: node.height };
  }

  splitBSP(node.left, depth + 1);
  splitBSP(node.right, depth + 1);
}

部屋配置ルール

各リーフノード内にランダムサイズの部屋を配置する。

部屋の幅  = random(区画幅 × 0.5, 区画幅 × 0.9)
部屋の高さ = random(区画高さ × 0.5, 区画高さ × 0.9)
部屋の位置 = 区画内でランダムにオフセット(壁から最低1unit離す)
パラメータ
部屋最小サイズ6×6 units
部屋最大サイズ区画の90%
壁厚0.5 units
生成される部屋数8〜16部屋(分割回数に依存)

部屋タイプ

部屋タイプ一覧

タイプ出現数説明視覚的特徴
スタート部屋1プレイヤーの初期位置明るいネオン照明、安全地帯
戦闘部屋4〜8敵がスポーンする主要エリア暗い照明、赤い警告ライン
エリートアリーナ1〜2エリート敵が出現する大部屋紫のオーラ、広い空間
ボスアリーナ0〜1ボス戦用の最大部屋特殊BGM、閉鎖ゲート
宝物部屋1〜2高レアリティ武器の宝箱金色のライティング
回復部屋1〜2HP回復+バフアイテム緑色のライティング
ポータル部屋1脱出ポータルの出現場所シアン色のリフトエフェクト

部屋タイプの配置ルール

距離計算: 部屋間の距離はBSPツリー上のホップ数(部屋中心間のマンハッタン距離ではなく、通路を通った実際の経路長)で算出する。

通路生成

BSPツリーの各内部ノードにおいて、左右の子ノードに属する部屋同士を通路で接続する。

通路パラメータ

パラメータ
通路幅2.0 units
通路形状L字型(水平→垂直 or 垂直→水平)
接続方式各部屋の最寄りの辺の中点を結ぶ

通路生成アルゴリズム

typescript
function connectRooms(roomA: Room, roomB: Room): Corridor {
  const pointA = roomA.center;
  const pointB = roomB.center;

  // L字型の通路を生成(ランダムに水平→垂直 or 垂直→水平)
  if (Math.random() > 0.5) {
    // 水平→垂直
    return [
      { x1: pointA.x, y1: pointA.y, x2: pointB.x, y2: pointA.y }, // 水平
      { x1: pointB.x, y1: pointA.y, x2: pointB.x, y2: pointB.y }  // 垂直
    ];
  } else {
    // 垂直→水平
    return [
      { x1: pointA.x, y1: pointA.y, x2: pointA.x, y2: pointB.y }, // 垂直
      { x1: pointA.x, y1: pointB.y, x2: pointB.x, y2: pointB.y }  // 水平
    ];
  }
}

環境バリエーション

テーマ

ダンジョンのビジュアルテーマはミッション難易度と進行度で決定される。

テーマ解放条件色調環境ハザード
データグリッド初期シアン / ブルーなし
ネオンアーケード永続Lv3以降ピンク / マゼンタ電撃トラップ(床が周期的に通電)
グリッチコア永続Lv6以降パープル / グリーン不安定床(一定時間で崩壊)
ヴォイドリフト永続Lv10以降黒 / 赤次元亀裂(床から出現する攻撃)

環境ハザード

ハザードダメージ発動間隔予兆
電撃トラップHP 5%/秒8秒ON/4秒OFF床の点滅(2秒前)
不安定床即死(落下)プレイヤーが乗って3秒後亀裂のひび割れ(1秒前)
次元亀裂ATK 30固定5秒間隔床の赤い円(1.5秒前)

環境オブジェクト

オブジェクト機能破壊可能
データモニュメント触れるとEXPジェム(大)ドロップYes
エネルギーピラー一定時間ごとにHP回復フィールド展開No
破壊可能な壁攻撃で破壊、ショートカット生成Yes(HP 50)
爆発バレル攻撃で爆発(半径2.0, 50ダメージ, 敵味方問わず)Yes(HP 1)

ミニマップ

ダンジョン探索中はHUD上にミニマップを表示する。

表示要素アイコン/色
現在位置白い矢印
訪問済み部屋薄い灰色
未訪問部屋非表示
通路薄い灰色のライン
ボスアリーナ赤いアイコン
宝物部屋金色のアイコン
脱出ポータルシアンのアイコン
敵(ミニマップ内)赤い点

ミニマップサイズ: 画面右上 150×150px。M キーで全体マップ表示に切り替え。

シード値によるリプレイ可能な生成

シード値の役割

ダンジョンの全要素はシード値から決定論的に生成される。同じシード値を使えば、完全に同じダンジョンが再現される。

typescript
interface DungeonSeed {
  seed: number;              // 乱数シード値
  mission: Mission;          // ミッション内容
  cycleState: CycleState;    // サイクル状態(ボスヒント配置に影響)
}

function generateDungeon(dungeonSeed: DungeonSeed): Dungeon {
  const rng = createSeededRNG(dungeonSeed.seed);
  // BSP分割、部屋配置、敵配置、アイテム配置を全て rng から生成
  // → 同じシードなら同じ結果
}

リベンジシステムとの連携

敗北時にシード値をlocalStorageに保存。リベンジポータルから再挑戦すると同じシード値で生成。

要素リベンジ時の再現
マップ構造完全に同じ
敵の種類・配置完全に同じ
ボスの種類・弱点完全に同じ
ドロップテーブル同じ(乱数は再生成)
ヒントの配置完全に同じ

プレイヤーが変更できるもの

リベンジ時にマップは固定だが、プレイヤーのビルドは自由に変更できる。

  • レベル(前回より成長している可能性あり)
  • 装備(ロビーで変更可能)
  • スキル選択(レベルアップ時に異なるスキルを選べる)

→ 同じダンジョンに対して「最適なビルド」を試行錯誤する楽しさ

サイクル内のヒント配置

5回サイクルの2〜4回目のダンジョンには、ボスの弱点ヒントが自動配置される。 ヒントの配置位置もシード値から決定論的に決まる。

サイクル内潜入回数とヒント配置:
  1回目: ヒントなし
  2回目: テキストヒント1個(壁の碑文)
  3回目: アイテムヒント1個(ボスの欠片ドロップ)
  4回目: 行動ヒント(ミニボスが弱点属性で大ダメージを受ける)
  5回目: ボス戦(弱点を突いて撃破)

パフォーマンス考慮

Three.jsでの実装指針

項目方針
床・壁のメッシュタイルベースのInstancedMesh で一括描画
通路同一マテリアルのメッシュを結合(BufferGeometry merge)
視野外の部屋Frustum Culling(Three.js標準)
LODプレイヤーから遠い部屋のオブジェクトを簡略化
描画上限Draw Call 目標: 100以下

メモリ目安

要素サイズ見込み
BSPツリーデータ~2KB
部屋ジオメトリ(全体)~500KB
テクスチャ~2MB(環境テーマ1種分)
合計~3MB