2D顶视角平滑路径查找libGDX

我正在尝试制作一个AI伙伴,它会跟随玩家(由你控制)移动,目前它可以工作,但当我添加碰撞检测后,当伙伴碰到障碍物时效果就不太好了。我只是想知道有什么最好的方法(比如A*算法的实现)来让AI的移动更加平滑并避免障碍物?这是我当前的伙伴类中的更新方法:

public void update() {    setBounds(getX(), getY(), getWidth(), getHeight());    float xDiff = Math.abs(player.getX() - getX());    float yDiff = Math.abs(player.getY() - getY());    if (player.getX() > getX() && xDiff > buddyDistance) {        setX(getX()+speed);    }    else if (player.getX() < getX() && xDiff > buddyDistance) {        setX(getX()-speed);    }    if (player.getY() > getY() && yDiff > buddyDistance) {        setY(getY()+speed);    }    else if (player.getY() < getY() && yDiff > buddyDistance) {        setY(getY()-speed);    }}

回答:

一个易于实现且可能有效的解决方案是使用势场方法,这取决于你的障碍物类型。

这个想法很简单:玩家就像一个磁铁,吸引伙伴靠近自己。同时,障碍物会排斥伙伴,使其避开它们。

我将首先使用向量来解释,而不是Java,以便更易读。

假设b是伙伴的位置,p是玩家的位置,o_1, ... o_k是你的障碍物位置。

b, p, o_1, ..., o_k每个都是具有xy坐标的二维向量。

那么向量(p-b)是从伙伴指向玩家的向量。我们还需要向量(b-o_i),它是从障碍物i指向伙伴的向量。此外,我们不直接使用向量(p-b)(b-o_i),而是先对它们进行归一化处理。

然后,normalized(p-b)就是我们吸引伙伴到玩家所需的全部内容。

为了使伙伴远离障碍物,我们希望当伙伴靠近障碍物时排斥力强,当远离时排斥力小(甚至为零)。因此,一个明显的选择是用1/|b-o_i|来缩放我们想要的方向,即normalized(b-o_i),其中|.|表示向量的范数。

现在,我们可以简单地混合所有这些“磁力”如下:

w = normalized(p-b) + normalized(b-o_1)/|b-o_1| + ... + normalized(b-o_l)/|b-o_k|

这个向量w通常指向玩家,但每当伙伴靠近障碍物时,它就会被排斥,这正是你想要的效果。

但是,我们如何确保伙伴以正确的速度移动呢?这很简单。我们归一化w,然后按速度缩放。也就是说,我们的最终速度向量是v = speed*w/|w|

这可以很容易地添加到你的代码中:

public void update() {    setBounds(getX(), getY(), getWidth(), getHeight()); //我保留了你的代码中的这一行,但实际上我不知道它做什么    float dx = player.getX() - getX(); //注意:我删除了abs    float dy = player.getY() - getY();    float norm = Math.sqrt(dx*dx + dy*dy);    //归一化:    float wx = dx/norm;    float wy = dy/norm;    for (obstacle o : obstacles) { //假设obstacles是一个包含障碍物类实例的可迭代数据结构         //注意,只需迭代附近的障碍物即可         dx = getX() - o.getX();         dy = getY() - o.getY();         norm = Math.sqrt(dx*dx + dy*dy);         //归一化:         float ox = dx/norm;         float oy = dy/norm;         //添加缩放以获得我们想要的排斥力         wx += ox/norm;         wy += oy/norm;    }     float norm_of_w = Math.sqrt(wx*wx + wy*wy);    float vx = speed * wx / norm_of_w;    float vy = speed * wy / norm_of_w;    setX(getX() + vx);    setY(getY() + vy);}

不幸的是,有几点需要考虑:

  • 不同类型的排斥可能比1/|b-o_i|更有效,例如1/|b-o_i|^2。
  • 尝试调整力的大小可能会有所帮助,例如尝试c*(b-o_i)/|b-o_i|,使用不同值的c(即ox = c*dx/norm;等等)。如果c太小,伙伴会一定程度上移动到障碍物中,如果c非常大,伙伴在离障碍物较远时就会开始避开它们。对于不同大小的障碍物使用不同的c值可能会得到更好的结果。
  • 如果障碍物是圆形形状并且两个障碍物之间有足够的空间,避开障碍物效果最佳。否则,伙伴可能会陷入局部最优,玩家需要通过移动到一个可以将伙伴从局部最优中拉出来的位置来“救援”他。
  • 如果障碍物不是圆形而是大型多边形,你可以尝试使用非常大的排斥力来覆盖多边形的大部分(即对于这种类型的障碍物使用非常大的c)。优点是伙伴可以避开障碍物,但不幸的是,如果你想让它靠近障碍物,它会因为强烈的排斥而拒绝靠近。
  • 重要的是要记住,当伙伴和障碍物靠近时,1/|b-o_i|会很大。如果它们在同一位置,你的程序会尝试除以零。你可能需要检查这种情况并避免它。

这就是全部内容,但值得注意的是,通常在使用势场时,目标使用负电荷,障碍物使用正电荷,即

w = -|p-b| + 1/|b-o_1| + ... + 1/|b-o_k|

请注意,这里w只是一个标量而不是向量。然后,应用梯度下降法向目标移动。这意味着计算w相对于b.xb.y的梯度。这个梯度然后指向到达玩家的方向,同时避开障碍物。这是一个比我提出的方法更好的方法,但需要更多的数学知识。你可以尝试或询问这是否是你想要的。


如果障碍物形状任意且局部最小值对你来说不可接受,最可能的最佳答案是使用Delaunay三角剖分结合漏斗算法。你可以在https://www.aaai.org/Papers/AAAI/2006/AAAI06-148.pdf中阅读更多相关信息

但我假设你更喜欢易于实现的东西。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注