如何将等距精灵按正确的顺序排序?


19

在正常的自上而下的2D游戏中,可以使用屏幕y轴对图像进行排序。在此示例中,树木已正确排序,但等轴墙却没有:

示例图片:按屏幕y排序

墙2比墙1低一个像素,因此它是在墙1之后绘制的,并在顶部结束。

如果按等轴y轴排序,则墙壁会以正确的顺序显示,但树木不会:

示例图片:按等轴y排序

如何正确执行此操作?


3
听起来您遇到了Painter's Algorithm中常见的问题。“常见”的解决方案是使用z-buffer =)。一些解决方法包括使用对象中心作为排序键而不是某个角。
Jari Komppa

Answers:


12

等距游戏在功能上是3D的,因此在内部,您应该存储游戏中每个实体的3D坐标。您选择的实际坐标是任意的,但假设X和Y是两个地面坐标轴,而Z则离地面不远。

然后,渲染器需要将其投影到2D中以在屏幕上绘制内容。“等轴测”就是这样一种投影。等轴测从3D投影到2D非常简单。假设X轴从左上角到右下角,并且每两个水平像素向下一个像素。同样,Y轴从右上角到左下角。Z轴直线上升。要将3D转换为2D,只需:

function projectIso(x, y, z) {
    return {
        x: x - y,
        y: (x / 2) + (y / 2) - z
    };
}

现在到您的原始问题,排序。现在,我们直接在3D模式下处理对象,排序变得更加简单。在我们这里的坐标空间中,最远的精灵具有最低的x,y和z坐标(即,所有三个轴都指向屏幕)。因此,您只需按以下各项的总和对它们进行排序:

function nearness(obj) {
    return obj.x + obj.y + obj.z;
}

function closer(a, b) {
    if (nearness(a) > nearness(b)) {
        return "a";
    } else {
        return "b";
    }
}

为了避免在每个帧中对实体进行重新排序,请使用鸽孔排序,请在此处进行详细说明。


1
我知道这是一个古老的起点,但是您知道如何也可以合并对象的3d边界,而不仅仅是其翻译。当它们重叠时,深度排序变得更加复杂。
罗布

4

假设您的子画面占据了一组矩形的瓦片(如果它们占据了任意集合,那么在一般情况下您根本无法正确绘制),问题是元素之间没有总顺序关系,因此无法排序他们使用会导致O(nlogn)比较的排序。

请注意,对于任何两个对象A和B,要么应该在B之前绘制A(A <-B),要么应该在A之前绘制B(A <-A),或者可以以任何顺序绘制它们。它们形成部分顺序。如果您自己绘制一些带有3个重叠对象的示例,您可能会注意到,即使第1个对象和第3个对象可能不重叠,因此也没有直接的依存关系,它们的绘制顺序取决于它们之间的第2个对象-取决于放置后,您将获得不同的绘图顺序。底线-传统排序在这里不起作用。

一种解决方案是使用比较(由Dani提及)并将每个对象与其他对象进行比较以确定它们的依存关系,并形成一个依存关系图(将是DAG)。然后对图形进行拓扑排序以确定绘制顺序。如果对象太多,则可能足够快(是O(n^2))。

另一种解决方案是使用(用于平衡- )四叉树并将所有对象的矩形存储到其中。

然后遍历所有对象X,并使用四叉树检查对象X上方的条纹中是否有对象Y,该对象Y从对象X的最左端开始到最右角-对于所有此类Y,Y < -X。像这样,您仍然必须形成图形并进行拓扑排序。

但是您可以避免。您使用对象列表Q和对象表T。在x轴(一行)上,将所有可见的插槽从较小的值迭代到较大的值,在y轴上逐行进行。如果在该插槽中有一个对象的底角,请执行上述步骤以确定相关性。如果对象X依赖于部分位于其上方的其他某个对象Y(Y <-X),并且每个这样的Y已经存在于Q中,则将X添加到Q中。如果存在某个不在Q中的Y,则将X添加到T并表示Y <-X。每次将对象添加到Q时,都将删除T中待处理对象的依赖关系。如果删除了所有依赖关系,则T中的对象将移至Q。

