AndroidでJNIを介して実行しているC++ゲームがあります。シーンの複雑さにより、フレームレートは約20〜45fpsの範囲で変化します。 30fpsを超えるものは、ゲームにとってばかげています。バッテリーが燃えているだけです。フレームレートを30 fpsに制限したい。
APIに隠れている「capFramerate()」メソッドはありますか?これを行うための信頼できる方法はありますか?
Markの解決策はほとんど問題ありませんが、完全に正しいわけではありません。問題は、スワップ自体にかなりの時間がかかることです(特に、ビデオドライバーが命令をキャッシュしている場合)。したがって、それを考慮に入れる必要があります。そうしないと、必要なフレームレートよりも低いフレームレートで終了します。したがって、次のようになります。
開始時のどこか(コンストラクターなど):
startTime = System.currentTimeMillis();
次に、レンダリングループで:
public void onDrawFrame(GL10 gl)
{
endTime = System.currentTimeMillis();
dt = endTime - startTime;
if (dt < 33)
Thread.Sleep(33 - dt);
startTime = System.currentTimeMillis();
UpdateGame(dt);
RenderGame(gl);
}
このようにして、バッファを交換するのにかかる時間とフレームを描画するのにかかる時間を考慮に入れます。
GLSurfaceViewを使用する場合、GLSurfaceViewによって別のスレッドで処理されるレンダラーのonDrawFrameで描画を実行します。単純に、onDrawFrameの各呼び出しに(1000/[frames])ミリ秒かかることを確認してください。
これを行うには:(onDrawFrame内で)
それでおしまい。
フィリの答え 見栄えが良く、残念ながらFPSは私のデバイスのAndroid=デバイスを25 FPSに制限しましたが、30をリクエストしましたが、Thread.sleep()
は十分に正確に機能せず、必要以上に長くスリープします。
LWJGLプロジェクトからこの実装が仕事をするのを見つけました: https://github.com/LWJGL/lwjgl/blob/master/src/Java/org/lwjgl/opengl/Sync.Java
Thread.sleep
に依存したくない場合は、以下を使用してください
double frameStartTime = (double) System.nanoTime()/1000000;
// start time in milliseconds
// using System.currentTimeMillis() is a bad idea
// call this when you first start to draw
int frameRate = 30;
double frameInterval = (double) 1000/frame_rate;
// 1s is 1000ms, ms is millisecond
// 30 frame per seconds means one frame is 1s/30 = 1000ms/30
public void onDrawFrame(GL10 gl)
{
double endTime = (double) System.nanoTime()/1000000;
double elapsedTime = endTime - frameStartTime;
if (elapsed >= frameInterval)
{
// call GLES20.glClear(...) here
UpdateGame(elapsedTime);
RenderGame(gl);
frameStartTime += frameInterval;
}
}
Filiのソリューションは一部の人にとって失敗しているので、次のvsyncの直前ではなく、直後までスリープしていると思われます。また、スリープを関数の最後に移動すると、前のフレームを補正しようとする代わりに、次のvsyncの前に現在のフレームを埋め込むことができるため、より良い結果が得られると思います。 Thread.sleep()は不正確ですが、幸いにも、最も近いvsync期間の1/60秒まで正確であることが必要です。 LWJGLコードtyrondisがこの状況に対して過度に複雑に見えるようにリンクを投稿しました。おそらく、vsyncが無効または利用できない場合のために設計されていますが、この質問の場合はそうではありません。
私はこのようなことを試みます:
private long lastTick = System.currentTimeMillis();
public void onDrawFrame(GL10 gl)
{
UpdateGame(dt);
RenderGame(gl);
// Subtract 10 from the desired period of 33ms to make generous
// allowance for overhead and inaccuracy; vsync will take up the slack
long nextTick = lastTick + 23;
long now;
while ((now = System.currentTimeMillis()) < nextTick)
Thread.sleep(nextTick - now);
lastTick = now;
}