11

マウスをドラッグできるようにするかなり単純な Java アプリケーションを作成しました。マウスのドラッグの長さに基づいて、その方向にボールを発射し、壁に跳ね返ります。

簡単なスクリーンショットを次に示します:
代替テキスト http://img222.imageshack.us/img222/3179/ballbouncemf9.png

画面上の円のそれぞれがボール オブジェクトです。ボールの動きは、x および y ベクトルに分解されます。

public class Ball {
    public int xPos;
    public int yPos;
    public int xVector;
    public int yVector;

    public Ball(int xPos, int yPos, int xVector, int yVector) {
        this.xPos = xPos;
        this.yPos = yPos;
        this.xVector = xVector;
        this.yVector = yVector;
    }

    public void step()
    {
        posX += xVector;
        posY += yVector;

        checkCollisions();
    }

    public void checkCollisions()
    {
        // Check if we have collided with a wall
        // If we have, take the negative of the appropriate vector
        // Depending on which wall you hit
    }

    public void draw()
    {
        // draw our circle at it's position
    }
}

これはうまくいきます。すべてのボールが壁から壁へと飛び跳ねます。

しかし、重力の影響を含めることができるようにしたいと決めました。オブジェクトが地球に向かって 9.8m/s で加速することは知っていますが、これがどのようにコードに変換されるかは直接わかりません。yVector が影響を受けることはわかっていますが、これを使った私の実験では、私が望んでいた効果が得られませんでした。

理想的には、このプログラムに重力効果を追加し、ボールが「地面」に落ち着く前に数回バウンドできるようにしたいと考えています。

この跳ねるような弾力のある重力効果を作成するにはどうすればよいですか? 各ステップでボールの速度ベクトルを操作するにはどうすればよいですか? それが「地面」に当たったときに、再び跳ね返ることができるようにするために何をしなければなりませんが、前回よりも少し短くなりますか?

私を正しい方向に向けるために、どんな助けでも大歓迎です。


みんなコメントありがとう!それはすでにうまく機能しています!

私の step() では、人々が提案したように重力定数を yVector に追加しています。これが私の checkCollision() です。

public void checkCollision()
{
    if (posX - radius < 0)              // Left Wall?
    {
        posX = radius;              // Place ball against edge
        xVector = -(xVector * friction);
    }
    else if (posX + radius > rightBound) // Right Wall?
    {
        posX = rightBound - radius;     // Place ball against edge
        xVector = -(xVector * friction);
    }

    // Same for posY and yVector here.
}

ただし、ボールは引き続き床を滑ったり転がったりします。これは、バウンスごとにベクトルのパーセンテージ (90%) を単純に取得しているためであり、それが真にゼロになることは決してないためだと思います。xVector が特定の絶対値になった場合はゼロに変更する必要があるというチェックを追加する必要がありますか?

4

9 に答える 9

15

あなたがしなければならないことは、yVector から小さな定数 (9.8 m/s を表すもの) を常に減算することです。ボールが下降している場合 (yVector が既に負の値)、これによりボールの移動が速くなります。上昇している場合 (yVector が正の場合)、速度が低下します。

これでは摩擦が考慮されないため、物体はほぼ永遠に跳ね返るはずです。

edit1: 摩擦を考慮して、摩擦が反転する (そして符号を反転する) たびに、絶対数を少し下げます。yVector=-500 でヒットした場合のように、符号を反転すると、+500 ではなく +480 になります。おそらく、xVector に対して同じことを行って、左右に跳ね返らないようにする必要があります。

edit2: また、「空気摩擦」に反応させたい場合は、調整ごとに両方のベクトルをごくわずかに減らします。

edit3: 底辺を永遠に転がっているものについて -- あなたの数字がどれだけ高いかに応じて、それは 2 つのうちの 1 つになる可能性があります。数値が大きく、完了するのに永遠にかかるように見えるか、丸めを行っていて、ベクトルが常に 5 か何かです。(5 の 90% は 4.5 なので、切り上げて 5 になる場合があります)。

