将2d体素图转换为线的算法有时会卡住

时间:2016-07-13 18:21:38

标签: javascript arrays algorithm line

我有一个游戏的2D体素图,这是一个2D数组,其中1表示地面,0表示天空。

示例:阵列(地面)中的所有1都是绿色框 2d voxel map, red is start coordinate

算法从触及天空的最左侧地面体素开始(图中的红色框)。

它将探索当前位置的8个邻居,以检查其中一个是否是地面体素并且还触及天空体素。这意味着它应该被添加到地线。

算法工作的例子(它也可以进入'洞穴') enter image description here

在这张地图上,它找到了它并在地面上返了一条线。

在某些情况下它会突然停止,就像在这张地图上一样: failed algorithm

大约10次循环后,它停止创建线。

这是代码,其中有一些解释性注释:

voxelToLine() {
let voxels = this.voxels.length,//this.voxels is the 2d array
    lineGround = [],
    checkedVoxels = [],
    nowChecking,
    toCheck = [],
    otherPaths = [],
    done = false;

for (let y = 1; y < voxels - 1; y++)//sets first coordinate for line
    if (this.voxels[0][y] && (!this.voxels[0][y - 1] || !this.voxels[1][y] || !this.voxels[0][y + 1])) {
        lineGround[0] = [0, y / voxels];
        nowChecking = [1, y];//search starts from this point
    }

let looped = 0;
while (!done) {//continues search untill right side is located, or it got stuk (max 10*voxelmap width loops)
    toCheck = nowChecking.neighbours(8, (n) => n[0] > 0 && n[0] < voxels - 1);//gets 8 neighbour points around current point, neighbours between 1 and (voxelwidth -1) get returned
    let foundNew = false;
    for (let i = 0; i < toCheck.length; i++) {//check every neighbour
        let x = toCheck[i][0],
            y = toCheck[i][1],
            index = y * voxels + x;
        if (!checkedVoxels.includes(index)) {
            if (this.voxels[x][y] && (!this.voxels[x][y - 1] || !this.voxels[x + 1][y] || !this.voxels[x - 1][y] || !this.voxels[x][y + 1])) {
                //if the neighbour is a floor voxel, and touches a skyvoxel this neighbour is added to the line
                checkedVoxels.push(index);
                if (foundNew) {//if a valid neighbour is already found, this means there are 2 possible paths from the current point
                    otherPaths.push([x, y]);
                } else {
                    lineGround.push([x / voxels, y / voxels]);
                    nowChecking = [x, y];
                    //valid point gets added to the line and currently explored point get updated
                    foundNew = true;
                }
                if (x >= voxels) done = true;
            }
        } else if (i == toCheck.length - 1 && !foundNew) {
            if (otherPaths.length > 0) {
                nowChecking = otherPaths.pop();
                //if none of the neighbours are correct an alternative path gets explored
                foundNew = true;
            }
        }
    }

    if (!foundNew || looped++ > voxels * 10) {
        //if it never found a valid neighbour, or it's looped too often break from the whileloop
        console.log('loops: ', looped);
        break;
    }
}

if (lineGround[0][0] !== 0) lineGround.splice(0, 0, [0, lineGround[0][1]]);
if (lineGround[lineGround.length - 1][0] !== 1) lineGround.push([1, lineGround[lineGround.length - 1][1]]);
//x=0 and x=1 have to exist, so if they don't exist yet, add them

return lineGround;
}

您也可以在此处测试:game。如果单击,则在单击的半径范围内删除(设置为0)几个体素。此行也会重新计算。

我坚持这个,因为我不知道为什么这条线在某些情况下会停止。 所有代码均为here。相关文件是js / Level.js

2 个答案:

答案 0 :(得分:2)

问题比你提出的问题多。我在你的网站上玩了一下,有很多模式出错了。

我试图遵循你的代码的逻辑,但迷失了细节。所以我重写了大部分代码。主要的想法是你应该记录你沿着地面行进的方向(坡度),以便知道你应该在邻居之间看哪个顺序作为地面的一部分。

让我们说邻居编号如下,从0到7:

+---+---+---+
| 7 | 0 | 1 |
+---+---+---+
| 6 | * | 2 |
+---+---+---+
| 5 | 4 | 3 |
+---+---+---+

标有*的单元格是您在地面找到的最后一个单元格。现在让我们说前一个发现是6,然后邻居之间的搜索应该从7开始,然后是0,1,2,...... 5.第一个被发现是固体的,应该是下一个细胞增加到地面。

另一个例子:如果找到的前一个是4(我们向上),那么应该从5开始搜索邻居,然后搜索6,7,0,1,2和3。

找到固体(地面)的第一个邻居是您要添加到地线的邻居。通过这种方式,您将跟随每条曲线,进入&#34;洞穴&#34;,向上或向下,向左或向右。

当然,如果你在岛上开始,事情仍然会很奇怪。但我并没有试图解决这个特殊情况。

我已在以下版本的方法中实现了上述想法:

voxelToLine() {
    let voxels = this.voxels.length, x, y, i;
    // neighbors' relative coordinates listed in clockwise order    
    const neighbor = [ [0,-1], [1,-1], [1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1] ];

    for (y = 0; y < voxels; y++) //sets first coordinate for line.
        if (this.voxels[0][y]) break; // found ground, don't look further down
    let lineGround = [[0, y / voxels]];
    let [curX, curY] = [0, y]; //search starts here
    let direction = 0; // upward

    let looped = 0;
    do {// Continues search until right side is located, 
        // or it got stuk (max 10*voxelmap width loops)
        for (i = 0; i < 8; i++) {//check every neighbour, starting at `direction`
            [x, y] = [curX + neighbor[direction][0], curY + neighbor[direction][1]];
            // if we found ground, then pick that cell as the next one on the line
            if (x>=0 && x<voxels && y>=0 && y<voxels && this.voxels[x][y]) break;
            direction = (direction + 1) % 8; // turn clockwise to get next neighbour
        }
        //if it never found a valid neighbour
        if (i === 8) break;
        lineGround.push([x / voxels, y / voxels]);
        // prepare for next round
        [curX, curY] = [x, y];
        direction = (direction + 5) % 8;
    } while (looped++ <= voxels*10 && curX < voxels - 1);

    //x=0 and x=1 have to exist, so if they don't exist yet, add them
    if (lineGround[0][0] !== 0) lineGround.splice(0, 0, [0, lineGround[0][1]]);
    if (lineGround[lineGround.length - 1][0] !== 1) 
        lineGround.push([1, lineGround[lineGround.length - 1][1]]);
    return lineGround;
}

答案 1 :(得分:1)

看起来它正在跳过最后一个合法地面体素正下方的体素,因为它已被“检查”(添加到checkedVoxels数组中)。

有趣的是,这会阻止你的地面路径转90度(你会发现你的示例图片没有这样的体素模式)。