不断对区域进行切分,直到区域大小达到 1 为止。
class Area { constructor (x, y, width, height) { this.x = x this.y = y this.width = width this.height = height } } async createWall (cb = async () => {}) { let { width, height } = this let areas = this.areas = [ new Area(0, 0, width, height) ] for (;;) { let index = areas.findIndex(area => area.width > 1 || area.height > 1) if (index >= 0) { let area = areas[index] let [ areaA, areaB ] = this.splitArea(area) areas.splice(index, 1) areas.push(areaA) areas.push(areaB) await cb() } else { break } } } splitArea (area) { let { x, y, width, height } = area let xA, xB, yA, yB, widthA, widthB, heightA, heightB // A、B 是两个分裂后的区域 if ( width > height) { // 竖切 let splitLength = Math.floor(width / 2) // 对半分 xA = x yA = y widthA = splitLength heightA = height xB = x + splitLength yB = y widthB = width - splitLength heightB = height let yRandom = this.getRandomInt(y, y + height - 1) let gap = { x: xB, y: yRandom, direction: 'horizontal' } this.gaps.push(gap) } else { // 横切 let splitLength = Math.floor(height / 2) // 对半分 xA = x yA = y widthA = width heightA = splitLength xB = x yB = y + splitLength widthB = width heightB = height - splitLength let xRandom = this.getRandomInt(x, x + width - 1) let gap = { x: xRandom, y: yB, direction: 'vertical' } this.gaps.push(gap) } let areaA = new Area(xA, yA, widthA, heightA) let areaB = new Area(xB, yB, widthB, heightB) return [ areaA, areaB ] }完整代码:https://codesandbox.io/s/maze-vite-14-eggfr?file=http://www.likecs.com/src/maze.js:12878-13569
canvas 的渲染代码这里我就不贴了,这里关键就是把 Cell 改为了 Area,用来表示一个任意大小的矩形范围,然后把缺口存储到另外一个数组 gaps 中,渲染的时候先渲染 Area,再渲染 gaps 就行。
结果:
感觉效果不太行,尝试不要每次都对半分,而是随机选择切割点,只需要改动 splitLength 的赋值语句即可:
splitArea (area) { let { x, y, width, height } = area let xA, xB, yA, yB, widthA, widthB, heightA, heightB // A、B 是两个分裂后的区域 if ( width > height) { // 竖切 let splitLength = this.getRandomInt(1, width - 1) // 随机切割 xA = x yA = y widthA = splitLength heightA = height xB = x + splitLength yB = y widthB = width - splitLength heightB = height let yRandom = this.getRandomInt(y, y + height - 1) let gap = { x: xB, y: yRandom, direction: 'horizontal' } this.gaps.push(gap) } else { // 横切 let splitLength = this.getRandomInt(1, height - 1) // 随机切割 xA = x yA = y widthA = width heightA = splitLength xB = x yB = y + splitLength widthB = width heightB = height - splitLength let xRandom = this.getRandomInt(x, x + width - 1) let gap = { x: xRandom, y: yB, direction: 'vertical' } this.gaps.push(gap) } let areaA = new Area(xA, yA, widthA, heightA) let areaB = new Area(xB, yB, widthB, heightB) return [ areaA, areaB ] }效果:https://codesandbox.io/s/maze-vite-15-i7oik?file=http://www.likecs.com/src/maze.js
稍微有所改观,至少看起来不会是那种规规整整的“田”字型了,但无论如何,都没法和回溯法的效果相提并论,我暂时还没能想到更加好的方法,如果大家有有趣的想法,请务必在评论中分享。
最终的源代码:https://gitee.com/judgeou/maze-vite/tree/迷宫生成/