斥力向量
Repulsion Vector
我正在尝试使用 SFML 和 C++ 为炮塔游戏实现基本 AI,但我遇到了一些问题。
该 AI 遵循在贝塞尔曲线中建立的一些 waypoints。
首先,这条道路上只有一个敌人。为此,敌人必须计算他与实际位置之间的距离
到他必须选择的下一个航路点。
如果距离小于我们确定的特定值,那么,我们到达下一个点。这将重复,直到到达最终目的地。 (在提交代码中,忘记 var m_go)
好的,当我们生成多个敌人并且所有敌人都必须遵循相同的路径时,我们的问题就出现了,因为它会产生糟糕的视觉效果(每个人都在另一个上面)。
为了解决这个视觉问题,我们决定使用排斥向量。微积分是这样的:representation of what we want
如你所见,我们用敌人和他最近的邻居之间的距离的倒数来计算排斥矢量。
然后,我们将它应用到 "theorical" 方向,通过添加它,我们得到一个结果,这是方向
我们的敌人必须跟随而不是 "collide" 它的邻居。
但是,我们的问题来了:
敌人在曲线中间分开,随着我们产生更多的敌人,所有敌人的速度都会急剧增加(包括不计算排斥矢量的敌人)。
1 - 这种分离通常发生在轨迹的中间吗?
2 - 有没有办法在不影响速度的情况下控制这个方向?
3 - 除了这个理论还有其他选择吗?
我提交下面的代码(西班牙语中有一个变量[resultante],它的意思是英语中的resultant):
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 - Is it usual that this sepparation occours in the middle of the
trajectory?
好吧,你根据与其他人 足够近 的距离向每个敌人施加排斥力。如果发生奇怪的事情,或者如果您过度移动它们,可能会导致它们偏离原始轨迹。
2 - Is it there a way to control this direction without the speed
getting affected?
在这一行
m_enemySprite.move(ENEMY_SPEED * l_resultanteX * dt, ENEMY_SPEED * l_resultanteY * dt);
我们看到您实际上是在根据 l_resultante
向量施加排斥力。该矢量直接取决于 l_nv
(排斥矢量),其模数(或 长度)与 成比例 this
(你现在正在处理的敌人)和 other
(邻居)。当你将这个向量乘以敌人的速度(一个常数值)时,距离越大,施加的力越大,它们之间的距离就越大。
我建议你:
归一化向量 l_nv
(更简单): 这是,强制它有模1. 使用这个解决方案,每个敌人都会被相同的力量(基本上 ENEMY_SPEED
)推向正确的方向。
Inverse the vector l_nv
(Little harder): 如果你应用这个向量成反比到距离(模数 = 1/距离),它们将表现相反,如果它们彼此距离越远,它们将被推动得越少。
还要考虑到您正在连续施加力,并且正在使每个处理过的邻居都有效。这意味着一些不受欢迎的事情。如果你推一个敌人,这个力可以将它移动到一个未来的敌人(在 for 循环中)可能比以前更多地推它的位置。如果此效果连续多次,可能会触发 连锁反应 ,使您的敌人被推得越来越远。如果您施加与距离成比例的力,这种效果将会被放大。
3 - Is it there any alternative to this theory?
我实际上 运行 没有想法,但我把这个 space 留在这里如果有人想编辑答案并提出建议
我正在尝试使用 SFML 和 C++ 为炮塔游戏实现基本 AI,但我遇到了一些问题。 该 AI 遵循在贝塞尔曲线中建立的一些 waypoints。 首先,这条道路上只有一个敌人。为此,敌人必须计算他与实际位置之间的距离 到他必须选择的下一个航路点。 如果距离小于我们确定的特定值,那么,我们到达下一个点。这将重复,直到到达最终目的地。 (在提交代码中,忘记 var m_go)
好的,当我们生成多个敌人并且所有敌人都必须遵循相同的路径时,我们的问题就出现了,因为它会产生糟糕的视觉效果(每个人都在另一个上面)。 为了解决这个视觉问题,我们决定使用排斥向量。微积分是这样的:representation of what we want
如你所见,我们用敌人和他最近的邻居之间的距离的倒数来计算排斥矢量。 然后,我们将它应用到 "theorical" 方向,通过添加它,我们得到一个结果,这是方向 我们的敌人必须跟随而不是 "collide" 它的邻居。 但是,我们的问题来了: 敌人在曲线中间分开,随着我们产生更多的敌人,所有敌人的速度都会急剧增加(包括不计算排斥矢量的敌人)。
1 - 这种分离通常发生在轨迹的中间吗?
2 - 有没有办法在不影响速度的情况下控制这个方向?
3 - 除了这个理论还有其他选择吗?
我提交下面的代码(西班牙语中有一个变量[resultante],它的意思是英语中的resultant):
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 - Is it usual that this sepparation occours in the middle of the trajectory?
好吧,你根据与其他人 足够近 的距离向每个敌人施加排斥力。如果发生奇怪的事情,或者如果您过度移动它们,可能会导致它们偏离原始轨迹。
2 - Is it there a way to control this direction without the speed getting affected?
在这一行
m_enemySprite.move(ENEMY_SPEED * l_resultanteX * dt, ENEMY_SPEED * l_resultanteY * dt);
我们看到您实际上是在根据 l_resultante
向量施加排斥力。该矢量直接取决于 l_nv
(排斥矢量),其模数(或 长度)与 成比例 this
(你现在正在处理的敌人)和 other
(邻居)。当你将这个向量乘以敌人的速度(一个常数值)时,距离越大,施加的力越大,它们之间的距离就越大。
我建议你:
归一化向量
l_nv
(更简单): 这是,强制它有模1. 使用这个解决方案,每个敌人都会被相同的力量(基本上ENEMY_SPEED
)推向正确的方向。Inverse the vector
l_nv
(Little harder): 如果你应用这个向量成反比到距离(模数 = 1/距离),它们将表现相反,如果它们彼此距离越远,它们将被推动得越少。
还要考虑到您正在连续施加力,并且正在使每个处理过的邻居都有效。这意味着一些不受欢迎的事情。如果你推一个敌人,这个力可以将它移动到一个未来的敌人(在 for 循环中)可能比以前更多地推它的位置。如果此效果连续多次,可能会触发 连锁反应 ,使您的敌人被推得越来越远。如果您施加与距离成比例的力,这种效果将会被放大。
3 - Is it there any alternative to this theory?
我实际上 运行 没有想法,但我把这个 space 留在这里如果有人想编辑答案并提出建议