以视觉上吸引人且直观的方式传播标签的算法


24

简洁版本

是否存在一种设计模式,用于以不重叠的方式分配车辆标签,并使其尽可能靠近其所指的车辆?如果没有,我建议的任何一种方法可行吗?您将如何自己实施?

扩大的视野

在我写的游戏中,我对空中载具有鸟瞰图。在每辆车旁边,我还带有一个小标签,上面标有有关该车的关键数据。这是一个实际的屏幕截图:

两辆带有标签的车辆

现在,由于车辆可能在不同的高度飞行,它们的图标可能会重叠。但是,我永远都不要让它们的标签重叠(或者,车辆“ A”的标签与车辆“ B”的图标重叠)。

目前,我可以检测到小精灵之间的碰撞,并且只需沿与否则重叠的小精灵相反的方向推开有问题的标签即可。这在大多数情况下都可行,但是当空域变得拥挤时,即使有其他“更智能”的选择,标签也可能被推离其飞行器很远。例如我得到:

  B - label
A -----------label
  C - label

在哪个地方会更好(=标签离车辆更近):

          B - label
label - A
          C - label

编辑:还必须考虑到,在重叠车辆的情况下,可能还有其他配置,其中车辆的标签可能会重叠(例如ASCII艺术示例显示了三辆非常接近的车辆,其中的标签A会与的图标重叠BC)。

我有两个关于如何改善当前状况的想法,但是在花时间实施它们之前,我曾想过向社区征询建议(毕竟,这似乎是一个“常见的问题”,可能存在一种设计模式)。

对于它的价值,这是我在想的两个想法:

标签空间的插槽化

在这种情况下,我会将所有屏幕划分为标签的“插槽”。然后,每辆车始终将其标签放置在最近的空车上(空=该位置没有其他精灵。

螺旋搜索

从屏幕上车辆的位置开始,我将尝试将标签以递增的角度放置,然后以递增的半径放置,直到找到不重叠的位置。在以下方面:

try 0°, 10px
try 10°, 10px
try 20°, 10px
...
try 350°, 10px
try 0°, 20px
try 10°, 20px
...

1
一次可能有多少架飞机重叠?
wangburg'11年

1
@wangburger-从来没有想过这会是有意义的(想对您的思路有更多的了解),但是答案是:这取决于玩家的游戏策略。从技术上讲,世界上可能有24辆车重叠,但在大多数游戏条件下,实际数字是3-4。
mac

3
在合理的时间内相对于飞机移动标签比重叠但静止的标签更容易混淆吗?
Maik Semder 2011年

3
您可能对en.wikipedia.org/wiki / ... 感兴趣-这不是一个容易解决的问题。不要期望找到完美的解决方案。
Blecki 2011年

2
GraphViz是一套用于以视觉上令人愉悦的方式布置图形的工具,具有避免标签重叠的趋势。尽管它可能无法直接使用,但您可能能够从他们的文档或源代码中收集一些信息,了解他们使用哪种算法来布置其图形。例如,它们似乎同时具有基于能量的模型和基于弹簧的模型。
拉尔斯·维克伦德

Answers:


14

本质上,此问题类似于避免碰撞问题。是的,飞机可以在不同的高度飞行,但是它们的标签都在相同的“高度”。

Unaligned Collision Prevention这样的算法可以为您朝正确的方向迈出一步。当然,根据您的情况,标签会“束缚”在其平面上,因此它们的移动范围有限。

如果查看“ 植绒行为”则要实现植绒的第一个“规则”:短程排斥。但是,您将使用“ away”向量作为标签的放置位置,而不是在远离最近邻居的方向上进行“转向”。

例如:

在此处输入图片说明

黑色的大圆圈代表您的影响范围,绿色的圆圈代表标签的有效放置,中间的绿色圆点是您当前正在考虑的平面,小的绿色圆点是圆圈上选择用于放置标签的点。

现在,黑点可以代表其他标签或其他平面。我不确定哪一个效果最好,如果它们是其他标签,您可能会更好地避免,但是我不确定。显然,“力”箭头是您当前平面和“影响对象”之间的方向向量。最后,盒子是标签。

因此,使用上面的示例,我认为这会产生类似以下内容:

            - label
           / 
          B 
label - A
          C 
           \
            - label

使用此方法时,在某些情况下,您将需要进行特殊处理,例如垂直对齐的三个平面:

          - label       label -
          |                   |
          B                   B
  label - A                   A - label
          C                   C
          |                   |
          - label       label -

这三个标签都可以从右向左翻转,具体取决于您定义标签边角的方式。基本上,您只需要注意围绕圆的角度,标签可以在该角度上切换从哪个角度绘制标签:0、90、180、270。

在此处输入图片说明

我认为最终,这看起来会很整洁,看着标签互相避开。如果过于分散注意力,也许您可​​以舍入到最接近的10度以减少运动的频率。

对这些奇怪的细节感到抱歉,当我为游戏制作放射状菜单时,我想到的大多数东西,但我认为以这种“动态”形式,它会很好地工作。


1
实际上,我的示例甚至没有考虑直接位于彼此之上的平面,因为它们的x和z坐标完全匹配(但是如果使用浮点数,则可能不会发生)。我举的例子是飞机彼此靠近。另外,正如我所说,您可以将上图中的黑点视为其他标签。
MichaelHouse

1
哦,看来您删除了评论?
MichaelHouse

1
我之前的[措辞较差,因此现已删除]的意思是,您可能遇到这样一种情况,即标签被其他不可移动(即车辆)的精灵“困住/包围”。在这种情况下,标签可能应该“跳出”包围圈的外面,但是我不清楚这是怎么发生的。顺便说一句:我本来是说您的图片上的绿点虽然是几架飞机,但是飞机在您的评论后才意识到我是错的...对不起!)。
Mac

