将笛卡尔坐标系下的宽度和高度转换为等轴投影。

3

我正在尝试使用SDL图形库创建等轴测游戏。

在SDL中进行渲染时,您需要一个源矩形和一个目标矩形。源矩形是您想要渲染的已加载纹理的部分,而目标矩形是您要渲染到屏幕上的区域。矩形只由4个属性组成,即X位置,Y位置,宽度和高度。

现在假设我有一个笛卡尔目标矩形,其坐标如下:

X=20,Y=10,Width=16,Height=16

现在假设我想将其转换为等轴测。为了转换X和Y坐标,我会使用:

isometricX = cartX - cartY;
isometricY = (cartX + cartY) / 2;

现在我不明白的是,我该如何将笛卡尔坐标系中的宽度和高度转换为等距宽度和高度,以创造视口向一侧旋转45度并向下旋转30度的错觉,从而创建等距视图。
编辑: 我想澄清一下我的问题,当我将笛卡尔坐标系转换为等距时,我将这个:http://i.stack.imgur.com/I79yK.png 变成了这个:http://i.stack.imgur.com/ZCJg1.png。现在我正在尝试弄清楚如何旋转瓦片以使它们全部拼合在一起,以及我需要如何调整高度和宽度才能实现这一点。

这篇回答可能会有所帮助。我不熟悉SDL,但看起来你无法仅凭大小和宽度在SDL中绘制等距矩形,你需要一种基于多边形的方法,其中可以指定每个点的坐标。(除非我对你尝试做的事情有过多的解读) - Xeren Narcy
@Xeren 我之前确实看过那个答案,在那个例子中,他们已经有了等距形式的瓷砖,他们有这个:http://coobird.net/img/tile-width-height.png 而我有一个平面瓦片,想找出一种方法使它成为等距瓦片。 - deery50
如果你面前有一张带有笛卡尔坐标系的正方形纸,并将其旋转,直到纸张右下角的点朝向你,那就是我想要的效果。我还发现SDL在渲染时可以支持旋转,这可能是另一种实现方式,但是如果我要使用它,我需要计算每个图像精灵旋转的角度,以使拼接好并且贴合紧密,我不太明白如何计算。 - deery50
我明白了...好的,最后一个问题:在这些图片中,瓦片已经按照等距网格排列,所以您是否需要一种将笛卡尔坐标系转换为等距坐标系(在此示例中,瓦片的位置和方向不会改变)的方法? - Xeren Narcy
@xeren 我更希望瓷砖在网格旋转时也跟着旋转。 - deery50
显示剩余2条评论
2个回答

3

首先,您需要以下操作来转换等角坐标:

isoX = carX + carY;
isoY = carY - carX / 2.0;

carX = (isoX - isoY) / 1.5;
carY = isoX / 3.0 + isoY / 1.5;

右上角和左下角的直角变成了120度,另外两个角变成了60度。右下角变为底部角,左上角变为顶部角。这也假定y向上增加,x向右增加(如果你的系统不同,请相应地翻转符号)。你可以通过代换来验证这些操作是彼此的逆。

对于一个矩形,您需要转换4个点 - 即角落 - 因为它们在SDL的用途上不会是“矩形”的(将成为平行四边形)。这在数值上更容易看出。

首先,给角落起个名字。我喜欢按顺时针方向从左下角开始 - 这个坐标将被称为C1,并具有关联的X1和Y1,其他三个角将是C2-4。

C2 - C3
|    |
C1 - C4

然后计算它们的笛卡尔坐标...
X1 = RECT.X;
Y1 = RECT.Y;

X2 = X1; // moving vertically
Y2 = RECT.Y + RECT.HEIGHT;

X3 = RECT.X + RECT.WIDTH;
Y3 = Y2; // moving horizontally

X4 = X3; // moving vertically
Y4 = RECT.Y;

最后,将变换分别应用于每个坐标,以获得I1、I2、I3、I4坐标...
iX1 = X1 + Y1;
iY1 = Y1 - X1 / 2.0;
// etc

而你最终得到的是屏幕坐标I1-4,它们具有以下形状:
    I2
  /    \
I1      I3
  \    /
    I4

