4

私は月着陸船のデモに基づいてゲームを作成しましたが、大幅に変更されています。40 ~ 50 fps 程度を取得できますが、問題は、40 ~ 50 fps の間で変動しすぎて、動くグラフィックがジッターすることです! それは非常に迷惑で、実際には良いフレームレートで実行されているのに、私のゲームが本当にひどいものに見えます.

スレッドの優先度を高く設定しようとしましたが、それは悪化しました...今では40〜60fpsの間で変動します...

一定になるように FPS を 30 程度に制限することを考えていました。これは良い考えですか、他の誰かが経験や別の解決策を持っていますか?

ありがとう!

これは私の実行ループです

@Override
    public void run() {
        while (mRun) {
            Canvas c = null;
            try {
                c = mSurfaceHolder.lockCanvas(null);
                synchronized (mSurfaceHolder) {
                    if(mMode == STATE_RUNNING){

                        updatePhysics();
                    }
                    doDraw(c);
                }
            } finally {
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
        }

private void updatePhysics() {

        now = android.os.SystemClock.uptimeMillis();

        elapsed = (now - mLastTime) / 1000.0;

        posistionY += elapsed * speed;
        mLastTime = now;
}
4

5 に答える 5

22

ゲームのロジック (オブジェクトの動きなど) の更新レートをフレームレートに基づいて設定しないでください。つまり、描画とロジック更新コードを 2 つの別個のコンポーネント/スレッドに配置します。このようにして、ゲーム ロジックはフレームレートから完全に独立しています。

ロジックの更新は、最後の更新からの経過時間に基づいている必要があります ( と呼びましょうdelta)。したがって、1 ピクセル/ミリ秒で移動するオブジェクトがある場合、各更新中にオブジェクトは次のように動作する必要があります。

public void update(int delta) {
    this.x += this.speed * delta;
}

したがって、FPS が遅れても、オブジェクトの移動速度には影響しません。これは、デルタが大きくなり、補正のためにオブジェクトがより遠くに移動するためです (場合によっては複雑になりますが、それが要点です)。

これは、ロジック更新オブジェクト内でデルタを計算する 1 つの方法です (いくつかのスレッド ループで実行されます)。

private long lastUpdateTime;
private long currentTime;

public void update() {
    currentTime = System.currentTimeMillis();
    int delta = (int) (currentTime - lastUpdateTime);
    lastUpdateTime = currentTime;
    myGameObject.update(delta); // This would call something like the update method above.
}

それが役立つことを願っています! 他にご不明な点がございましたらお問い合わせください。私は自分でAndroidゲームを作っています。:)


サンプルコード:

これら 2 つのスニペット (1 つのアクティビティと 1 つのビュー) をコピーして、コードを実行します。その結果、FPS に関係なく、白い点が画面に滑らかに落ちるはずです。コードはやや複雑で長いように見えますが、実際には非常に単純です。コメントはすべてを説明する必要があります。

この活動クラスはあまり重要ではありません。その中のほとんどのコードは無視できます。

public class TestActivity extends Activity {

    private TestView view;

    public void onCreate(Bundle savedInstanceState) {
        // These lines just add the view we're using.
        super.onCreate(savedInstanceState);
        setContentView(R.layout.randomimage);
        RelativeLayout rl = (RelativeLayout) findViewById(R.id.relative_layout);
        view = new TestView(this);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                10000, 10000);
        rl.addView(view, params);

        // This starts our view's logic thread
        view.startMyLogicThread();
    }

    public void onPause() {
        super.onPause();
        // When our activity pauses, we want our view to stop updating its logic.
        // This prevents your application from running in the background, which eats up the battery.
        view.setActive(false);
    }
}

このクラスはエキサイティングなものがある場所です!

public class TestView extends View {

    // Of course, this stuff should be in its own object, but just for this example..
    private float position; // Where our dot is
    private float velocity; // How fast the dot's moving

    private Paint p; // Used during onDraw()
    private boolean active; // If our logic is still active

    public TestView(Context context) {
        super(context);
        // Set some initial arbitrary values
        position = 10f;
        velocity = .05f;
        p = new Paint();
        p.setColor(Color.WHITE);
        active = true;
    }

    // We draw everything here. This is by default in its own thread (the UI thread).
    // Let's just call this thread THREAD_A.
    public void onDraw(Canvas c) {
        c.drawCircle(150, position, 1, p);
    }

