简体   繁体   English

敌人路径跟踪(太空射击游戏)

[英]Enemies path following (Space Shooter game)

I am recently working with SFML libraries and I am trying to do a Space Shooter game from scratch.我最近正在使用 SFML 库,并且正在尝试从头开始制作 Space Shooter 游戏。 After some time working on it I get something that works fine but I am facing one issue and I do not know exactly how to proceed, so I hope your wisdom can lead me to a good solution.经过一段时间的工作后,我得到了一些可以正常工作的东西,但我面临一个问题,我不知道该怎么做,所以我希望你的智慧能引导我找到一个好的解决方案。 I will try to explain it the best I can:我会尽力解释它:

Enemies following a path: currently in my game, I have enemies that can follow linear paths doing the following:沿着路径的敌人:目前在我的游戏中,我的敌人可以沿着线性路径执行以下操作:

   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 is a vector that basically holds the 2d points to be followed. *m_wayPoints_v 是一个向量,它基本上包含要遵循的 2d 点。

Related to this small piece of code, I have to say that is sometimes given me problems because getting closer to the next point becomes difficult as the higher the speed of the enemies is.与这一小段代码相关,我不得不说这有时会给我带来问题,因为敌人的速度越高,越接近下一个点变得越困难。

Is there any other way to be more accurate on path following independtly of the enemy speed?有没有其他方法可以在独立于敌人速度的情况下更准确地跟踪路径? And also related to path following, if I would like to do an introduction of the enemies before each wave movement pattern starts (doing circles, spirals, ellipses or whatever before reaching the final point), for example:并且还与路径跟踪有关,如果我想在每个波浪运动模式开始之前介绍敌人(在到达终点之前做圆圈,螺旋,椭圆或其他任何事情),例如:

For example, in the picture below:例如,在下图中:

在此处输入图像描述

The black line is the path I want a spaceship to follow before starting the IA pattern (move from left to right and from right to left) which is the red circle.黑线是我希望宇宙飞船在开始 IA 模式(从左到右和从右到左移动)之前遵循的路径,即红色圆圈。

Is it done hardcoding all and each of the movements or is there any other better solution?它是否对所有和每个动作都进行了硬编码,还是有其他更好的解决方案?

I hope I made myself clear on this...in case I did not, please let me know and I will give more details.我希望我清楚这一点......如果我没有,请告诉我,我会提供更多细节。 Thank you very much in advance!非常感谢您!

Way points航点

You need to add some additional information to the way points and the NPC's position in relationship to the way points.您需要在路点和与路点相关的 NPC 的 position 中添加一些附加信息。

The code snippet (pseudo code) shows how a set of way points can be created as a linked list.代码片段(伪代码)显示了如何将一组路点创建为链表。 Each way point has a link and a distance to the next way point, and the total distance for this way point.每个路点都有一个链接和到下一个路点的距离,以及这个路点的总距离。

Then each step you just increase the NPC distance on the set of way points.然后每一步你只需增加一组路点上的 NPC 距离。 If that distance is greater than the totalDistance at the next way point, follow the link to the next .如果该距离大于下一个路径点的totalDistance ,请点击指向next的链接。 You can use a while loop to search for the next way point so you will always be at the correct position no matter what your speed.您可以使用 while 循环来搜索下一个路径点,这样无论您的速度如何,您都将始终处于正确的 position 位置。

Once you are at the correct way point its just a matter of calculating the position the NPC is between the current and next way point.一旦您处于正确的航路点,只需计算 position NPC 就位于当前航路点和下一个航路点之间。

Define a way point定义航路点

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;
}

Declaring and linking waypoints声明和链接航路点

   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 follows way pointy path at any speed 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;
   

Note you can link back to the start but be careful to check when the total distance goes back to zero in the while loop or you will end up in an infinite loop.请注意,您可以链接回起点,但要小心检查 while 循环中总距离何时回到零,否则您将进入无限循环。 When you pass zero recalc NPC distance as modulo of last way point totalDistance so you never travel more than one loop of way points to find the next.当您通过零时,重新计算 NPC distance作为最后一个路点totalDistance的模数,因此您永远不会经过一个以上的路点循环来找到下一个路点。

