0

ダンジョン ジェネレーターを構築する過程で、接続されていない部屋に関するいくつかの問題を解決する必要がありますが、最後に見つけた問題の解決策が見つかりません。

接続されていない部屋は簡単に検出できますが、接続されていない部屋グループに関しては、どうすればよいかわかりません。

これが何が起こるかの写真です...

ここに画像の説明を入力 右上隅を見ると、接続されていない部屋のグループが表示される場合があります。接続されていない部屋のグループをすべて検出して、残りの部屋に接続する必要があります。


システム

仕組みは非常にシンプルで、すべてのタイル オブジェクトとそのプロパティを含む配列があります。変更するには、配列内のオブジェクトにアクセスするだけです。

ダンジョン生成:

  1. タイプが床 (灰色のブロック) のすべてのタイルを作成します。
  2. 重なり合わないランダムな部屋を配置し、互いに最小距離を 1 タイルにします。
  3. すべての部屋の周りに壁を配置します。
  4. 部屋の壁から 1 タイル以上離して床ブロックに壁を配置します。
  5. 壁から 1 タイル離れた壁ブロックに空のブロックを配置します。

地図の凡例

白 = 部屋のブロック

灰色 = 床ブロック || 廊下ブロック

黒いブロック、灰色の枠=壁ブロック

|| ブラウン || 赤 = ドア ブロック

完全な黒 = 空のブロック

4

2 に答える 2

0

グループ化された部屋の問題を解決するには、フラッド フィルを使用します。フラッド フィル アルゴリズムはたくさんあるので、自分に合ったものを選んでください。

次に、部屋を色で塗りつぶすと、接続されているすべての部屋も塗りつぶされます。次にスキャンして空き部屋を見つけ、空き部屋がなくなるまで続けます。接続されたルーム グループの一覧が表示されます。グループごとに 1 つずつ、または隔離された部屋。

グループ化された部屋を見つけるためにビットマップ塗りつぶしを使用する例。クリックして部屋をやり直します。同じグループの部屋は同じ色です。配列groupRoomsは部屋のグループを保持します。配列roomsはすべての部屋で、map部屋と floodFill を描画するために使用されるビットマップです。 floodFill 関数は接続された部屋を見つけます。

警告: 終了するために正しい実行に依存する while ループが多数あります。終了条件が失敗した場合、コードはページをブロックします。不明な場合は、ループにカウンターを追加し、カウントが高くなった場合の終了条件を追加します。現状のままで完全に安全です (10 億年もの間実行される可能性はわずかですが、実行される場合は、同じ確率で量子トンネルを使用して修正を行います。)

/** SimpleFullCanvasMouse.js begin **/
const CANVAS_ELEMENT_ID = "canv";
const U = undefined;
var canvas, ctx;
var createCanvas, resizeCanvas;
var L = typeof log === "function" ? log : function(d){ console.log(d); }
createCanvas = function () {
    var c,cs;
    cs = (c = document.createElement("canvas")).style; 
    c.id = CANVAS_ELEMENT_ID;    
    cs.position = "absolute";
    cs.top = cs.left = "0px";
    cs.zIndex = 1000;
    document.body.appendChild(c); 
    return c;
}
resizeCanvas = function () {
    if (canvas === U) { canvas = createCanvas(); }
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight; 
    ctx = canvas.getContext("2d"); 
    if(demo !== undefined){
        demo();
    }
}


// creates a blank image with 2d context
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}


