17

等距网格

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

无论如何,我需要帮助的是获得一组等距网格位置,这些位置包含在图像中显示的蓝色框中。没有遍历每个 x,y 屏幕位置并获得相应的网格位置(请参阅我之前提出的关于如何从屏幕位置转换为网格位置获取等距网格上的行/列的问题。)我不确定如何有效地实现这一目标。

我之前发现的一个问题与此处的链接几乎完全相同。答案是将网格渲染为每个网格正方形具有不同颜色的图像,然后检测正方形下方存在哪些颜色,我已经实现了这个解决方案,但是速度很慢!我几乎在想检查选择框中每个像素的网格位置会更快。为什么哦,为什么 javascript 循环这么慢!

我真的需要基于我的坐标系来解决这个问题的数学解决方案,但我似乎无法想出一些可行的方法(并且也可以处理脱离网格的选择框)

如果您需要更多信息,请告诉我。

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

4

6 回答 6

10

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

设 theta 是等轴坐标的 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);
    }
  }
}
于 2011-08-19T16:40:58.453 回答
7

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

我认为应该使用数学而不是您提到的图形来完成。

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

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

现在,您知道您的正方形是 45° 移动 r/l。

(我说的是像 coordsys 一样的 XNA - 左上 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 到 endX 的 X 并通过从 selectedStartSquare.Y 到 endY 的 for 循环通过 Y 但每次都更改开始( startYIndex++ 每个循环)

这只是一个很接近的例子,因为我从未使用过 Isometric 游戏。这可能需要一些调整,但我“认为!!” 这应该有效。(在睡觉前写了这个,因为我已经在床上了,所以我什至没有纸……祝你好运)

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

于 2011-08-10T21:42:41.753 回答
4

我认为 Aleksander 对如何解决您的问题有一个非常好的想法。

这是一个替代方案:

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

alvaro 就如何检查点是否在框内给出了简短而简洁的答案。

于 2011-08-10T21:50:26.720 回答
4

考虑到瓦片的规则布局,你不能简单地从选择框的左上角开始,hittest 找到哪个瓦片,然后根据它们的间距移动到下一个瓦片。

例如,如果您的瓷砖是 32x16,您将从拐角处开始,沿着 32 移动,然后当您到达终点时,沿着下一行移动。

瓷砖间距

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

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;
}
于 2011-08-15T14:39:22.757 回答
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

在我们访问的每个图块上,我们都会添加一个。

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

于 2011-08-15T20:32:59.177 回答
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,我们只需要在等网格上迭代从 x1 到 x2 和 y1 到 y2 的值​​,只接受 y 坐标小于的等方格两条“较大”的线,并小于两条“较小”的线。

于 2011-08-17T00:06:28.580 回答