eg in while loop if passing last way point例如,如果经过最后一个路点,则在 while 循环中

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

Smooth paths平滑路径

Using the above method you can add additional information to the way points.使用上述方法,您可以将附加信息添加到路点。

For example for each way point add a vector that is 90deg off the path to the next.例如,为每个航路点添加一个与下一个路径成 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

Then when you calculate the unitDist along the line between to way points you can use that unit dist to smoothly interpolate the offset然后,当您沿路点之间的线计算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;

Now if you add 3 way points with the first offDist a positive distance and the second a negative offDist you will get a path that does smooth curves as you show in the image.现在,如果您添加 3 个路径点,第一个offDist为正距离,第二个为负offDist ,您将获得一条路径,如您在图像中显示的那样平滑曲线。

Note that the actual speed of the NPC will change over each way point.请注意,NPC 的实际速度会随每个航点而变化。 The maths to get a constant speed using this method is too heavy to be worth the effort as for small offsets no one will notice.使用这种方法获得恒定速度的数学太繁重,不值得付出努力,因为对于没有人注意到的小偏移量。 If your offset are too large then rethink your way point layout如果您的偏移量太大,请重新考虑您的航点布局

Note The above method is a modification of a quadratic bezier curve where the control point is defined as an offset from center between end points注意上述方法是二次贝塞尔曲线的修改,其中控制点定义为端点之间的中心偏移量

Sigmoid curve S形曲线

You don't need to add the offsets as you can get some (limited) smoothing along the path by manipulating the unitDist value (See comment in first snippet)您不需要添加偏移量,因为您可以通过操纵unitDist值沿路径获得一些(有限的)平滑(参见第一个片段中的注释)

Use the following to function convert unit values into a bell like curve sigBell and a standard ease out in curve.使用以下 function 将单位值转换为钟形曲线sigBell和标准缓出曲线。 Use argument power to control the slopes of the curves.使用参数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);
}

This doesn't answer your specific question.这不能回答您的具体问题。 I'm just curious why you don't use the sfml type sf::Vector2 (or its typedefs 2i, 2u, 2f)?我只是好奇你为什么不使用 sfml 类型 sf::Vector2 (或其 typedefs 2i、2u、2f)? Seems like it would clean up some of your code maybe.似乎它可能会清理您的一些代码。

As far as the animation is concerned.就 animation 而言。 You could consider loading the directions for the flight pattern you want into a stack or something.您可以考虑将您想要的飞行模式的方向加载到堆栈或其他东西中。 Then pop each position and move your ship to that position and render, repeat.然后弹出每个 position 并将您的船移动到该 position 并渲染,重复。

And if you want a sin-like flight path similar to your picture, you can find an equation similar to the flight path you like.而如果你想要一个类似于你图片的类似 sin 的飞行路径,你可以找到一个类似于你喜欢的飞行路径的方程。 Use desmos or something to make a cool graph that fits your need.使用 desmos 或其他东西制作适合您需要的酷图。 Then iterate at w/e interval inputting each iteration into this equation, your results are your position at each iteration.然后以 w/e 间隔进行迭代,将每次迭代输入此方程,您的结果是每次迭代时的 position。

Well, I think I found one of the problems but I am not sure what the solution can be.好吧,我想我发现了其中一个问题,但我不确定解决方案是什么。

When using the piece of code I posted before, I found that there is a problem when reaching the destination point due to the speed value.使用我之前贴的这段代码时,发现由于速度值的原因,到达目的地点时出现问题。 Currently to move a space ship fluently, I need to set the speed to 200...which means that in these formulas:目前要流畅地移动太空船,我需要将速度设置为 200 ......这意味着在这些公式中:

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

The new position might exceed the "2.0f" tolerance so the space ship cannot find the destination point and it gets stuck because the minimum movement that can be done per frame (assuming 60fps) 200 * 1 / 60 = 3.33px.新的 position 可能超过“2.0f”容差,因此太空船无法找到目的地点并且卡住,因为每帧可以完成的最小移动(假设 60fps)200 * 1 / 60 = 3.33px。 Is there any way this behavior can be avoided?有什么办法可以避免这种行为?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM