我正在创建2D太空游戏,需要使飞船拦截一颗行星。我有用于直线截距的工作代码,但无法弄清楚如何计算圆形轨道上的行星位置。
该游戏在科学上不够准确,因此我不必担心惯性,重力,椭圆轨道等。
我知道太空飞船的位置和速度,也知道行星的轨道(半径)和速度
我正在创建2D太空游戏,需要使飞船拦截一颗行星。我有用于直线截距的工作代码,但无法弄清楚如何计算圆形轨道上的行星位置。
该游戏在科学上不够准确,因此我不必担心惯性,重力,椭圆轨道等。
我知道太空飞船的位置和速度,也知道行星的轨道(半径)和速度
Answers:
对此的解析解决方案很困难,但是我们可以使用二进制搜索来找到所需精度范围内的解决方案。
船舶可以在时间t_min到达轨道上的最近点:
shipOrbitRadius = (ship.position - planet.orbitCenter).length;
shortestDistance = abs(shipOrbitRadius - planet.orbitRadius);
t_min = shortestDistance/ship.maxSpeed;
船舶可以在小于或等于t_max的时间内到达轨道上的任何一点:
(在这里,为简单起见,我假设这艘船可以在阳光下行驶。如果要避免这种情况,那么至少在某些情况下,您将需要切换到非直线路径。“接环”可能看起来不错并且在轨道上力学y,而算法的更改不会超过一个常数因子)
if(shipOrbitRadius > planet.orbitRadius)
{
t_max = planet.orbitRadius * 2/ship.maxSpeed + t_min;
}
else
{
t_max = planet.orbitRadius * 2/ship.maxSpeed - t_min;
}
如果我们的轨道周期很短,我们也许可以通过选择,使它在行星最接近船的起始位置t_max
之后的第一次来提高这个上限t_min
。取这两个值中t_max
的较小者。请参阅后面的答案,以了解其工作原理。
现在我们可以在这些极端t_min和t_max之间使用二进制搜索。我们将搜索误差接近零的t值:
error = (planet.positionAtTime(t) - ship.position).squareMagnitude/(ship.maxSpeed*ship.maxSpeed) - t*t;
(使用这种构造,错误@ t_min> = 0且错误@ t_max <= 0,因此对于中间的t值,必须至少有一个截距,错误= 0)
为了完整起见,位置函数类似于...
Vector2 Planet.positionAtTime(float t)
{
angle = atan2(startPosition - orbitCenter) + t * orbitalSpeedInRadians;
return new Vector2(cos(angle), sin(angle)) * orbitRadius + orbitCenter;
}
请注意,如果行星的轨道周期与船舶的速度相比非常短,则此误差函数可能会在从t_min到t_max的跨度内多次改变符号。只需跟踪您遇到的最早的+ ve和-ve对,然后继续在它们之间进行搜索,直到误差足够接近于零(“足够接近”对您的单位和游戏环境敏感)即可。运作良好-确保截取准确到一帧之内)
一旦有了一个使误差最小的t,就可以将飞船指向planet.positionAtTime(t)并全开油门,确信行星会在您同时达到该点。
您始终可以在Log_2((2 * orbitRadius / ship.maxSpeed)/ errorThreshold)迭代中找到解决方案。因此,例如,如果我的飞船可以在60帧内横越轨道,并且我希望截距精确到一帧内,那么我将需要进行大约6次迭代。
让我们不要过于复杂。这不是一个“完美”的解决方案,但适用于大多数游戏,并且任何瑕疵对于玩家来说都是不可见的。
if(!OldTargetPoint)
TargetPoint = PlanetPosition;
else
TargetPoint = OldTargetPoint;
Distance = CurPosition - TargetPoint;
TimeNeeded = Distance / Speed;
TargetPoint = PlanetPositionInFuture(TimeNeeded);
SteerTowards(TargetPoint);
[...repeat this every AI update, for example every second...]
之所以起作用,是因为航天器越接近误差越小。因此,随着时间的推移,计算变得更加稳定。
误差是计算出的到达行星所需时间(TimeNeeded)与到达行星所需的实际时间(在考虑了新的TargetPoint之后)之间的差。
让我们从问题背后的数学开始。
找到直线和形状之间的交点,只需将直线的方程式插入形状方程式中即可,在这种情况下为圆形。
取一个中心为c且半径为r的圆。如果点p在圆上
平方距离可以重写为点积(http://en.wikipedia.org/wiki/Dot_product)。
这是一个简单的二次方程,我们得出解
我们该怎么办?好了,我们现在知道了船的航行距离以及它将到达的终点!
现在,剩下要做的就是计算当船开始驶向轨道时行星应该在哪里。这可以通过所谓的Polar coodinates(http://mathworld.wolfram.com/PolarCoordinates.html)轻松计算。
为您的飞船选择一条直线,然后运行数学运算以查看其是否与行星轨道发生碰撞。如果是这样,请计算到达该点所需的时间。利用这段时间从该点开始从行星返回轨道,以计算飞船开始移动时行星应位于的位置。
这里有两个“开箱即用”的解决方案。
问题是:给定船舶以给定速度沿直线运动,行星以给定角速度沿给定半径的圆运动,并且行星和船舶的起始位置确定了船舶的方向矢量直线应位于绘制拦截路线。
解决方案一:否认问题的前提。问题中“可滑动”的数量是角度。相反,请解决该问题。将飞船直指轨道中心。
解决方案二:完全不使用自动驾驶仪。制作一个微型游戏,玩家必须使用推进器来接近该行星,如果以相对较高的速度撞击该行星,它们会炸毁,但燃料也有限。让玩家学习如何解决拦截问题!
如果您不想使用极坐标,请考虑船舶的所有可能位置在形成圆锥 space. The equation for this is
where is the the ship velocity. It is assumed the ship starts at zero.
The position of the planet in space and time can be parametrized by e.g.
where goes from upwards. is the angular speed and is the starting angle of the planet at time zero. Then solve where the ship and planet could meet in time and space. You get an equation for to solve:
This equation needs to be solved numerically. It may have many solutions. By eyeballing it, it seems it always has a solution
Here's part of a solution. I didn't get to finish it in time. I'll try again later.
If I understand correctly, you have a planet's position & velocity, as well as a ship's position and speed. You want to get the ship's movement direction. I'm assuming the ship's and planet's speeds are constant. I also assume, without loss of generality, that the ship is at (0,0); to do this, subtract the ship's position from the planet's, and add the ship's position back onto the result of the operation described below.
Unfortunately, without latex, I can't format this answer very well, but we'll attempt to make do. Let:
s_s
= the ship's speed (s_s.x, s_s.y, likewise)s_a
= the ship's bearing (angle of movement, what we want to calculate)p_p
= the planet's initial position, global coordsp_r
= the planet's distance (radius) from the center of orbit, derivable from p_p
p_a
= the planet's initial angle in radians, relative to the center of orbitp_s
= the planet's angular velocity (rad/sec)t
= the time to collision (this turns out to be something we must calculate as well)Here's the equations for the position of the two bodies, broken down into components:
ship.x = s_s.x * t * cos(s_a)
ship.y = s_s.y * t * sin(s_a)
planet.x = p_r * cos(p_a + p_s * t) + p_p.x
planet.y = p_r * sin(p_a + p_s * t) + p_p.y
Since we want ship.x = planet.x
and ship.y = planet.y
at some instant t
, we obtain this equation (the y
case is nearly symmetrical):
s_s.x * t * cos(s_a) = p_r * cos(p_a + p_s * t) + p_p.x
s_s.y * t * sin(s_a) = p_r * sin(p_a + p_s * t) + p_p.y
Solving the top equation for s_a:
s_s.x * t * cos(s_a) = p_r * cos(p_a + p_s * t) + p_p.x
=> s_a = arccos((p_r * cos(p_a + p_s * t) + p_p.x) / (s_s.x * t))
Substituting this into the second equation results in a fairly terrifying equation that Wolfram alpha won't solve for me. There may be a better way to do this not involving polar coordinates. If anyone wants to give this method a shot, you're welcome to it; I've made this a wiki. Otherwise, you may want to take this to the Math StackExchange.
I would fix the location at which to intercept (graze the circle, at the "outgoing" side of the orbit.)
Now you just have to adjust the spaceship's speed so that planet and ship reach that point at the same time.
Note that the rendez-vous could be after N more orbits, depending how far away the ship is, and how fast the planet is orbiting the star.
Pick the N that in time, comes nearest to the ship's journey duration at current speed.
Then speed up or slow down ship to match the timestamp for those N orbits exactly.
In all this, the actual course is already known! Just not the speed.