var demo = (function(){
    var rooms = [];
    const MAP_WIDTH = 128;
    const MAP_HEIGHT = 128;
    const MAX_WIDTH = 10;
    const MIN_WIDTHHEIGHT = 4;
    const MAX_HEIGHT = 10;
    const OUTLINE = 1;
    const MAX_SEARCH_COUNT = 150; // how many rooms is determined by the number of room fit search that fail. The greater this number the more rooms. but there is a limit 50 part fills space, 150 a few more 1000 a few more and so on
    const WALL_COLOUR = "BLACK";
    const FLOOR_COLOUR = "#AAA";
    const DOOR_COLOUR = "RED";
    const FILL_LEVEL = 160; // the colour to fill as grey
    const RAND_BELL = function(min,max){return Math.floor(((Math.random() + Math.random() + Math.random()) / 3) * (max - min)) + min;}
    const RAND = function(min,max){return Math.floor(Math.random() * (max - min)) + min;}
    var map;
    var roomGroups;
    function canRoomFit(x,y,w,h){
        var i,len;
        len = rooms.length;
        for(i = 0; i < len; i ++){
            r = rooms[i];
            if(!(r.x + r.w < x || r.x > x + w || r.y + r.h < y || r.y > y + h)) {
               return false;
            }
        }
        return true;
    
    }
    function createRoom(){
        var found = false;
        var x,y,w,h;
        var searchCount = 0;
        while(!found && searchCount < MAX_SEARCH_COUNT){
            
            w = RAND_BELL(MIN_WIDTHHEIGHT, MAX_WIDTH);
            h = RAND_BELL(MIN_WIDTHHEIGHT, MAX_HEIGHT);
            x = RAND(OUTLINE, map.width - (w + OUTLINE));
            y = RAND(OUTLINE, map.height - (h + OUTLINE));
            found = canRoomFit(x,y,w,h);
            searchCount += 1;
        }
        if(found){
            var room = {
                x: x, y : y, w : w, h : h,
                doors : [],
                groupID : 0,
            };
            var perm = w * 2 + h* 2;
            var doorMinSpace = 4;
            while(room.doors.length === 0){ // sometimes doors are not added keep trying untill they are
                var doorAt = 0;
                while(doorAt < perm){
                    doorAt += RAND_BELL(doorMinSpace,7);
                    if(doorAt < w - 1){
                        room.doors.push({x : doorAt, y : 0});
                    }else
                    if(doorAt > w + 1 && doorAt < (w + h)- 1){
                        room.doors.push({x : w-1, y : doorAt-w});
                    }else
                    if(doorAt > w + h + 1 && doorAt < (w + h + w)- 1){
                        room.doors.push({x : doorAt-(w+h), y : h-1});
                    }else
                    if(doorAt > w + h + w + 1 && doorAt < perm - 1){
                        room.doors.push({x : 0, y : doorAt-(w+h+w)});
                    }
                }
                if(doorMinSpace > 0){
                    doorMinSpace -= 1;
                }
            }
            rooms.push(room);
            return true;
        }
        return false;
    }
    function addRooms(){
        var search = true;
        while(search){
            search = createRoom();
         
        }
    }
    function drawRooms(showGroupColour){
        var groups = roomGroups.length + 1;
        var col = function(r){
            return "hsl("+Math.floor((r.groupID / groups)*360)+",100%,50%)"
        };
        var rect = function(r,add,col){
            map.ctx.fillStyle = col;
            map.ctx.fillRect(r.x-add,r.y-add,r.w+add+add,r.h+add+add)
        }
        // draw floors
        rooms.forEach(function(r){
            if(showGroupColour){
                rect(r,OUTLINE,col(r));
            }else{
                rect(r,OUTLINE,FLOOR_COLOUR);
            }
            
        });
        // draw walls
        rooms.forEach(function(r){
            rect(r,0,WALL_COLOUR);
    
        });
        // draw inside floors
        rooms.forEach(function(r){
            if(showGroupColour){
                rect(r,-1,col(r));
            }else{
                rect(r,-1,FLOOR_COLOUR);
            }
        });
        // draw doors
        rooms.forEach(function(r){
            r.doors.forEach(function(d){
                if(showGroupColour){
                    map.ctx.fillStyle = col(r);
                }else{
                    map.ctx.fillStyle = FLOOR_COLOUR;
                    
                }
                map.ctx.fillRect(r.x + d.x,r.y + d.y,1,1)
            });
        });
    
    }
    function floodFill(posX, posY, imgData) {
        var data = imgData.data; // image data to fill;
        var stack = [];          // paint stack to find new pixels to paint
        var lookLeft = false;    // test directions
        var lookRight = false;
        var w = imgData.width;   // width and height
        var h = imgData.height;
        var painted = new Uint8ClampedArray(w*h);  // byte array to mark painted area;
        var dw = w*4; // data width.
        var x = posX;   // just short version of pos because I am lazy
        var y = posY;
        var ind = y * dw + x * 4;  // get the starting pixel index
        var sp = 0; // stack pointer
    
        // function checks a pixel colour passes tollerance, is painted, or out of bounds.
        // if the pixel is over tollerance and not painted set it do reduce anti alising artifacts
        var checkColour = function(x,y){
            if( x<0 || y < 0 || y >=h || x >= w){  // test bounds
                return false;
            }
            var ind = y * dw + x * 4;  // get index of pixel
            if(data[ind] !== 0 && data[ind] !== 255){
                return true;
            }
            return false;
        }
        // set a pixel and flag it as painted;
        var setPixel = function(x,y){
            var ind = y * dw + x * 4;  // get index;
            data[ind] = 255;       // set RGBA
    
        }
        stack.push([x,y]);  // push the first pixel to paint onto the paint stack
            
        while (stack.length) {   // do while pixels on the stack
            var pos = stack.pop();  // get the pixel
            x = pos[0];
            y = pos[1];
            while (checkColour(x,y-1)) {  // finf the bottom most pixel within tollerance;
                y -= 1;
            }
            lookLeft = false;  // set look directions
            lookRight = false; // only look is a pixel left or right was blocked
            while (checkColour(x,y)) { // move up till no more room
                setPixel(x,y);         // set the pixel
                if (checkColour(x - 1,y)) {  // check left is blocked
                    if (!lookLeft) {        
                        stack.push([x - 1, y]);  // push a new area to fill if found
                        lookLeft = true;
                    }
                } else 
                if (lookLeft) {
                    lookLeft = false;
                }
                if (checkColour(x+1,y)) {  // check right is blocked
                    if (!lookRight) {
                        stack.push([x + 1, y]); // push a new area to fill if found
                        lookRight = true;
                    }
                } else 
                if (lookRight) {
                    lookRight = false;
                }
                y += 1;                 // move up one pixel
            }
        }
    }

    function findRoomsConnectedTo(room,mapData){
        var groupID = roomGroups.length + 1;
        floodFill(room.x + 2,room.y + 2,mapData);
        var group = [];
        for(var i = 0; i < rooms.length; i ++){
            var r = rooms[i];
            var ind = (r.x+1) * 4 + (r.y+1) * 4 *  MAP_WIDTH;
            if(mapData.data[ind] === 255){
                r.groupID = groupID;
                group.push(r);
                rooms.splice(i,1)
                i --;
            }
            
        }
        roomGroups.push(group);
    }
    function groupRooms(){
        var mapData = map.ctx.getImageData(0,0,MAP_WIDTH,MAP_HEIGHT);
        while(rooms.length > 0){
            findRoomsConnectedTo(rooms[0],mapData);
        }
    }
    
    function demo(){
        L("Run demo")
        var now = performance.now();
        map = createImage(MAP_WIDTH,MAP_HEIGHT);
        roomGroups = [];
        rooms = [];
        map.ctx.fillRect(0,0,MAP_WIDTH,MAP_HEIGHT)
        addRooms();
        drawRooms();
        var roomTemp = rooms.map(function(r){return r;})
        groupRooms();
        rooms = roomTemp;
        drawRooms(true);
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.imageSmoothingEnabled = false;
        ctx.mozImageSmoothingEnabled = false;
        ctx.drawImage(map,0,0,canvas.width,canvas.height);
        L("Demo complete in "+(performance.now()-now));
    }
    return demo
})();


resizeCanvas(); // create and size canvas
window.addEventListener("resize",resizeCanvas); // add resize event
canvas.addEventListener("click",demo);

于 2016-07-04T15:48:34.150 に答える