1
知道了 因此,您会将黑点同时视为平面和标签。如果第一次迭代找到的位置不好,则将半径加倍并再次检查。或者,您已经有了一个指向大多数人群的矢量,您可以按照它进行跟踪,直到找到合适的地方。但是我认为第一种方法会有更好的结果。
MichaelHouse

9

经过一番思考,我最终决定实施在原始问题中简要介绍的螺旋搜索方法。

理由是Byte56的方法需要在特定条件下进行特殊处理,而螺旋搜索则不需要,而且编码方式非常紧凑。同样,四处搜索会强调找到离车辆较近的位置以放置标签,而IMO是使地图可读的主要因素。

但是,请继续支持他的答案,因为它不仅有用,而且写得很好!

这是使用螺旋代码获得的结果的屏幕截图:

在此处输入图片说明

这是虽然不是完全独立的代码,但它给出了实现的简单程度的想法:

def place_tags(self):
    for tag in self.tags:
        start_angle = tag.angle
        while not tag.place() or is_colliding(tag):  #See note n.1
            tag.angle = (tag.angle + angle_step) % 360
            if tag.angle == start_angle:
                tag.radius += radius_step
        tag.connector.update()                       #See note n.2

注1 - tag.place()如果标签完全是在屏幕/雷达的可见区域返回True。因此,该行的内容类似于“如果标签在雷达外部或与其他物体重叠,请继续循环播放……”

注2 - tag.connector.update是绘制飞机图标连接到与文本信息的标签/标签的行中的方法。


做得好,确实非常紧凑。感谢您的好评。也感谢您发布了您最终所做的事情,这对于以后搜索答案的人们总是有用的。从屏幕截图看来,它运行得很好!确保接受您的答案,因为这就是您最终要解决的问题。
MichaelHouse

@ Byte56-感谢您的“复古赞美”;)我正在等待选择可接受的答案,因为我仍然想对您的解决方案进行编码并比较结果。首先,我怀疑您的解决方案可能会导致更长但更快的代码执行速度。另外,我想看看两者在拥挤的空域中如何比较……所以我仍然有机会用您的算法发布代码。关注此空间!;)
mac

@ Byte56-我可以执行您的算法。它在低密度下工作良好且速度很快,但是当空间塞满时,我就难以找到一种简单的实现方式,该实现方式可以管理特定情况,在这种情况下,标签应“跳跃”其他标签块或被非标签纸困住。可移动(即平面图标)精灵。我将这个答案标记为选中,但是再次:非常感谢您的时间和贡献!:)
mac
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.