【问题标题】:Enemies path following (Space Shooter game)敌人路径跟踪(太空射击游戏)
【发布时间】:2020-07-05 08:19:50
【问题描述】:

我最近正在使用 SFML 库,并且正在尝试从头开始制作太空射击游戏。经过一段时间的工作,我得到了一些可以正常工作的东西,但我面临一个问题,我不知道该怎么做,所以我希望你的智慧可以引导我找到一个好的解决方案。我会尽力解释它:

沿路径的敌人:目前在我的游戏中,我的敌人可以沿线性路径执行以下操作:

   float vx = (float)m_wayPoints_v[m_wayPointsIndex_ui8].x - (float)m_pos_v.x;
   float vy = (float)m_wayPoints_v[m_wayPointsIndex_ui8].y - (float)m_pos_v.y;

   float len = sqrt(vx * vx + vy * vy);
   //cout << len << endl;
   if (len < 2.0f)
   {
      // Close enough, entity has arrived
      //cout << "Has arrived" << endl;
      m_wayPointsIndex_ui8++;
      if (m_wayPointsIndex_ui8 >= m_wayPoints_v.size())
      {
         m_wayPointsIndex_ui8 = 0;
      }
   }
   else
   {
      vx /= len;
      vy /= len;

      m_pos_v.x += vx * float(m_moveSpeed_ui16) * time;
      m_pos_v.y += vy * float(m_moveSpeed_ui16) * time;
   }

*m_wayPoints_v 是一个向量,它基本上包含要跟踪的二维点。

关于这段小代码,我不得不说这有时会给我带来问题,因为敌人的速度越高,越接近下一个点变得困难。

有没有其他方法可以在独立于敌人速度的情况下更准确地跟踪路径?并且还与路径跟踪有关,如果我想在每个波浪运动模式开始之前介绍敌人(在到达终点之前做圆圈、螺旋、椭圆或其他任何事情),例如:

比如下图:

黑线是我希望宇宙飞船在开始 IA 模式(从左到右和从右到左移动)之前遵循的路径,即红色圆圈。

是否对所有动作都进行了硬编码,还是有其他更好的解决方案?

我希望我清楚地表明了这一点......如果我没有,请告诉我,我会提供更多细节。非常感谢您!

