31

私はいくつかの基本的な物理コードを使用してオブジェクトを最高速度に加速しようとしている 2D ゲームに取り組んでいます。

その擬似コードは次のとおりです。


const float acceleration = 0.02f;
const float friction     = 0.8f;  // value is always 0.0..1.0
      float velocity     = 0;
      float position     = 0;

move()
{
   velocity += acceleration;
   velocity *= friction;
   position += velocity;
}

これは、質量や実際の摩擦に依存しない非常に単純化されたアプローチです (コード内の摩擦は、動きに対して作用する単なる一般的な力です)。「速度 *= 摩擦;」としてうまく機能します。一部は、速度が特定のポイントを超えないようにします。しかし、私が少し迷っているのは、この最高速度と、加速と摩擦との関係です。

私がやりたいのは、最高速度とそれに到達するのにかかる時間を設定し、それらを使用して加速度と摩擦の値を導き出すことです。

つまり、


const float max_velocity = 2.0; 
const int   ticks;       = 120; // If my game runs at 60 FPS, I'd like a 
                                // moving object to reach max_velocity in 
                                // exactly 2 seconds.
const float acceleration = ?
const float friction     = ?
4

6 に答える 6

40

私は最近、ドラッグによる発射体の動きのモデリングに関する作業を行っていたので、この質問は非常に興味深いものでした。

ポイント 1:基本的に、状態の新しい各値が古い値の関数である明示的/前方オイラー反復を使用して、位置と速度を更新しています。このような場合、最初に位置を更新してから、速度を更新する必要があります。

ポイント 2:抗力摩擦の効果については、より現実的な物理モデルがあります。1 つのモデル ( Adam Lissによって提案された) には、速度に比例する抗力 (ストークス抗力として知られ、一般に低速度の状況に適用される) が含まれます。私が以前に提案したものには、速度の2 乗に比例する抗力が含まれます(二次抗力として知られ、一般に高速の状況に適用されます)。最大速度と最大速度に効果的に到達するのに必要な時間の公式をどのように推測するかについて、それぞれについて説明します。完全な派生はかなり複雑なので省略します。


ストークスの抗力:

速度を更新する式は次のようになります。

velocity += acceleration - friction*velocity

これは、次の微分方程式を表します。

dv/dt = a - f*v

この積分表の最初のエントリを使用して、解を見つけることができます (t = 0 で v = 0 と仮定)。

v = (a/f) - (a/f)*exp(-f*t)

最大 (つまり終末) 速度は t >> 0 のときに発生するため、方程式の第 2 項はゼロに非常に近くなり、次のようになります。

v_max = a/f

最大速度に到達するのに必要な時間に関しては、方程式が実際に到達することはなく、それに向けて漸近することに注意してください。ただし、指数関数の引数が -5 に等しい場合、速度は最大速度の約 98% であり、おそらく等しいと考えるのに十分近いでしょう。次に、最大速度までの時間を次のように概算できます。

t_max = 5/f

次に、これらの 2 つの方程式を使用して、目的のvmaxtmaxを指定してfaを解くことができます。


二次抗力:

速度を更新する式は次のようになります。

velocity += acceleration - friction*velocity*velocity

これは、次の微分方程式を表します。

dv/dt = a - f*v^2

この積分表の最初のエントリを使用して、解を見つけることができます (t = 0 で v = 0 と仮定)。

v = sqrt(a/f)*(exp(2*sqrt(a*f)*t) - 1)/(exp(2*sqrt(a*f)*t) + 1)

t >> 0 の場合に最大 (終末) 速度が発生するため、指数項は 1 よりはるかに大きくなり、式は次のようになります。

v_max = sqrt(a/f)

最大速度に到達するのに必要な時間に関しては、方程式が実際に到達することはなく、漸近することに注意してください。ただし、指数関数の引数が 5 に等しい場合、速度は最大速度の約 99% であり、おそらく等しいと考えるのに十分近いでしょう。次に、最大速度までの時間を次のように概算できます。

t_max = 2.5/sqrt(a*f)

これは以下と同等です:

t_max = 2.5/(f*v_max)

目的のvmaxtmaxの場合、 tmaxの 2 番目の方程式はfがどうあるべきかを示し、それをvmaxの方程式に差し込んでaの値を得ることができます。


