2D等距:屏幕到平铺坐标


9

我正在编写一个等距的2D游戏,并且很难准确地确定光标在哪个图块上。这是一张图:

其中xs和ys是屏幕坐标(像素),xt和yt是图块坐标,W和H分别是图块宽度和图块高度(以像素为单位)。我对坐标的表示法是(y,x),这可能会令人困惑,对此感到抱歉。

到目前为止,我能弄清楚的最好的是:

int xtemp = xs / (W / 2);
int ytemp = ys / (H / 2);
int xt = (xs - ys) / 2;
int yt = ytemp + xt;

这似乎几乎是正确的,但是给我一个非常不精确的结果,使得很难选择某些图块,或者有时它会在我尝试单击的图块旁边选择一个图块。我不明白为什么,我想知道是否有人可以帮助我理解其背后的逻辑。

谢谢!

Answers:


2

为了进行精确测量,我们可以考虑以下几点:

首先让我们考虑如何将坐标从等距空间(由i和j向量确定(如isometricMap [i,j]中)或作为屏幕上的yt和xt)转换为由屏幕的x和y确定的屏幕空间。为了简单起见,假设您的屏幕空间在原点与等距空间对齐。

进行变换的一种方法是先进行旋转,然后缩放y或x轴。为了获得与您的yt和xt匹配的必要值,我在这里不能完全提出。您可以创建一个矩阵来执行此操作或不执行此操作,然后使用反向矩阵,但是反向操作基本上就是您想要的。

反向缩放值,然后向后旋转以获得值并向下舍入。

我猜还有其他方法,但这对我来说似乎是最合适的。


啊 我已经多次修改了这篇文章,无论如何我还是无法完全清楚地表达我的观点。我要睡觉了
托尼2012年

1
谢谢,矩阵绝对是此处的最佳解决方案。我现在几乎可以正常工作了!
Asik

4

对于我正在编写的游戏,我也遇到了同样的问题。我想根据您对等轴测系统的实现方式,这个问题会有所不同,但是我将解释如何解决这个问题。

我首先从我的tile_to_screen函数开始。(我假设那是首先将磁贴放置在正确位置的方式。)此函数具有一个公式来计算screen_x和screen_y。我的看起来像这样(python):

def map_to_screen(self, point):
    x = (SCREEN_WIDTH + (point.y - point.x) * TILE_WIDTH) / 2
    y = (SCREEN_HEIGHT + (point.y + point.x) * TILE_HEIGHT) / 2
    return (x, y)

我将这两个方程式放入一个线性方程式系统中。用您选择的任何方法求解此方程组。(我使用了rref方法。此外,一些图形计算器可以解决此问题。)

最终方程如下:

# constants for quick calculating (only process once)
DOUBLED_TILE_AREA = 2 * TILE_HEIGHT * TILE_WIDTH
S2M_CONST_X = -SCREEN_HEIGHT * TILE_WIDTH + SCREEN_WIDTH * TILE_HEIGHT
S2M_CONST_Y = -SCREEN_HEIGHT * TILE_WIDTH - SCREEN_WIDTH * TILE_HEIGHT

def screen_to_map(self, point):
    # the "+ TILE_HEIGHT/2" adjusts for the render offset since I
    # anchor my sprites from the center of the tile
    point = (point.x * TILE_HEIGHT, (point.y + TILE_HEIGHT/2) * TILE_WIDTH)
    x = (2 * (point.y - point.x) + self.S2M_CONST_X) / self.DOUBLED_TILE_AREA
    y = (2 * (point.x + point.y) + self.S2M_CONST_Y) / self.DOUBLED_TILE_AREA
    return (x, y)

如您所见,这不像初始方程式那么简单。但这确实适用于我创建的游戏。感谢线性代数!

更新资料

用各种运算符编写一个简单的Point类之后,我将这个答案简化为以下内容:

# constants for quickly calculating screen_to_iso
TILE_AREA = TILE_HEIGHT * TILE_WIDTH
S2I_CONST_X = -SCREEN_CENTER.y * TILE_WIDTH + SCREEN_CENTER.x * TILE_HEIGHT
S2I_CONST_Y = -SCREEN_CENTER.y * TILE_WIDTH - SCREEN_CENTER.x * TILE_HEIGHT

def screen_to_iso(p):
    ''' Converts a screen point (px) into a level point (tile) '''
    # the "y + TILE_HEIGHT/2" is because we anchor tiles by center, not bottom
    p = Point(p.x * TILE_HEIGHT, (p.y + TILE_HEIGHT/2) * TILE_WIDTH)
    return Point(int((p.y - p.x + S2I_CONST_X) / TILE_AREA),
                 int((p.y + p.x + S2I_CONST_Y) / TILE_AREA))

def iso_to_screen(p):
    ''' Converts a level point (tile) into a screen point (px) '''
    return SCREEN_CENTER + Point((p.y - p.x) * TILE_WIDTH / 2,
                                 (p.y + p.x) * TILE_HEIGHT / 2)

是的,两个线性方程组也应该起作用。考虑到我们有两个不平行的向量,您应该能够通过使用yt和xt的单位向量获得平面上的任何点。虽然我认为您的实现看起来有些局限,但我不会去验证它。
托尼2012年

2

您使用的是良好的坐标系。如果使用交错列,事情将变得更加棘手。

思考此问题的一种方法是,您可以将(xt,yt)转换为(xs,ys)。我将按照塔恩的回答进行调用map_to_screen

您需要此函数的函数。我们可以称之为screen_to_map。函数逆具有以下属性:

map_to_screen(screen_to_map(xs, ys)) == (xs, ys)
screen_to_map(map_to_screen(xt, yt)) == (xt, yt)

编写完两个函数后,对单元测试这两个都是好东西。你怎么写逆的?并非所有函数都有逆函数,但在这种情况下:

  1. 如果您将其写为旋转后跟平移,则反向是反向平移(负dx,dy),然后是反向旋转(负角)。
  2. 如果您将其写为矩阵乘法,那么逆就是矩阵逆乘法。
  3. 如果将其写为以(xt,yt)定义(xs,ys)的代数方程式,则可以通过求解给定(xs,ys)的(xt,yt)方程来找到反函数。

确保测试逆+原函数会返回您开始的答案。如果您除去+ TILE_HEIGHT/2渲染偏移,Thane's将通过两个测试。当我求解代数时,我想到了:

x = (2*xs - SCREEN_WIDTH) / TILE_WIDTH
y = (2*ys - SCREEN_HEIGHT) / TILE_HEIGHT
yt =  (y + x) / 2
xt =  (y - x) / 2

我认为与Thane的相同screen_to_map

该函数会将鼠标坐标转换为浮点;用于floor将它们转换为整数图块坐标。


1
谢谢!我最终使用了一个转换矩阵,因此编写反函数很简单,即它只是Matrix.Invert()。加上它带来了更具声明性的编码风格(Matrix.Translate()* Matrix.Scale()* Matrix.Rotate()而不是一堆方程式)。也许它稍微慢一些,但这不应该成为问题。
阿西克2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.