【问题讨论】:

    标签: c++ game-physics sfml


    【解决方案1】:

    路点

    你需要添加一些额外的信息到路点和NPC相对于路点的位置。

    代码 sn-p(伪代码)显示了如何将一组路点创建为链表。每个路点都有一个链接和到下一个路点的距离,以及这个路点的总距离。

    然后每一步你只需增加一组路点上的 NPC 距离。如果该距离大于下一个路径点的totalDistance,请点击指向next 的链接。您可以使用 while 循环来搜索下一个路径点,这样无论您的速度如何,您都将始终处于正确的位置。

    一旦你在正确的路点,只需计算 NPC 在当前和下一个路点之间的位置。

    定义一个路径点

    class WayPoint {
      public:
        WayPoint(float, float);
        float x, y, distanceToNext, totalDistance;
        WayPoint next;
        WayPoint addNext(WayPoint wp);
    
    }
    WayPoint::WayPoint(float px, float py) { 
        x = px; y = py; 
        distanceToNext = 0.0f;
        totalDistance = 0.0f;
    }
        
    WayPoint WayPoint::addNext(WayPoint wp) {
        next = wp;
        distanceToNext = sqrt((next.x - x) * (next.x - x) + (next.y - y) * (next.y - y));
        next.totalDistance =  totalDistance + distanceToNext;    
        return wp;
    }
    

    声明和链接航路点

       WayPoint a(10.0f, 10.0f);
       WayPoint b(100.0f, 400.0f);
       WayPoint c(200.0f, 100.0f);
       a.addNext(b);
       b.addNext(c);
       
    

    NPC 以任何速度跟随尖锐的路径

       WayPoint currentWayPoint = a;
       NPC ship;
       
       ship.distance  += ship.speed * time;
       while (ship.distance > currentWayPoint.next.totalDistance) {
           currentWayPoint = currentWayPoint.next;
       }
       float unitDist = (ship.distance - currentWayPoint.totalDistance)  / currentWayPoint.distanceToNext;
       
       // NOTE to smooth the line following use the ease curve. See Bottom of answer
       // float unitDist = sigBell((ship.distance - currentWayPoint.totalDistance)  / currentWayPoint.distanceToNext);
       
       ship.pos.x = (currentWayPoint.next.x - currentWayPoint.x) * unitDist + currentWayPoint.x;
       ship.pos.y = (currentWayPoint.next.y - currentWayPoint.y) * unitDist + currentWayPoint.y;
       
    

    注意您可以链接回起点,但要小心检查 while 循环中总距离何时回到零,否则您将进入无限循环。当您通过零重新计算 NPC distance 作为最后一个路点 totalDistance 的模数时,您永远不会经过一个以上的路点循环才能找到下一个路点。

    例如,如果经过最后一个路点,则在 while 循环中

    if (currentWayPoint.next.totalDistance == 0.0f) {
         ship.distance = mod(ship.distance, currentWayPoint.totalDistance);
    }
    

    平滑路径

    使用上述方法,您可以将附加信息添加到路点。

    例如,为每个航路点添加一个与下一个路径成 90 度的矢量。

    // 90 degh CW
    offX = -(next.y - y) / distanceToNext; // Yes offX = - y
    offY = (next.x - x) / distanceToNext;  // 
    offDist = ?; // how far from the line you want to path to go
    

    然后,当您沿路点之间的线计算 unitDist 时,您可以使用该单位 dist 平滑地插入偏移量

    float unitDist = (ship.distance - currentWayPoint.totalDistance)  / currentWayPoint.distanceToNext;
    // very basic ease in and ease out  or use sigBell curve
    float unitOffset = unitDist < 0.5f ? (unitDist * 2.0f) * (unitDist * 2.0f) : sqrt((unitDist - 0.5f) * 2.0f);
    
    
    float x = currentWayPoint.offX * currentWayPoint.offDist * unitOffset;
    float y = currentWayPoint.offY * currentWayPoint.offDist * unitOffset;
    ship.pos.x = (currentWayPoint.next.x - currentWayPoint.x) * unitDist + currentWayPoint.x + x;
    ship.pos.y = (currentWayPoint.next.y - currentWayPoint.y) * unitDist + currentWayPoint.y + y;
    

    现在,如果您添加 3 个路点,第一个 offDist 距离为正,第二个距离为负 offDist,您将获得一条路径,该路径可以像您在图像中显示的那样平滑曲线。

    注意 NPC 的实际速度会随着每个航点的变化而变化。使用这种方法获得恒定速度的数学太繁重,不值得付出努力,因为对于没有人注意到的小偏移量。如果您的偏移量太大,请重新考虑您的航点布局

    注意上述方法是对二次贝塞尔曲线的修改,其中控制点定义为端点之间距离中心的偏移量

    S形曲线

    您不需要添加偏移量,因为您可以通过操纵 unitDist 值沿路径获得一些(有限的)平滑(参见第一个 sn-p 中的注释)

    使用以下函数将单位值转换为钟形曲线sigBell 和标准缓出曲线。使用参数power 来控制曲线的斜率。

    float sigmoid(float unit, float power) { // power should be > 0. power 1 is straight line 2 is ease out ease in 0.5 is ease to center ease from center
        float u = unit <= 0.0f ? 0.0f : (unit >= 1.0f ? 1.0f: unit); // clamp as float errors will show
        float p = pow(u, power);
        return p / (p + pow(1.0f - u, power));
    }
    float sigBell(float unit, float power) {
        float u = unit < 0.5f ? unit * 2.0f : 1.0f - (unit - 0.5f) * 2.0f;
        return sigmoid(u, power);
    }
    

    【讨论】:

    • 你好 Blindman67!非常感谢你。你的回答完美解决了我的问题!你太棒了:)
    【解决方案2】:

    这并不能回答您的具体问题。我只是好奇你为什么不使用 sfml 类型 sf::Vector2 (或其 typedefs 2i、2u、2f)?似乎它可能会清理您的一些代码。

    就动画而言。您可以考虑将您想要的飞行模式的方向加载到堆栈或其他东西中。然后弹出每个位置并将您的船移动到该位置并渲染,重复。

    如果你想要一个类似于你图片的类似 sin 的飞行路径,你可以找到一个类似于你喜欢的飞行路径的方程。使用 desmos 或其他东西制作适合您需要的酷图。然后以 w/e 间隔进行迭代,将每次迭代输入到这个方程中,你的结果就是你在每次迭代中的位置。

    【讨论】:

    • 您好 ArtVandelay_,首先非常感谢您的回答。我的错误没有在我的帖子中指定这一点,但 m_pos_v 是 Vector2f,不确定这是否是您的意思。我明白你的意思,但是当路径与直线不同时,问题就出现了,因为使用 cos 和 sin 函数很难在某个点结束。我会检查什么是desmos,因为我不知道!谢谢:)
    【解决方案3】:

    嗯,我想我发现了其中一个问题,但我不确定解决方案是什么。

    在使用我之前贴的那段代码时,发现由于速度值,到达目的地点时出现问题。目前要流畅地移动宇宙飞船,我需要将速度设置为 200...这意味着在这些公式中:

         m_pos_v.x += vx * float(m_moveSpeed_ui16) * time;
         m_pos_v.y += vy * float(m_moveSpeed_ui16) * time;
    

    新位置可能超过“2.0f”容差,因此太空船无法找到目标点,并且卡住,因为每帧可以完成的最小移动(假设 60fps)200 * 1 / 60 = 3.33px。有什么办法可以避免这种行为?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多