デバッグ ステートメントを出力して、Vector 番号がどのようなものかを確認します。値が 5 付近に達してそこにとどまる場合は、端数を切り捨てて 5 に戻す代わりに、端数を 4 に切り捨てる関数を使用できます。値が下がり続けて最終的に止まる場合は、摩擦係数を上げる必要があるかもしれません。 .

簡単な「丸め」関数が見つからない場合は、(0.9 * ベクトル) - 1 を使用できます。既存の方程式から 1 を引くと、同じことが行われます。

于 2008-12-04T21:47:14.530 に答える
13

ボールがすべて地面を転がっているときは、はい、速度が特定の最小値を下回っているかどうかを確認し、そうであればゼロに設定します。この種の理想化された運動の背後にある物理学を見て、現実世界で起こっていることと比較すると、単一の方程式を使用して、実際のボールが動かなくなるという事実を説明できないことがわかります。

ところで、あなたがやっていることは、数値積分のオイラー法と呼ばれています。こんなふうになります:

  • 運動の運動方程式から始めます:
    x(t) = x0 + vx*t + 0.5*ax t^2
    y(t) = y0 + vy
    t + 0.5*ay t^2
    vx(t) = vx0 + ax
    t
    vy(t) = vy0 + ay*t
    ここで、x と y は位置、vx と vy は速度、ax と y は加速度、t は時間です。x0、y0、vx0、vy0 は初期値です。これは、外力がない場合の物体の運動を表します。

  • ここで重力を適用します:
    y = -9.8 m/s^2
    この時点まで、難しいことは何もする必要はありません。この方程式を使用して、各ボールの位置をいつでも解くことができます。

  • ここで空気摩擦を追加します。これは球状のボールなので、摩擦係数は c であると仮定できます。空気摩擦をモデル化する方法には、通常 2 つの選択肢があります。速度または速度の 2 乗に比例する場合があります。正方形を使用してみましょう:
    ax = -c vx^2
    ay = -c
    vy^2 - 9.8
    加速度は一定ではない速度に依存するため、積分する必要があります。手でこれを解決する方法がないため、これは悪いことです。数値的に統合する必要があります。

  • 離散時間ステップ dt を取ります。オイラーの方法では、上記の方程式のすべての t を単に dt に置き換え、初期値 x0、y0 などの代わりに前のタイムステップの値を使用します。したがって、方程式は次のようになります (疑似コード)。 :

    // 以前の値を保存します
    xold = x;
    yold = y;
    vxold = vx;
    vyold = vy;

    // 加速度を更新
    ax = -c vxold^2;
    ay = -c
    vyold^2 - 9.8;

    // 速度を更新
    vx = vxold + ax dt;
    vy = vyold + ay
    dt;

    // 位置
    x を更新 = xold + vxold*dt + 0.5*ax dt^2;
    y = yold + vyold
    dt + 0.5*ay*dt^2;

これは概算であるため、正確には正確ではありませんが、問題ないように見えます。問題は、タイムステップが大きくなると誤差が大きくなるため、実際のボールがどのように動くかを正確にモデル化するには、dt に非常に小さな値を使用する必要があり、コンピューターの精度に問題が生じることです。それを解決するために、より複雑なテクニックがあります。しかし、重力と摩擦のような振る舞いを同時に見たいだけなら、オイラーの方法で問題ありません。

于 2008-12-05T17:45:54.220 に答える
1

弾道運動です。したがって、x 軸で直線的な動きが得られ、y 軸で一様に加速された動きが得られます。

基本的な考え方は、y 軸が次の式に従うということです。

y = y0 + v0 * t + (0.5)*a*t^2

または、C コードでは、たとえば次のようになります。

float speed = 10.0f, acceleration = -9.8f, y = [whatever position];

y += speed*t + 0.5f*acceleration*t^2;

ここで、tiem パラメーター化を使用します。ただし、Torricelli を使用できます。

v = sqrt(v0^2 + 2*acceleration*(y-y0));

また、このモデルでは、v と y の最後の値を維持する必要があります。

最後に、dt (時間差) が 1/60 秒 (60 FPS) に固定されている最初のモデルを使用して、同様のことを行いました。

まあ、どちらのモデルも本物そっくりの良い結果をもたらしますが、たとえば sqrt() はコストがかかります。

