1

Androidで2Dゲームを描画するためにSurfaceViewを使用するには、メインアクティビティでこれを使用しますonCreate()

setContentView(new GameView(this));

これはこのクラスへの参照です:

public class GameView extends SurfaceView implements SurfaceHolder.Callback

run()さらに、私はその機能を持つスレッドを持っています:

public void run() {
    Canvas c;
    while (_run) {
        c = null;
        try {
            c = _surfaceHolder.lockCanvas(null);
            synchronized (_surfaceHolder) {
                _panel.updatePhysics();
                _panel.onDraw(c);
            }
        }
        finally { // when exception is thrown above we may not leave the surface in an inconsistent state
            if (c != null) {
                _surfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

updatePhysics()私はいくつかの計算をします。もちろん、これらはこの単純な例よりも複雑ですが、同じように機能します。

public void updatePhysics() {
    GraphicObject.Coordinates coord;
    GraphicObject.Speed speed;
    for (GraphicObject graphic : _allElements) {
        coord = graphic.getCoordinates();
        speed = graphic.getSpeed();
        coord.setX(coord.getX() + speed.getX());
        coord.setY(coord.getY() + speed.getY());
        ...
    }
}

そして、onDraw()では、すべてをキャンバスに描画します。

@Override
public void onDraw(Canvas canvas) {
    canvas.drawBitmap(BITMAP, xPos, yPos, null);
    ...
}

これは正常に機能します-すべて。そして、私が自分のデバイスでそれをテストしたとき、それはかなり良さそうに見えました。しかし、私がそれを他の誰かに渡して、彼がテストゲームをしたとき、オブジェクトははるかに速く動いていました!なんでそうなの?スレッドはupdatePhysics()できるだけ頻繁に呼び出すため、これは高速デバイスがこの関数をより頻繁に呼び出すことを意味しますか?

これを防ぎ、すべてのデバイスでゲームを同じように高速にするにはどうすればよいですか?このようなもの?

private long lastRun = System.currentTimeMillis();

public void updatePhysics() {
    long millisPassed = System.currentTimeMillis()-lastRun;
    ...
    float newCoord = (coord.getX() + speed.getX()) * millisPassed / 33;
    coord.setX(newCoord);
    ...
}

ご協力いただきありがとうございます!

4

2 に答える 2

3

可能であれば、時間を直接使用してすべての物理を計算します。通常はこれが最適です。

時間に基づいて計算する方法がない場合、それはステップベースであり、次のステップの生成にそれほど時間がかからないことがわかっている場合は、別のオプションがあります。

2つのスレッドを作成します。最初のものは、固定速度で状態を進めます(そして、これがその速度で遅いデバイスでも機能することを確認する必要があります)。2つ目は現在の状態を取得し、それを見て描画します。これで、2番目のスレッドは、いくつかの状態をスキップするだけなので、必要なだけ遅くすることができます(または、高速の場合は同じ状態を数回描画します)。

以下の小さな例には、ある状態オブジェクトを進め、毎回参照を置き換える1つのスレッドがあるため、消費スレッドは、状態オブジェクトが変更されることを心配する必要はありません。

class GameState {
    private int state = 0;

    public GameState advanceState() {
        GameState result = new GameState();
        result.state = this.state + 1;
        return result;
    }
}

class SurfaceViewImplementation extends SurfaceView {

    // the current state
    volatile GameState mState = new GameState();

    void somewhere() {
        Thread fastProducer = new Thread(new Runnable() {
            private static final long MAX_WAIT = 1000 / 60; 
            @Override
            public void run() {
                while (!Thread.interrupted()) {
                    long timeBefore = SystemClock.currentThreadTimeMillis();
                    GameState newState = mState.advanceState();
                    mState = newState;
                    long timeAfter = SystemClock.currentThreadTimeMillis();
                    long timeSpent = timeAfter - timeBefore;
                    SystemClock.sleep(Math.max(0, MAX_WAIT - timeSpent));
                }
            }
        });
        fastProducer.start();
        Thread slowConsumer = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.interrupted()) {
                    GameState currentState = mState;
                    longRunningDraw(currentState);
                }
            }
        });
        slowConsumer.start();
    }
}

生成スレッドが希望の速度で実行できない場合でも、速度に依存しない結果を得ることができません。

于 2012-04-28T14:17:28.827 に答える
-1

フレームのレンダリングを開始する時間を節約し(あなたの場合はupdatePhysics())、次にこの時点に到達したときに、どれだけの時間が経過するかを知っています。高速であれば、Thread.Sleep(を使用できます。 );

于 2012-04-28T12:19:15.480 に答える