在边界框内有效获取等距网格位置

时间:2011-08-10 20:00:38

标签: javascript math isometric

Isometric Grid

我有一个等距网格系统,坐标从网格左上角的[0,0]开始(上图中显示的角落),x向图像底部递增,y向顶部递增(所以[0,height]将是顶角,[width,0]将是钻石形状的底角,宽度和高度是网格的大小,即200 x 200正方形)

无论如何,我需要帮助的是获得一系列等距网格位置,这些位置包含在图像中显示的蓝色框中。没有遍历每个x,y屏幕pos并获得相应的网格位置(请参阅我之前提到的关于如何从屏幕位置转换为网格位置Get row/column on isometric grid.的问题)我不知道如何实现这一点effeciently。

我之前发现的一个问题几乎完全相同Link here.答案是将网格渲染为每个网格方格的不同颜色的图像,然后检测广场下面的颜色,已经实施了这个解决方案,但它很慢!我几乎在考虑检查选择框中每个像素的网格位置会更快。为什么哦为什么javascript在循环中这么慢!

我真的需要一个基于我的坐标系统来解决这个问题的数学解决方案,但我似乎无法想出一些有用的东西(并处理选择框离开网格)

如果您需要更多信息,请与我们联系。

编辑:不幸的是,提供的答案到目前为止还没有奏效,因为选择就像在正方形网格上有一个菱形选区,实际上没有左上角,右下角要迭代,除非我错过了答案?我已经优化了渲染方法,但是在大量选择中,它仍会在帧中添加明显的下降,因为它遍历所有像素检查颜色并获得相应的方块

6 个答案:

答案 0 :(得分:10)

线性代数就是答案。这里有两个感兴趣的坐标系:屏幕坐标和等距坐标。将所选区域的角从屏幕坐标转换为等距坐标将对您有很大帮助。

设θ是等距坐标的x和y轴之间的角度,在屏幕上测量,单位是等距坐标中一步的像素长度。 然后

var c = Math.cos(theta/2);
var s = Math.sin(theta/2);
var origin = [oX, oY]; //the pixel coordinates of (0, 0)
var unit = 20;
var iso2Screen = function(iso) {
  var screenX = origin[0] + unit * (iso[0] * c + iso[1] * c);
  var screenY = origin[1] + unit * (iso[0] * -s + iso[1] * s);
  return [screenX, screenY];
}

反转这种关系,我们得到了