于 2010-12-05T05:10:48.090 に答える
1

スライスするたびに、ボールを下方向に加速して重力の効果を適用する必要があります。Bill K が示唆したように、「yVector」から減算するのと同じくらい簡単です。ボールが底に当たったとき、yVector = -yVector であるため、ボールは上向きに動いていますが、下向きにはまだ加速しています。最終的にボールの跳ね返りを止めたい場合は、衝突を少し非弾性にする必要があります。基本的には、y-up 方向の速度をいくらか取り除くことによって、おそらく "yVector = -yVector" の代わりに "yVector = -0.9" にします。 * yVector".

于 2008-12-04T21:54:00.553 に答える
1
public void step()
{
    posX += xVector;
    posY += yVector;

    yVector += g //some constant representing 9.8

    checkCollisions();
}

checkCollisions() では、yVector が地面で跳ね返るときに、yVector を反転して 0 から 1 の間の数値で乗算する必要があります。これにより、目的の効果が得られるはずです

于 2008-12-04T21:54:57.683 に答える
0

「地面」にぶつかったときに何をしなければならないので、再び跳ね返らせることができますか

完全な衝突 (つまり、すべてのエネルギーが保存される) を想定する場合、ぶつかった壁に応じて速度スカラーの符号を逆にするだけで済みます。

たとえば、ボールが右または左の壁に当たった場合、x スカラー コンポーネントを変更し、y スカラー コンポーネントを同じままにします。

 this.xVector = -this.xVector;

ボールが上壁または下壁に当たった場合、y スカラー コンポーネントを反転し、x スカラー コンポーネントを同じままにします。

 this.yVector = -this.yVector;

前回より少し短い?

このシナリオでは、エネルギーの一部が壁との衝突で失われるため、損失係数を追加して、壁に衝突するたびに速度の一部を取得します。

 double loss_factor = 0.99;
 this.xVector = -(loss_factor * this.xVector);
 this.yVector = -(loss_factor * this.yVector;
于 2008-12-04T22:37:05.820 に答える
0

重力が行うことを実際にシミュレートしたいのですが、重力が行うのは、オブジェクトの速度を変化させるために時間の経過とともに作用する力を作成することだけです。一歩進むたびに、ウィジェットの下部に向かってボールを「引っ張る」ために、ボールの速度を少し変更します。

摩擦なし/バウンドするボールが安定する問題に対処するには、「地面」の衝突が単なる厳密な反射とは異なる効果を発揮するようにする必要があります。地面にぶつかった後の速度は、そうでない場合よりも小さくなります。

このような弾力性のあるビジュアライゼーションで一般的にやりたいことは、地面に横方向の摩擦を与えることです。これにより、常に地面にぶつかると、最終的に転がって停止します。

于 2008-12-04T21:55:00.553 に答える
0

「Bill K」が言ったことに同意し、それらを「解決」したい場合は、時間の経過とともに x および y ベクトルを減らす (抵抗を適用する) 必要があると付け加えます。これは一度に非常に少量にする必要があるため、ベクトルを int から浮動小数点型に変更するか、数秒ごとに 1 ずつ減らす必要がある場合があります。

于 2008-12-04T21:55:07.603 に答える
0

やりたいことは、xVector と yVector の値を変更して、重力と摩擦をシミュレートすることです。これは非常に簡単です。(すべての変数を float に変更する必要があります。描画するときは、float を丸めます。)

ステップ関数では、ボールの位置を更新した後、次のようにする必要があります。

yVector *= 0.95;
xVector *= 0.95;
yVector -= 2.0;

これにより、X と Y の速度がわずかに低下し、最終的にボールの動きが停止し、一定の下向きの「加速」が Y 値に適用されます。これにより、「減速」よりも速く蓄積され、ボールが落下します。

これは、実際にやりたいことの概算です。本当に必要なのは、ボールの加速を表すベクトルを保持することです。次に、そのベクトルに一定の重力ベクトルを内積して、ボールの加速をわずかに変更します。しかし、より現実的な物理シミュレーションを探しているのでない限り、私はあなたが望むよりも複雑になると思います.

于 2008-12-04T21:55:34.233 に答える