【问题标题】:Drawing isometric walls绘制等距墙
【发布时间】:2013-01-23 20:42:00
【问题描述】:

我在使用等距墙时遇到了一些问题。

我正在使用从后到前的渲染方法绘制等距地砖,效果很好。我还保持我的地砖在一个漂亮的网格中正确排列。代码(这似乎是等距平面图的标准)如下:

for(int x = 0; x < 6; x++){
    for(int y  = 3; y >=0 ; y--){

        int xCo = (y+x)*(tileWidth/2);
        int yCo = (x-y)*(tileHeight/2);
        tile.draw(g, xCo, yCo);
    }            
}

这是一个不错的小地板:

网格是由这个图块构成的:

不幸的是,当我对墙壁使用相同的逻辑时,一切都变得一团糟。

for(int x = 0; x < 6; x++){
    for(int y  = 3; y >= 0 ; y--){

        int xCo = (y+x)*(wallWidth()/2);
        int yCo = (x-y)*(wallHeight()/2);
        walls.draw(g, xCo, yCo);    
}
}

我将它用作我的墙砖:

(它是来自 google 图片搜索的占位符,但应该可以正常工作)

这是我得到的结果:

我的墙壁的渲染顺序显然是正确的,较近的墙壁在较远的墙壁上渲染,这正是我想要的,但定位也非常不正确,我不知道应该如何更正没有使用像素值。

作为参考,我确实使用硬编码的像素值进行了试验,因为我发现从一堵墙的右角到另一堵墙的右角,变化正好是 (200, -100) 像素。当我为此创建渲染循环帐户时

int xCo = (x+y)*200;
int yCo = (y-x)*-100;

它工作得很好,但这不是一个可行的解决方案,因为它不允许任何多功能性。

所以,寻找有关如何使我的等距墙对齐的建议。我做错了什么?

谢谢!

【问题讨论】:

  • 感谢您让我的照片可见。我也在 gamedev.stackexchange 上发过帖子,他们对链接和图片有超级限制,所以我没有在这里尝试。
  • 我注意到你的墙有一个模式:第一面墙的前顶部水平边缘与第三面墙的前底部水平边缘共线。此外,第一壁的右前垂直边缘与第三壁的左后垂直边缘共线。第二层和第四层墙也是如此。所以他们有点排队。只是没有按照你想要的方式。
  • 哈哈是的,确实如此。我会在这个事实中得到一些安慰,但除了“我的方法只有一半错误”之外,我不知道该怎么做
  • 地砖边缘在图像的中点相交。墙边没有。我推荐 xCo = (x+y)*(wallWidth()*9/10) 和 yCo = (y-x)*(wallHeight()/3)。
  • 我想看看你们对wallWidth()wallHeight() 的实现。 wallWidth 是否等于墙砖的宽度?还是只是正面的宽度,不包括左边的黑色边框?同样,wallHeight 是图像的高度,还是右下角和右上角之间的距离,还是别的什么?

标签: java math isometric


【解决方案1】:

您不能简单地对墙壁和地板使用相同的绘图代码,因为墙壁和地板不在同一平面上:地板是平的(水平的)而墙壁是垂直的。所以你必须稍微不同地绘制它们。

就地砖的位置而言,您在地板案例中的 x 和 y 坐标意味着“左/右”和“前/后”。对于砖块来说,left 和 right 仍然有意义,但我们希望将 forward/backward 替换为 up 和 down 以反映垂直方向。所以我们的“y”有了新的含义。

现在,在数学中,y 轴通常指向上方,而在 2D 计算机图形中则指向下方。您可以选择 - 下面的代码假定它指向上方,因此 y = 0 表示“在地板上”。

所以让我们开始考虑顺序。您发布的示例砖是用于作为地板(上)左端的墙。由于砖的黑色部分(墙的深度),我们必须确保首先绘制更靠右的砖,以便左侧的黑色深度将被更近的砖覆盖。同样的道理也适用于墙顶部的黑色,我们必须先绘制较低的砖块。