var screen2Iso = function(screen) {
  var isoX = ((screen[0] - origin[0]) / c - (screen[1] - origin[1]) / s) / unit;
  var isoY = ((screen[0] - origin[0]) / c + (screen[1] - origin[1]) / s) / unit;

现在将选择框每个角的屏幕坐标转换为等距坐标,并获得最小和最大x和y。

var cornersScreen = ...//4-element array of 2-element arrays
var cornersIso = [];
for(var i = 0; i < 4; i++) {
  cornersIso.push(screen2Iso(cornersScreen[i]));
}
var minX, maxX, minY, maxY;
minX = Math.min(cornersIso[0][0], cornersIso[1][0], cornersIso[2][0], cornersIso[3][0]);
maxX = Math.max(cornersIso[0][0], cornersIso[1][0], cornersIso[2][0], cornersIso[3][0]);
minY = Math.min(cornersIso[0][1], cornersIso[1][1], cornersIso[2][1], cornersIso[3][1]);
maxY = Math.max(cornersIso[0][1], cornersIso[1][1], cornersIso[2][1], cornersIso[3][1]);

所有选定的等距点都位于等距框[minX,maxX] x [minY,maxY]内,但并非该框中的所有点都在选区内。

你可以做很多不同的事情来清除那个不在选择范围内的那些点;我建议迭代等距x和y的整数值,将等距坐标转换为屏幕坐标,然后测试以查看该屏幕坐标是否位于选择框内,即:

var sMinX, sMaxX, sMinY, sMaxY;
sMinX = Math.min(cornersScreen[0][0], cornersScreen[1][0], cornersScreen[2][0], cornersScreen[3][0]);
sMaxX = Math.max(cornersScreen[0][0], cornersScreen[1][0], cornersScreen[2][0], cornersScreen[3][0]);
sMinY = Math.min(cornersScreen[0][1], cornersScreen[1][1], cornersScreen[2][1], cornersScreen[3][1]);
sMaxY = Math.max(cornersScreen[0][1], cornersScreen[1][1], cornersScreen[2][1], cornersScreen[3][1]);
var selectedPoints = [];
for(var x = Math.floor(minX); x <= Math.ceil(maxX); x++) {
  for(var y = Math.floor(minY); x <= Math.ceil(maxY); y++) {
    var iso = [x,y];
    var screen = iso2Screen(iso);
    if(screen[0] >= sMinX && screen[0] <= sMaxX && screen[1] >= sMinY && screen[1] <= sMaxY) {
      selectedPoints.push(iso);
    }
  }
}

答案 1 :(得分:7)

我是学生为爱好做XNA游戏。我正在研究基于正方形(Project Squared)的经典2D游戏。你的这个游戏让我想起了我的工作,所以我决定帮助你。

我认为应该使用数学,而不是你提到的图形。

首先你应该知道哪个方格(网格上的位置)在“蓝框”(可能是选择框)的开头和选择结束时。所以现在你知道列表的开头和结尾了。

由于在您的游戏中,方块可能具有静态大小(或者您可以缩放相机,但从不增加宽度/高度),因此您可以轻松计算出选择框中的哪些方块。

现在,你知道你的方块是45°移动r / l。

(我说的是XNA,比如coordsys-up left 0,0)

If ( selectedStartSquare.selectionY <= square.height )
    startY = selectedStartSquare.selectionY
else startY = selectedStartSquare.selectionY + 1; 

if (selectedEndSquare.selectionY > square.height)
    endY = -||-.selectionY
else endY = -||- + 1;

这将为您提供正方形的起点和终点高度指数。你需要为X索引做同样的事情。

现在你拥有了获得所有方块所需的一切。通过selectedStartSquare.X中的X到endX,从forStartSquare.Y到forY循环使用for循环,但每次都更改start(startYIndex ++每个循环)

这只是一个很接近的例子,因为我从未使用等距游戏。这可能需要一些调整,但我“想!!”这应该工作。 (在睡觉之前写了这个,因为我已经在床上了,所以没有纸张......祝你好运)

对不起我的英语,我来自克罗地亚......

答案 2 :(得分:4)

我认为亚历山大对如何解决问题有很好的想法。

这是另一种选择:

减少要检查的网格方块数的一种简单方法是在网格坐标中找到蓝色方框角的坐标。 在您的示例中,您只需要检查1<x<13 and 3<y<16的方块。

alvaro在how to check if a point is inside a box上给出了简明扼要的答案。

答案 3 :(得分:4)

考虑到瓷砖的常规布局,您不能简单地从选择器框的左上角开始,按命中查找哪个瓷砖,然后根据它们的间距移动到下一个瓷砖。

例如,如果你的牌是32x16,你会从角落开始,然后沿着32移动,然后当你到达终点时,沿着下一行。

tile spacing

例如,在一种奇怪的伪代码中...

var altRow = false;
var selectedTiles = [];
for (int y = selectorBox.top; y < selectorBox.top+selectorBox.height; y+=TILE_HEIGHT/2) {
    for (int x = selectorBox.left ; x < selectorBox.left+selectorBox.width; x+= TILE_WIDTH) {
        selectedTiles.add(tileAtPoint(x + (altRow ? - TILE_WIDTH/2 : 0), y);
    }
    altRow = !altRow;
}

答案 4 :(得分:2)

我没有看到其他人做的方法: 1.将选择的矩形转换为网格(获取每个角的平铺位置) 2.现在我们有一个2d问题,即:找到变形菱形中的所有点。

这可以通过类似于: http://en.wikipedia.org/wiki/Flood_fill

但也许这样的事情会更好:

1. Start at the topmost
2. Go down
3. Go right until we encounter the edge (can be calculated from the rombus line)
4. Do the same thing but go left.
5. Go to 2 until we are at the middle
6. Go right and down.
7. Go right until we encouter the edge
8. Go left and down.
9. Go left until we encouter the edge
10. Go to 6 until we are at the bottom

在我们访问的每个图块中,我们添加一个。

现在唯一的问题是确定我们是否遇到过边缘......

答案 5 :(得分:2)

我假设您可以将选择框的角点的坐标映射到iso网格方块。这为您提供了一个简单的代数测试解决方案的绘图问题。如果我们从等方(x1,y1)和(x2,y2)中的角开始,我们知道我们需要沿着斜率为1和-1的线进行测试,这些线穿过这些点,

因此四条线是y-y1 = x-x1,y-y1 = x1-x,y-y2 = x-x2,y-y2 = x2-x。他们在包含您开始选择框角落的等方形区域相遇。

如果我们为方便起见假设x2和y2分别大于x1和y1,我们只需要在iso网格上迭代从x1到x2和y1到y2的值,只接受其y坐标的等方形小于两条“较大”线,小于两条“较小”线。