2D宇宙ゲームを作成していますが、宇宙船が惑星を妨害する必要があります。直線切片の作業コードはありますが、円軌道での惑星の位置の計算方法がわかりません。
ゲームは科学的に正確ではないので、慣性、重力、楕円軌道などについて心配していません。
宇宙船の位置と速度、そして惑星の軌道(半径)と速度も知っています。
2D宇宙ゲームを作成していますが、宇宙船が惑星を妨害する必要があります。直線切片の作業コードはありますが、円軌道での惑星の位置の計算方法がわかりません。
ゲームは科学的に正確ではないので、慣性、重力、楕円軌道などについて心配していません。
宇宙船の位置と速度、そして惑星の軌道(半径)と速度も知っています。
回答:
これに対する分析ソリューションは困難ですが、バイナリ検索を使用して、必要な精度内でソリューションを見つけることができます。
船は時間t_minで軌道上の最も近い点に到達できます。
shipOrbitRadius = (ship.position - planet.orbitCenter).length;
shortestDistance = abs(shipOrbitRadius - planet.orbitRadius);
t_min = shortestDistance/ship.maxSpeed;
船は、t_max以下の時間で軌道上の任意のポイントに到達できます。
(ここでは、簡単にするために、船は太陽を通り抜けることができると仮定します。これを避けたい場合は、少なくともいくつかの場合、非直線のパスに切り替える必要があります。 mechanics-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
。これらの2つの値のうち、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値に対してerror = 0のインターセプトが少なくとも1つ必要です)
ここで、完全性のために、位置関数は次のようなものです...
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フレームで軌道を横断でき、1フレーム以内の正確な切片が必要な場合、約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)と呼ばれるもので簡単に計算されます
船のラインを選択し、数学を実行して、惑星の軌道と衝突するかどうかを確認します。もしそうなら、そのポイントに到達するのにかかる時間を計算します。この時間を使用して、惑星とともにこの地点から軌道に戻り、船が移動し始めたときに惑星がどこにあるべきかを計算します。
以下に、わずかに「すぐに使える」2つのソリューションを示します。
問題は、船が与えられた速度で直線で動き、惑星が与えられた角速度で与えられた半径の円で動き、惑星と船の開始位置がどの方向ベクトルを決めるかを考えるとインターセプトコースをプロットするには直線が必要です。
解決策1:質問の前提を否定します。問題の「スリップ可能」な量は角度です。代わりに、それを修正してください。軌道の中心に船をまっすぐ向けます。
解決策2:自動操縦ではまったく行わないでください。プレイヤーがスラスタを使用して惑星に接近しなければならないミニゲームを作成し、相対速度が高すぎる場合は爆破しますが、燃料も限られています。プレイヤーにインターセプト問題の解決方法を学ばせましょう!
極座標を使用したくない場合は、船の可能なすべての位置が スペース。この方程式は
どこで は船の速度です。船はゼロから始まると想定されています。
空間と時間における惑星の位置は、例えば
どこで から行く 上向き。 は角速度であり、 時間ゼロでの惑星の開始角度です。次に、船と惑星が時間と空間で出会う場所を解決します。次の方程式が得られます 解決する:
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.