BSPで部屋を割る
四角い部屋がきっちり並ぶダンジョンは、空間そのものを再帰的に二分割して作る。BSP(Binary Space Partitioning)。大きな矩形を縦か横にざっくり割って、できた2つをまた割って、を木構造でくり返す。十分小さくなった葉の矩形の中に、一回り小さい部屋を置く。
BSPは空間を二分木に畳む。根が領域全体、内部ノードが「一本の直線で割った境界」、葉が分割しきった小区画。各葉に部屋を彫れば、兄弟の区画は元の親矩形を縦か横に分けたものなので領域が交わらず、部屋同士の不重複が木構造から保証される。割る向き・位置に乱数を入れると規則的すぎない部屋割りになり、サブディビジョンで矩形を分けていくのは再帰的な構図生成と同じ骨格。
まず割る部分だけを作る。split は矩形を受け取り、横か縦をランダムに選んで分割位置 cut を決め、左右(上下)の2枚に切ってそれぞれを再び split に渡す。矩形が minSize より小さくなったら割るのをやめ、その矩形を葉として残す。盤面いっぱいの一枚から始めて、depth を時間で増やしながら分割の深さを段々深くする。
割る向きを長い辺に合わせているので、横長の区画は縦に、縦長の区画は横に切れて、どの葉もおよそ正方形に近づく。depth が増えるたびに各区画がさらに二つに割れ、盤面が細かいタイルへ分かれていく。cut を span * 0.5 の固定にすると等分されて格子状に、乱数を強くすると区画の大小が散らばる。minSize を上げると割りが早く止まって大きな区画が残る。
葉の矩形が決まったら、その中に一回り小さい部屋を彫る。区画の縁から pad だけ内側に余白を取り、残りの幅・高さの中でさらにランダムに縮めて置く。区画より小さい部屋にするので、隣り合う部屋のあいだに壁が残る。
薄い線が分割の境界、塗りつぶしが各葉に彫った部屋。区画は盤面を隙間なく覆い、部屋はその内側に浮く。pad を増やすと部屋が痩せて区画とのあいだの壁が太り、0 にすると部屋が区画いっぱいに膨らんで隣と接する。部屋が重ならないのは、各部屋が自分の葉の矩形の内側に収まっていて、葉同士が交わらないため。
部屋を置いただけでは行き来できない。兄弟の部屋の中心どうしをL字の廊下で結ぶと、木の枝に沿って全部屋が一本に繋がる。split が左右の子のインデックスを覚えるノード木に変え、葉から根へ戻りながら、左の子の部屋と右の子の部屋の中心を横線と縦線で縫う。
各部屋から伸びる細い線が廊下で、兄弟の中心を横へ引いてから縦へ下りるL字になっている。木を葉から戻りながら、ひとつ上のノードで左右の部屋を結ぶので、深い階層の近い部屋から順に繋がり、最後に根で大きく二分された両側が一本で繋がる。Math.min(ax, bx) と Math.abs(ax - bx) で挟んでいるため、左右どちらが上でも同じ向きに彫れる。割る向きと位置の乱数が変わるたびに、部屋の配置と廊下の縫い目が組み変わる。
廊下まで引いても、乱数次第でどこかの部屋が孤立する可能性は残る。全部の部屋に行き着けるかは、別途BFSで到達範囲を塗って確かめる。