これは少しやり過ぎのように思えますが、実際にはドラッグをモデル化する最も簡単な方法です。統合手順を本当に見たい人は、私にメールを送ってください。送信します。ここに入力するには少し複雑すぎます。

別のポイント:これはすぐにはわかりませんでしたが、代わりにv(t)に対して導出した式を使用すれば、速度の更新は不要になります。単純に静止状態からの加速をモデル化し、加速が始まってからの時間を追跡している場合、コードは次のようになります。

position += velocity_function(timeSinceStart)

ここで、「velocity_function」はv(t)の 2 つの式の 1 つであり、速度変数は不要になります。一般に、ここにはトレードオフがあります。v(t)の計算は、単純に反復スキームで速度を更新するよりも計算コストが高くなる可能性がありますが (指数項のため)、安定して制限されたままであることが保証されています。特定の条件下 (非常に短いtmaxを取得しようとする場合など) では、反復が不安定になり、爆発する可能性があります。これは、フォワード オイラー法によくある問題です。ただし、変数の制限 (0 < f < 1 など) を維持することで、これらの不安定性を防ぐことができます。

さらに、多少自虐的であると感じている場合は、v(t)の式を統合してp(t)の閉じた形式の解を取得できる場合があります。これにより、ニュートン反復の必要性が完全になくなります。他の人が試すためにこれを残します。=)

于 2009-03-20T17:13:37.980 に答える
3

警告: 部分的な解決策

述べたように物理学に従えば、最大速度はありません。純粋に物理的な観点からは、加速度を一定の値に固定しました。つまり、速度は常に増加しています。

別の方法として、オブジェクトに作用する 2 つの力を考えてみましょう。

  • それを加速する傾向がある一定の外力F、および
  • 抗力dは速度に比例し、速度を遅くする傾向があります。

したがって、反復時の速度は次のようにnなります。v n = v 0 + n F - d v n-1

iteration で発生する最大速度v nmaxnmaxを選択するよう求めました。

問題は制約不足であることに注意してください。つまり、Fdは関連しているため、一方の値を任意に選択してから、他方を計算できます。

ボールが転がり始めた今、誰かが数学を手に入れようとしていますか?

警告:これは見苦しく、ベキ級数が関係しています!


n**F**編集:の後にスペースがない限り、最初の式のシーケンスが文字どおりに表示されるのはなぜnですか?

于 2009-03-20T20:25:21.653 に答える
2
velocity *= friction;

これは、速度が特定のポイントを通過するのを妨げません...

摩擦は、速度が増加するにつれて指数関数的に増加し (引用しないでください)、静止時には 0 になります。やがて、摩擦=加速度のポイントに到達します。

したがって、次のようなものが必要です。

velocity += (acceleration - friction);
position += velocity;
friction = a*exp(b*velocity);

a と b の値を選択する場所。b は最高速度に到達するまでの時間を制御し、a は摩擦の急激な増加を制御します。(繰り返しますが、これについて独自の調査を行わないでください。12 年生の物理学で覚えていることから行っています。)

于 2009-03-20T17:11:49.763 に答える
2

これはあなたの質問への回答ではありませんが、このようなシミュレーションですべきでないことの 1 つは、固定フレーム レートに依存することです。最後の更新からの時間を計算し、式でデルタ T を使用します。何かのようなもの:

static double lastUpdate=0;
if (lastUpdate!=0) {
  deltaT = time() - lastUpdate;
  velocity += acceleration * deltaT;
  position += velocity * deltaT;
}
lastUpdate = time();

また、フォーカスを失って更新を停止したかどうかを確認し、フォーカスが得られたら lastUpdate を 0 に設定することもお勧めします。そうすることで、戻ったときに巨大な deltaT を処理する必要がなくなります。

于 2009-03-20T21:09:34.700 に答える
1

これはおそらくあなたが探しているものではありませんが、あなたが取り組んでいるエンジンによっては、farseer(C#用)のような他の誰かによって構築されたエンジンを使用する方が良いかもしれません。 Codeplexはメンテナンスのためダウンしています。

于 2009-03-20T20:50:00.643 に答える
1

非常に単純な数学を使用して非常単純な物理モデルで何ができるかを確認したい場合は、 http: //scratch.mit.edu/ でいくつかの Scratch プロジェクトを参照してください。いくつかの有用なアイデアが得られる可能性があります。確かに楽しんでください。

于 2009-03-20T20:30:04.047 に答える