部屋を廊下で繋ぐ
ばらばらに置いた部屋は、隣へ渡る通路がないと歩いて回れない。部屋同士の中心を通路で結ぶと、離れた区画が一本の道で繋がる。斜めには引かず、横にまっすぐ、縦にまっすぐのL字で繋ぐと、グリッドに乗った廊下になる。
部屋の中心どうしをL字(横線+縦線)で結ぶ。中心点を結べば必ず両方の部屋の内側を通り、廊下が部屋を貫通する形になる。BSP(二分割)で割った木の左右の子をたどって兄弟の部屋を結べば、全体が一本の連結成分になることが構造から保証される。中心でなく辺どうしを最短で結ぶと曲がりの少ない廊下、複数本引くとループのある回遊構造になる。
L字は2本の直線でできている。点 a から点 b へ、まず a の高さで横に床を彫り、次に b の縦で床を彫る。横線と縦線が角で出会って、カクッと折れた廊下になる。彫るといっても床(1)を立てるだけ。
// a の高さで横へ彫って、b の縦で下りる(L字)
const hCarve = (x0, x1, y) => {
for (let x = Math.min(x0, x1); x <= Math.max(x0, x1); x++) grid[idx(x, y)] = 1
}
const vCarve = (y0, y1, x) => {
for (let y = Math.min(y0, y1); y <= Math.max(y0, y1); y++) grid[idx(x, y)] = 1
}
hCarve(a.cx, b.cx, a.cy) // まず横へ
vCarve(a.cy, b.cy, b.cx) // つぎ縦へMath.min / Math.max で挟んでいるので、a と b の左右・上下がどちらでも同じ向きに彫れる。下は、その繋ぎの最小版。重ならないようランダムに置いた部屋の中心を順に拾って、隣同士をこのL字で結んでいく。部屋を床に塗り、廊下を床に塗ると、ばらばらだった部屋が一本の道で数珠つなぎになる。
L字は横へ引いてから縦へ引くだけ。廊下も部屋も同じ「床」として 1 を立てているので、塗るときは区別がいらない。隣同士を順に繋いだだけだと一本道のダンジョンになり、つなぐ相手を増やすとループのある回遊できる構造になる。生成したマップに連結性のBFSをもう一度流せば、ぜんぶ繋がったかを確かめられる。