斥力向量

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 留在这里如果有人想编辑答案并提出建议