我们假设对象精灵不会从其底部,左侧或右侧的插槽中窥视(仅在顶部,如图片中的树)。这将提高大量对象的性能。这种方法将再次成为O(n^2),但仅在最坏的情况下才出现,其中包括怪异大小的对象和/或怪异的对象配置。在大多数情况下为O(n * logn * sqrt(n))。知道精灵的高度可以消除sqrt(n),因为您不必检查上方的整个条纹。根据屏幕上对象的数量,您可以尝试用指示替换哪个插槽的数组替换四叉树(如果有很多对象,这很有意义)。

最后,请随时检查此源代码以获取一些想法:https : //github.com/axel22/sages/blob/master/src/gui/scala/name/brijest/sages/gui/Canvas.scala


2

我认为没有数学解决方案。您的物品所在的2D世界中可能没有足够的数据。如果您的墙在X上镜像,则它们的顺序为“正确”。再一次,您也许可以通过某种方式对图像的边框进行重叠测试,但这是我不熟悉的领域。

您可能应该按屏幕Y的每分位数进行排序,并说更复杂的事情是“设计问题”。例如,如果您要编写内容,则只需告诉您的设计师排序算法,然后将wall2推高2个像素即可解决问题。这就是我们必须在我开发的等距游戏中修复它的方式。这可能包括取出“长”物品并将其分解为瓷砖大小的块。

如果您允许用户编辑内容,则完全安全的做法是使所有内容都基于图块,并且最多使一个图块变大。这样就可以避免问题。您可能可以通过使所有内容都大于瓷砖来实现,但前提是必须是正方形。我还没有玩过。


2

完美的等距分选很难。但是,对于第一种方法,要对项目进行正确排序,您应该对每个重叠的对象使用更复杂的比较函数。此功能必须检查以下情况:如果满足以下条件,则重叠对象“ a”位于“ b”之后:

(a.posX + a.sizeX <= b.posX)或(a.posY + a.sizeY <= b.posY)或(a.posZ + a.sizeZ <= b.posZ)

当然,这是朴素的等距实现的第一个想法。对于更复杂的情况(如果您想要视图旋转,z轴上的每个像素位置等),您需要检查更多条件。


+1,如果您有不同宽度/高度的图块,请查看此答案中的比较功能。
mucaho 2015年

2

我使用一个非常简单的公式,该公式与我在OpenGL中的小等距引擎很好地配合使用:

您的每个对象(树木,地砖,字符等)在屏幕上都有X和Y位置。您需要启用深度测试并为每个值找到合适的Z值。您可以简单地执行以下操作:

z = x + y + objectIndex

我对地板和将要在地板上方的对象使用和的不同索引(地板为0,所有具有高度的对象为1)。这应该很好。



1

如果在相同的x位置比较y值,则每次都会起作用。因此,请勿将中心与中心进行比较。而是将一个精灵的中心与另一个精灵的相同x位置进行比较。

在此处输入图片说明

但是,这需要每个精灵的一些几何数据。从左侧到右侧的两个点描述了精灵的下边界可能很容易。或者,您可以分析精灵图像数据并找到不透明的第一个像素。

一种更简单的方法是将所有子画面分为三组:沿x轴对角线,沿y轴对角线和平面。如果两个对象都沿同一轴对角线,则根据另一个轴对它们进行排序。


0

您需要为相同类型的所有对象分配唯一的ID。然后,您可以按位置对所有对象进行排序,并按其ID的顺序绘制对象组。因此,组A中的对象1永远不会覆盖组A中的对象2等。


0

这不是一个真正的答案,而只是我要在此处对axel22的答案进行评论和投票的地方 /gamedev//a/8181/112940

我没有足够的声誉来投票或评论其他答案,但是他的答案的第二段可能是在不依靠“现代” 3D尝试在等距游戏中对实体进行排序时应该意识到的最重要的事情Z缓冲区之类的技术。

在引擎中,我想做纯2D的“老式”游戏。而且我花了很多时间试图弄清楚为什么我对“ sort”(在我的情况下是c ++ std :: sort)的调用在某些地图配置上无法正常工作。

只有当我意识到这是一种“偏序”情况时,我才能够解决它。

到目前为止,我在网络上找到的所有可行解决方案都使用某种拓扑排序来正确处理该问题。拓扑排序似乎是要走的路。

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.