但与这个低劣的描述不同的是,I4和I2的角度将会是约127度,而I1和I3的角度应该是约53度。(这可以微调为恰好60/120,并且取决于计算isoY时carX的2.0因子——它应该是sqrt(3)而不是2.0,但差不多就行了)
如果使用反向变换,可以将I1-4坐标转换为C1-4,或者从屏幕坐标中定位世界坐标等。
如果只是初次实现相机/视口,可能会有一点棘手,但这超出了要求的范围,所以我不会继续深入讨论它(除非进一步追问)...
(编辑)关于SDL...
SDL似乎不太适用于广义变换。我没有使用过它,但它的界面与我之前用于游戏引擎的GDI(windows)非常相似,我遇到了这个确切的问题(旋转+缩放纹理)。
有一个(看起来不是标准的)SDL函数,可以同时缩放和旋转纹理,但它按错误顺序执行,所以它始终保持图像的透视性,而这里并不需要。
基本几何图形会很好,因为填充和线条不需要缩放,只需要定位。但是对于纹理...你要么编写代码逐像素渲染纹理,要么使用一组变换(旋转后缩放),或层叠(绘制带阿尔法掩码的等角正方形并渲染预先计算的纹理)等等...
当然,如果您可以选择,可以使用适用于原始几何和纹理数据的东西,如OpenGL / Direct3D。个人建议使用OpenGL/SFML来实现这样的内容。

感谢迄今为止提供的所有帮助。所以,通过您上面的操作,我成功地将每个瓷砖的角落转换为等距投影,并将其从这个:http://i.imgur.com/tHCpAQi.png 转换成了这个:http://i.imgur.com/hmKBHcX.png。(我使用SDL在每个点之间绘制线条)。但是,当我实际渲染精灵时,使用SDL_RenderCopyEx,我仍然不明白如何找到旋转精灵的角度(默认相对于瓷砖中心)。 - deery50
不幸的是,旋转并不足够,因为在这个坐标变换下,角落处的角度会改变。你不再拥有屏幕坐标定义的正方形,否则就可以定义一个位置和大小+旋转来创建等距透视图。问题在于SDL的能力...寻找基于SDL的解决方案,我只有两个选择:1、编写自己的函数,在此变换下进行每像素渲染(我可以帮助理解概念),或者2、使用45度,并将整个屏幕沿高度缩小到50%(可能需要离屏渲染)... - Xeren Narcy
我发现SDL有纹理多边形渲染方法,我认为这是你应该使用的,但我不知道它如何转换纹理。相比之下,在OpenGL中,您可以为多边形中的每个屏幕坐标指定纹理坐标。 - Xeren Narcy
进一步阅读让我不太相信这会在SDL中有效... 对于这种非矩形渲染,我强烈建议考虑OpenGL(SFML开箱即用地设置了2D OpenGL)。 - Xeren Narcy
好的,非常感谢您的帮助。我会查看SFML和OpenGL。我以前使用过SDL和OpenGL,但是有点繁琐,我希望不需要为我的简单小游戏切换。 - deery50
@XerenNarcy 我正在尝试实现它,但似乎缺少括号:isoY = carY - carX / 2.0; -> isoY = (carY - carX) / 2.0; 我错了吗? - Jerry Lundegaard

0

很遗憾,我无法评论以请求澄清,因此我必须用一个问题来回答:您不能将所有四个点转换,然后从这些点计算出变换后的宽度和高度吗?

X=20,Y=10,宽度=16,高度=16

正如您所说的那样

isometricX = cartX - cartY;
isometricY = (cartX + cartY) / 2;

所以

isometricX1 = cartX1 - cartY1;
isometricY1 = (cartX1 + cartY1) / 2;

并且

isometricWidth = std::abs(isometricY - isometricY1)

可能有更有效的方法,但由于我不懂笛卡尔几何,所以找不到那个解决方案。

编辑isometricWidth 找到了两点之间的距离而不是宽度和高度 另一个注意事项是你需要对角线(是的,我意识到其他人可能有更好的答案:P)


isometricHeight 是多少?或者你能更详细地解释一下你的数学工作原理吗? - deery50

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接