如果我们坚持前面讨论的 x 和 y 方向(x 从左到右,y 从下到上),这意味着我们必须在负方向上运行两个 for 循环:

    for (int y = 3; y >= 0; y--) {
        for (int x = 5; x >= 0; x--) {
            ...
        }
    }

现在的主要问题是我们必须将每块砖的绘制相对于其他砖偏移多少。让我们一次只做一个方向,从 x 方向开始。

让我们想象一下只有两块相邻的砖块:

两者的左侧有可见的黑色深度部分,但右侧不应该显示它。因此,我们不能简单地将正确的图像偏移 PNG 的整个宽度。事实上,假设砖与你的地砖对齐,那么墙的实际前部的宽度应该是地砖宽度的一半。

int xCo = x * tileWidth / 2;

左边的黑墙深度也不能忽视,因为我们大概想让每块砖向左偏移一点,这样墙前角的x坐标就和地砖对齐了,而不是后角的 x 坐标。

现在,每块砖的 y 坐标有点棘手,因为它不仅取决于砖的行,还取决于 x 坐标:越靠右,我们应该画得越高。但是让我们暂时忽略 x 方向并尝试简单地绘制一列砖块:

同样,两块砖的 y 坐标之间的增量不是 PNG 的全高。与我们假设砖块与砖块对齐的左/右案例不同,这允许我们使用tileWidth 作为 delta-x,砖块可以具有任意高度。但是我们仍然可以从图像高度计算出实际的砖块高度,因为我们知道左侧的深度和顶部的深度必须对齐。

如果我们看一下砖PNG右上角的小透明三角形,我们会注意到它的宽度和高度的比例必须与地砖的宽度和高度的比例相同。这允许我们从上面计算的 xoffset 计算 yoffset,并使用它来推断砖的实际高度:

int yoffset = xoffset * tileHeight / tileWidth;
int wallHeight = wallHeight() - tileHeight / 2 - yoffset;

请注意,这仅在 PNG 的边界处没有空白空间的假设下有效,并且它仍然可能由于舍入错误而失败。因此,如有必要,您可以在此处添加Math.ceil()(或简单地+ 1)。

所以对于简单的列,我们现在可以开始了:我们可以简单地将y 变量与上面的wallHeight 相乘。但如前所述,砖块的 x 位置也会影响 y 像素坐标。如果我们再看第一张图片,两块砖彼此相邻,我们需要将右边的砖向上移动多少才能与左边的砖对齐?嗯,这个其实很简单,因为它和地砖一样:只有地砖的一半高度!

所以我们都准备好了。如果我们将所有内容放在一起,我们最终会得到如下代码:

int xoffset = wallWidth() - tileWidth / 2;
int yoffset = xoffset * tileHeight / tileWidth;
int wallHeight = wallHeight() - tileHeight / 2 - yoffset;

for (int y = 3; y >= 0; y--) {
    for (int x = 5; x >= 0; x--) {

        int xCo = x * tileWidth / 2;
        int yCo = y * wallHeight - x * tileHeight / 2;

        walls.draw(g, xCo - xoffset, yCo - yoffset);
    }
}

(我假设 wallWidth() 和 wallHeight() 返回砖块 PNG 的宽度和高度。)

请注意,for 循环之前的三个常量可以从实际的绘图代码中移出——它们只依赖于图像属性和其他常量,不必在每次绘制墙壁时都重新计算。

