我正在尝试制作一个跟随玩家(由你控制)的伙伴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);
}
}
答案 0 :(得分:1)
一个易于实施且可能根据您的障碍类型而工作的解决方案是使用潜在的领域。
这个想法很简单:玩家表现得像个磁铁一样将好友拉向他。与此同时,障碍击退了伙伴,以便伙伴避开它们。
为了更好的可读性,我将首先使用向量来解释它,而不是Java。
让我们说,b
是伙伴的位置,p
是玩家的,o_1, ... o_k
是你障碍的位置。
b, p, o_1, ..., o_k
中的每一个都是具有x
和y
坐标的二维向量。
然后向量(p-b)
是从伙伴指向玩家的向量。我们还需要的是从障碍(b-o_i)
指向伙伴的向量i
。此外,我们不是直接使用向量(p-b)
,(b-o_i)
,而是先将它们标准化。
然后,normalized(p-b)
已经是我们将伙伴拉到玩家所需的全部内容。
为了从障碍物中击退好友,我们希望如果好友接近它,那么排斥就会很强烈,如果好友远离它,我们会希望排斥力很小(甚至是零)。因此,一个明显的选择是使用normalized(b-o_i)
来扩展我们想要的方向,即1/|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()); //I kept this from your code, but I don't actually know what it does
float dx = player.getX() - getX(); //note: I removed abs
float dy = player.getY() - getY();
float norm = Math.sqrt(dx*dx + dy*dy);
//normalization:
float wx = dx/norm;
float wy = dy/norm;
for (obstacle o : obstacles) { //assuming obstacles is an iterable datastructure containing instances of the class obstacle
//note, it suffices to iterate over close by obstacles
dx = getX() - o.getX();
dy = getY() - o.getY();
norm = Math.sqrt(dx*dx + dy*dy);
//normalization:
float ox = dx/norm;
float oy = dy/norm;
//add scaling to get the repulsion force we want
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);
}
不幸的是,有几件事需要考虑:
c*(b-o_i)/|b-o_i|
c
的不同值(即ox = c*dx/norm;
等等)。如果c
太小,那么伙伴会在某种程度上进入障碍,如果c
非常大,他会在远离它们的时候避开它们。对不同的障碍物大小使用不同的c值也可能会产生更好的结果。1/|b-o_i|
很大。如果它们处于相同位置,那么您的程序将尝试除以零。您可能想要检查此案例并避免它。就是这样,但值得注意的是,通常有潜在的领域,其想法是对目标使用负电荷,对障碍使用正电荷,即
w = -|p-b| + 1/|b-o_1| + ... + 1/|b-o_k|
请注意,这里,w只是一个标量,而不是一个向量。然后,应用梯度下降向目标移动。这意味着计算w相对于b.x,b.y的梯度。然后,该梯度指向到达玩家的方向,同时避开障碍物。这比我提出的方法更好,但需要更多的数学知识。请随意尝试或询问这是否是您想要的。
最有可能的是,如果障碍物具有任意形状并且局部最小值对您来说是不可接受的最佳答案是使用Delaunay三角测量结合漏斗算法。您可以在https://www.aaai.org/Papers/AAAI/2006/AAAI06-148.pdf
中详细了解但我认为你更喜欢易于实现的东西。