    // This just updates our position based on a delta that's given.
    public void update(int delta) {
        position += delta * velocity;
        postInvalidate(); // Tells our view to redraw itself, since our position changed.
    }

    // The important part!
    // This starts another thread (let's call this THREAD_B). THREAD_B will run completely
    // independent from THREAD_A (above); therefore, FPS changes will not affect how
    // our velocity increases our position.
    public void startMyLogicThread() {
        new Thread() {
            public void run() {
                // Store the current time values.
                long time1 = System.currentTimeMillis();
                long time2;

                // Once active is false, this loop (and thread) terminates.
                while (active) {
                    try {
                        // This is your target delta. 25ms = 40fps
                        Thread.sleep(25);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }

                    time2 = System.currentTimeMillis(); // Get current time
                    int delta = (int) (time2 - time1); // Calculate how long it's been since last update
                    update(delta); // Call update with our delta
                    time1 = time2; // Update our time variables.
                }
            }
        }.start(); // Start THREAD_B
    }

    // Method that's called by the activity
    public void setActive(boolean active) {
        this.active = active;
    }
}
于 2010-07-22T21:11:57.450 に答える
3

上記のコードの一部に実際に何か問題があるのではなく、非効率である可能性があると考えています。私はこのコードについて話している...

   // The important part!
// This starts another thread (let's call this THREAD_B). THREAD_B will run completely
// independent from THREAD_A (above); therefore, FPS changes will not affect how
// our velocity increases our position.
public void startMyLogicThread() {
    new Thread() {
        public void run() {
            // Store the current time values.
            long time1 = System.currentTimeMillis();
            long time2;

            // Once active is false, this loop (and thread) terminates.
            while (active) {
                try {
                    // This is your target delta. 25ms = 40fps
                    Thread.sleep(25);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }

                time2 = System.currentTimeMillis(); // Get current time
                int delta = (int) (time2 - time1); // Calculate how long it's been since last update
                update(delta); // Call update with our delta
                time1 = time2; // Update our time variables.
            }
        }
    }.start(); // Start THREAD_B
}

具体的には、次の行について考えています...

// This is your target delta. 25ms = 40fps
Thread.sleep(25);

スレッドを何もせずにたむろさせるだけでは、貴重な処理時間を無駄にしているように思えます。実際にやりたいことは更新を実行することです。更新中に使用されたものと25ミリ秒(または選択したフレームレートが何であれ)の差のためにスレッドをスリープさせます。このようにして、現在のフレームがレンダリングされている間に更新が行われ、次のフレームの更新で更新された値が使用されるように完了します。

ここで考えられる唯一の問題は、現在のフレーム レンダーが部分的に更新された値を使用しないように、ある種の同期が必要になることです。おそらく、一連の値の新しいインスタンスに更新し、レンダリングの直前に新しいインスタンスを現在のインスタンスにします。

グラフィックブックで、目的のフレームレートを維持しながらできるだけ多くの更新を実行し、それらだけで画面の更新を実行することが目標であると読んだことを覚えていると思います。

もちろん、これには更新を実行するための 1 つのスレッドが必要です。SurfaceView を使用する場合、キャンバスをロックするとレンダリングがこのスレッドによって制御されます (理論的には、とにかく私の理解によると)。

したがって、コードでは、もっと似ています...

// Calculate next render time
nextRender = System.currentTimeInMillis() + 25;

while (System.currentTimeInMillis() < nextRender)
{
    // All objects must be updated here
    update();

    // I could see maintaining a pointer to the next object to be updated,
    // such that you update as many objects as you can before the next render, and 
    // then continue the update from where you left off in the next render...
}

// Perform a render (if using a surface view)
c = lockCanvas() blah, blah...
// Paint and unlock

// If using a standard view
postInvalidate();

幸運を祈ります。これを使っている人からのフィードバックは、きっと私たち全員が何かを学ぶのに役立つでしょう...

ルプバーバティ

于 2011-11-12T03:38:08.077 に答える
0

ゲームのアクションが多い場合は、View の代わりに SurfaceView を使用します。GUI を迅速に更新する必要がない場合は View で問題ありませんが、2D ゲームの場合は常に SurfaceView を使用することをお勧めします。

于 2010-12-05T07:01:19.853 に答える
0

ガベージコレクターの話だと思う

于 2010-10-21T06:07:59.577 に答える