【讨论】:

  • 所以,首先我要感谢您提供了令人难以置信的彻底回答。你是某种巫师。但我很厚,我仍然需要一些帮助。首先,我不明白这是怎么回事:“其实,假设砖块和你的地砖对齐,那么墙的实际前面部分的宽度应该是瓷砖宽度的一半。”如果它们排成一列,它们的宽度不应该相同吗?另外,如果我想让它们排成一行而不是堆叠起来,你能解释一下如何获得墙壁的 y 坐标吗?你的第一张两墙并排的照片就是我想要的。
  • 不用担心 - 如果您不需要将墙壁堆叠起来,那么只需将外部 for 循环替换为 int y = 0。除此之外,您是否尝试过代码并查看它是否执行了您想要的操作?对您来说,最简单的方法可能是复制并粘贴它,然后开始使用它。 ---关于你的宽度问题:这里的问题是,有时我用宽度这个词来指代墙壁或瓷砖的宽度,有时指的是图像的宽度。由于等轴测视图,这两个宽度是不同的。在引用的句子中,我指的是图像宽度。
  • 关于你的第二个问题,基本的绘制算法没有改变,你只需要找到适合墙壁的位置。如果您查看xCoyCo 的公式,您会发现它们将使具有x = 0y = 0 的墙在像素坐标(0, 0) 减去偏移量处绘制。如果您可以根据像素坐标确定应该真正在哪里绘制一面墙,那么您可以将这些坐标添加到walls.draw(...) 的调用中。如果其中一堵墙放置正确,所有其他墙也应该正确放置。
  • 我建议我们在聊天中继续讨论:chat.stackoverflow.com/rooms/23269/isometric-walls
【解决方案2】:

如果您查看形状像菱形的地砖,将其向上移动一半宽度并穿过一半长度,两条边将对齐。
墙砖不是菱形,因此通过移动一半宽度和一半长度,您想要匹配的边缘将不会对齐。

给定u = 移动距离 和v = 向上移动的距离 和A等距角

v = u*tan(A)

u 在这种情况下是图像正面的宽度。

如果墙壁图像的面(纹理位)与地砖的边缘长度匹配,则给出

int offset = ?;// this is the width of the black stripe on the image.

for(int x = 0; x < 6; x++){
    for(int y  = 3; y >=0 ; y--){

        int xCo = ((y+x+1)*(tileWidth/2))-offset;
        int yCo = (x-y)*(tileHeight/2);
        wall.draw(g, xCo, yCo);
    }            
}

【讨论】:

  • 我可以用这个。我在我的手机上,所以我稍后会测试它,但谢谢!我想确保我在这里的术语很清楚:当您说“如果图像的正面与地砖的边缘长度匹配”时,您到底指的是什么?对于我来说,有太多可能的含义(图像的宽度是矩形的,瓷砖的实际长度很难找到并且因为它不是正方形而烦人,等等)。再次感谢!
  • 我指的是墙上图像上的纹理,而不是黑色深度部分。地砖的边缘是指菱形上两个相邻点之间的线,而不是包含图像的矩形的边。
  • 所以我只需要使偏移量与图像宽度保持一致,这样它就可以接受多个相同规格的图像,对吧?
  • 抱歉修正了它在错误的地方偏移应该是 ((y+x+1)*(tileWidth/2)-offset
【解决方案3】:

在等距领域中,您可以移动三个轴 - Z 表示上下,X 和 Y 表示“对角线”。

首先,让我们想象一个 1 x 1 x 1 单位的等距立方体的像素表示,所有边都表示等长:

它会在 Z 轴上高 A 个像素。 它的其他边缘的长度也是 A 像素,但旋转了 60 度 - 所以它将是 sin(30)*A 像素高和 cos(30)*A 像素长在 X 和 Y 方向上 - 也就是 0.5*A 和 sqrt (3)/2 *A。

因此,要在 X、Y 和 Z 中定位等距立方体大小的对象,我们必须通过以下方式在屏幕 x 和 y 上平移它:

y += Z*A
x += X*A/2 - Y*A/2
y += (X+Y)*A*sqrt(3)/2

只要我做出的假设成立,这应该可行。

编辑:顺便说一句,如果图像具有深度,则必须对 A 进行硬编码,因此您无法从图像尺寸中自动提取 A。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多