我有一个游戏的2D体素图,这是一个2D数组,其中1表示地面,0表示天空。
算法从触及天空的最左侧地面体素开始(图中的红色框)。
它将探索当前位置的8个邻居,以检查其中一个是否是地面体素并且还触及天空体素。这意味着它应该被添加到地线。
在这张地图上,它找到了它并在地面上返了一条线。
大约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
答案 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度(你会发现你的示例图片没有这样的体素模式)。