斥力矢量

Repulsion Vector

本文关键字:      更新时间:2023-10-16

我正在尝试在SFML和C++中为炮塔游戏实现基本的AI,但我遇到了一些问题。 这个AI遵循贝塞尔库尔夫中建立的一些航点。 首先,这条路只有一个敌人。为此,敌人必须计算他实际位置之间的距离 到下一个航点,他必须选择。 如果距离小于我们确定的特定值,那么,我们到达下一个点。这将重复,直到到达最终目的地。(在提交代码中,忘记 var m_go)

好吧,当我们生成几个敌人并且所有敌人都必须遵循相同的路径时,我们的问题就出现了,因为它会产生糟糕的视觉效果(每个人都会得到另一个)。 为了解决这个视觉问题,我们决定使用斥力向量。微积分是这样的:表示我们想要的东西

如您所见,我们用敌人与其最近邻居之间的距离倒数计算斥力矢量。 然后,我们得到它将其应用于"理论"方向,通过添加它,我们得到一个结果,即 我们的敌人必须跟随,以免与邻居"碰撞"。 但是,我们的问题来了: 敌人在曲线中间被分隔开来,随着我们生成更多的敌人,所有敌人的速度都会急剧增加(包括不计算反叛向量的敌人)。

1 - 这个隔膜通常发生在轨迹的中间吗?

2 - 有没有办法控制这个方向而不会影响速度?

3 - 这个理论还有其他选择吗?

我提交下面的代码(西班牙语中有一个变量[resultante],它在英语中表示结果):

if (!m_pathCompleted) {
if (m_currentWP == 14 && m_cambio == true) {
m_currentWP = 0;
m_path = m_pathA;
m_cambio = false;
}
if (m_neighbors.size() > 1) {
for (int i = 0; i < m_neighbors.size(); i++) {
if (m_enemyId != m_neighbors[i]->GetId()) {
float l_nvx = m_neighbors[i]->GetSprite().getPosition().x - m_enemySprite.getPosition().x;
float l_nvy = m_neighbors[i]->GetSprite().getPosition().y - m_enemySprite.getPosition().y;
float distance = std::sqrt(l_nvx * l_nvx + l_nvy * l_nvy);
if (distance < MINIMUM_NEIGHBOR_DISTANCE) {
l_nvx *= -1;
l_nvy *= -1;
float l_vx = m_path[m_currentWP].x - m_enemySprite.getPosition().x;
float l_vy = m_path[m_currentWP].y - m_enemySprite.getPosition().y;
float l_resultanteX = l_nvx + l_vx;
float l_resultanteY = l_nvy + l_vy;
float l_waypointDistance = std::sqrt(l_resultanteX * l_resultanteX + l_resultanteY * l_resultanteY);
if (l_waypointDistance < MINIMUM_WAYPOINT_DISTANCE) {
if (m_currentWP == m_path.size() - 1) {
std::cout << "n";
std::cout << "[GAME OVER]" << std::endl;
m_go = false;
m_pathCompleted = true;
} else {
m_currentWP++;
}
}
if (l_waypointDistance > MINIMUM_WAYPOINT_DISTANCE) {
l_resultanteX = l_resultanteX / l_waypointDistance;
l_resultanteY = l_resultanteY / l_waypointDistance;
m_enemySprite.move(ENEMY_SPEED * l_resultanteX * dt, ENEMY_SPEED * l_resultanteY * dt);
}
} else {
float vx = m_path[m_currentWP].x - m_enemySprite.getPosition().x;
float vy = m_path[m_currentWP].y - m_enemySprite.getPosition().y;
float len = std::sqrt(vx * vx + vy * vy);
if (len < MINIMUM_WAYPOINT_DISTANCE) {
if (m_currentWP == m_path.size() - 1) {
std::cout << "n";
std::cout << "[GAME OVER]" << std::endl;
m_go = false;
m_pathCompleted = true;
} else {
m_currentWP++;
}
}
if (len > MINIMUM_WAYPOINT_DISTANCE) {
vx = vx / len;
vy = vy / len;
m_enemySprite.move(ENEMY_SPEED * vx * dt, ENEMY_SPEED * vy * dt);
}
}
}
}
} else {
float vx = m_path[m_currentWP].x - m_enemySprite.getPosition().x;
float vy = m_path[m_currentWP].y - m_enemySprite.getPosition().y;
float len = std::sqrt(vx * vx + vy * vy);
if (len < MINIMUM_WAYPOINT_DISTANCE) {
if (m_currentWP == m_path.size() - 1) {
std::cout << "n";
std::cout << "[GAME OVER]" << std::endl;
m_go = false;
m_pathCompleted = true;
} else {
m_currentWP++;
}
}
if (len > MINIMUM_WAYPOINT_DISTANCE) {
vx = vx / len;
vy = vy / len;
m_enemySprite.move(ENEMY_SPEED * vx * dt, ENEMY_SPEED * vy * dt);
}
}
}

我将尝试一一回答您的问题,但首先,我在代码中没有看到任何严重的错误,因此它可能只是一组未考虑的情况。

1 - 这个隔膜通常发生在中间吗 弹道?

好吧,你正在根据足够接近的其他敌人的距离对每个敌人施加排斥力。如果发生奇怪的事情,或者你移动它们超过必要,可能会导致它们偏离原来的轨迹。

2 - 有没有办法在没有速度的情况下控制这个方向 受到影响?

在这一行中

m_enemySprite.move(ENEMY_SPEED * l_resultanteX * dt, ENEMY_SPEED * l_resultanteY * dt);

我们看到你实际上是根据l_resultante矢量施加排斥力的。该向量直接取决于l_nv(斥力向量),其模块(或长度)与this(您现在正在处理的敌人)和other(邻居)之间的距离成正比。当你把这个向量乘以敌人的速度(一个常数值)时,距离越大,施加的力就越大,他们之间的距离就会更大。

我建议你:

  • 规范化向量l_nv(更简单):这是强制它有模块 1。有了这个解决方案,每个敌人都将被推入相同的力量(基本上是ENEMY_SPEED),但方向正确。

  • 反转向量l_nv(稍微困难一点):如果应用此向量与距离成反比(模块 = 1/距离),它们的行为将相反,如果它们彼此相距较远,它们将被推得更少。

还要考虑到您正在连续施加力,并且您正在使它们通过每个处理的邻居有效。这意味着一些不受欢迎的事情。如果你推动一个敌人,这支部队可以把它移动到一个位置,未来的敌人(在for循环中)可能会比以前更多地推动它。如果这种效果串联几次,可能会引发连锁反应,你的敌人被推得越来越多。如果您施加与距离成比例的力,则此效果将被放大。

3 - 这个理论还有其他选择吗?

我实际上没有想法,但如果有人想编辑答案并提出一些建议,我会在这里留下这个空间

相关文章:
  • 没有找到相关文章