J'ai un jeu C++ fonctionnant via JNI sous Android. La fréquence d'images varie d'environ 20 à 45 images par seconde en raison de la complexité de la scène. Tout ce qui dépasse 30fps est idiot pour le jeu; c'est juste brûler la batterie. J'aimerais limiter la fréquence d'images à 30 images par seconde.
Existe-t-il une méthode "capFramerate ()" masquée dans l'API? Un moyen fiable de le faire?
La solution de Mark est presque bonne, mais pas tout à fait correcte. Le problème est que le swap lui-même prend beaucoup de temps (surtout si le pilote vidéo met en cache des instructions). Par conséquent, vous devez en tenir compte ou vous obtiendrez une cadence d'images inférieure à celle souhaitée. Il convient donc de:
quelque part au début (comme le constructeur):
startTime = System.currentTimeMillis();
puis dans la boucle de rendu:
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);
}
De cette façon, vous prendrez en compte le temps nécessaire pour échanger les tampons et le temps nécessaire pour dessiner le cadre.
Lorsque vous utilisez GLSurfaceView, vous effectuez le dessin dans le onDrawFrame de votre outil de rendu, qui est traité dans un fil séparé par GLSurfaceView. Assurez-vous simplement que chaque appel à onDrawFrame prend (1000/[frames]) millisecondes, dans votre cas environ 33 ms.
Pour ce faire: (dans votre onDrawFrame)
C'est tout.
La réponse de Fili était superbe pour moi. Malheureusement, le nombre d'images par seconde sur mon appareil Android a été limité à 25 images par seconde, même si j'en ai demandé 30. J'ai découvert que Thread.sleep()
ne fonctionne pas assez correctement et qu'il dort plus longtemps qu'il ne le devrait.
J'ai trouvé cette implémentation du projet LWJGL faire le travail: https://github.com/LWJGL/lwjgl/blob/master/src/Java/org/lwjgl/opengl/Sync.Java
Si vous ne voulez pas compter sur Thread.sleep
, utilisez ce qui suit
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;
}
}
La solution de Fili échoue pour certaines personnes, alors je suppose qu'elle dort jusqu'au lendemain du prochain vsync au lieu du précédent. Je pense aussi que déplacer le sommeil à la fin de la fonction donnerait de meilleurs résultats, car cela permettrait de masquer le cadre actuel avant le prochain vsync, au lieu d'essayer de compenser le précédent. Thread.sleep () est inexact, mais heureusement nous n’avons besoin que de précision pour la période vsync la plus proche de 1/60. Le code LWJGL tyrondis a publié un lien vers semble trop compliqué pour cette situation, il est probablement conçu pour quand vsync est désactivé ou non disponible, ce qui ne devrait pas être le cas dans le contexte de cette question.
Je voudrais essayer quelque chose comme ça:
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;
}