1

LunarLander サンプル プロジェクトに基づいて小さなプログラムを作成しています。私が達成したいのは、次のような一定の間隔で更新することですSurfaceView(主にいくつかの s を再配置します)。Drawable

private static final int UPDATE_INTERVAL = 1000 / 20; // 20 FPS

Threadメソッドでこれを行っているを実行していrun()ます:

@Override
    public void run() {
        while (isRunning) {
            Canvas c = null;
            try {
                c = view.getSurfaceHolder().lockCanvas(null);
                synchronized (view.getSurfaceHolder()) {
                    if (GameState.RUNNING.equals(state)) {
                        long now = System.currentTimeMillis();
                        if (now - lastUpdate > UPDATE_INTERVAL) {
                            lastUpdate = now;
                            updater.updatePieces();
                        }
                        doDraw(c);
                    }
                }
            } finally {
                if (c != null) {
                    view.getSurfaceHolder().unlockCanvasAndPost(c);
                }
            }
        }
    }

私の問題は、これが状態になるThreadのを待ってビジーである可能性があることです。ただし、再描画に 1000 / 20 秒以上かかる場合は別のケースがあります。これは、アニメーションが常にシームレスであるとは限らないことを意味します。エミュレーターで試してみましたが、通常は問題ありませんが、速度が低下することがあります。その後、約 0.5 秒間速度が上がります。これは単なるエミュレーターですか、それとも何か他のものですか?(now - lastUpdate > UPDATE_INTERVAL)trueDrawable

詳細については気にしませんupdatePieces()。上から下に小さな円を移動するだけです。私は新しいオブジェクトを作成していません。どういうわけか、このコードは正しくない気がします。これは s をアニメーション化するための良い習慣Drawableですか、それとも明らかな何かが欠けていますか? 私はこのトピックではちょっと新しいです。

UI スレッドからこれにタッチ イベントとキー イベントを伝達するので、UI スレッドが応答しなくなる可能性があるためsleep()、これを呼び出したくありません。Thread

4

2 に答える 2

1

オブジェクトが実際にどのように反応するかを考えてみてください。オブジェクトは一定の距離を移動します。一定時間に移動する距離は、速度に依存します。速度は、最後の加速度に依存します。距離、速度、加速度はすべて時間に依存します。

あなたのコードでは、時間をまったく扱っていないため、現在のコードに対する実際の解釈はありません。物理を次のようにまとめます。

  1. オイラー積分などのデルタ システムを実装します。このシステムには、前のフレームの描画にかかった時間に関する情報が含まれている必要があります。注: オイラー積分は、加速度が変化する場合ではなく、重力のように加速度が一定の場合に正確です。しかし、それは最適化のトピックなので、今はこの問題を考慮しないでください。
  2. 前のフレームにかかった時間がわかると、オブジェクトがまったく動かなかった時間もわかります。
  3. 次の更新でその時間を考慮してください。

例えば:

public void updatePieces(float timeDelta) {
    // The amount of an acceleration depends on a force
    // in a specified direction. You can skip this, and just
    // give your object a velocity. But to consider acceleration
    // later on, you have to assign forces and resolve them in
    // their respective direction.
    mAccelerationX  = fResX / weight;             
    mAccelerationY  = fResY / weight; 

    // Consider the previous velocity and add the change
    // in velocity.
    mVelocityX = mVelocityX + (mAccelerationX * timeDelta);
    mVelocityY = mVelocityY + (mAccelerationY * timeDelta);

    mPositionX = mPositionX + (mVelocityX * timeDelta);
    mPositionY = mPositionY + (mVelocityY * timeDelta);
}

少し物理学を知っていると、これが理にかなっている理由を理解するのに役立ちます。

mAccelerationX  = fResX / weight;

は基本的にニュートンの第 2 法則ですF = ma

mVelocityX = mVelocityX + (mAccelerationX * timeDelta);

m/s^2これは、加速度を時間で乗算しているためですs。これにより、前のフレームから速度がどれだけ変化したかがわかります。かんたんキャンセルはお受けいたし(m/s^2)*s = m*s/s^2 = m/sます。前の速度を追加して完了です。

同じロジックが以下に適用されます。

mPositionX = mPositionX + (mVelocityX * timeDelta);
// (m/s)*s  = m*s/s = m

物理を画面に現実的にするために、乗算する倍率を追加する必要がある場合があることに注意してください。たとえば、ピクセル数が 5 メートルに等しいなどです。

これについて説明している記事はたくさんあります。たとえば、Glenn Fiedler によるIntegration Basicsがあります。検索すると、さらに見つかります。

于 2012-08-15T12:09:17.187 に答える
0

これは、他の作業を行うシステムの時間に基づいて何かを行うための良い方法ではありません。コードは可能な限り多くの CPU 時間を消費しています。コーディングしたものは「ビジー ループ」と呼ばれます。するべき仕事がないとき、あなたは忙しいままであることを意味します。

より良いオプションには、何らかのタイプのタイマー/アラーム (Java タイマー、Android AlarmManager) を使用して、時間になったときに実行する作業をスケジュールするか、Thread.sleep() を呼び出して次の更新間隔まで遅らせることが含まれます。

于 2012-08-15T11:12:33.777 に答える