gpt4 book ai didi

java - 如何在空游戏中计算 FPS?

转载 作者:行者123 更新时间:2023-11-29 09:30:51 31 4
gpt4 key购买 nike

为了创建游戏循环,我遵循了 this excellent tutorial .但我认为下一个关于显示 FPS 的教程有点不确定,所以我尝试单人学习。我对我制作的 trackFps() 方法很有信心;它在计算帧之间的时间差后调用,每次运行时测量预测的 FPS,将这些预测的 FPS 值存储在 ArrayList 中,然后在一秒钟过去后将这些预测的 FPS 相加并除以添加值以获得平均 FPS。

当我用调试器运行它时,它运行良好,但当我正常运行它时,我得到以下异常:

FATAL EXCEPTION: Thread-11
java.lang.IndexOutOfBoundsException: Invalid index 26, size is 6
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:257)
at java.util.ArrayList.get(ArrayList.java:311)
at biz.hireholly.engine.GameLoop.trackFps(GameLoop.java:120)
at biz.hireholly.engine.GameLoop.run(GameLoop.java:73)

它非常具有解释性,出现在这一行 fps += fpsStore.get(fpsTrackCount-1);。但我看不出 fpsTrackCount 变量是如何达到 26 的,它应该只与存储在 ArrayList 中的变量数量一样高fpsStore.

有人会查看我的 GameLoop 类,特别是底部的 trackFps() 方法吗?我会提供全部。它有很多评论,但内容并不多,你们中的一些人应该非常熟悉。

GameLoop(底层包含计算FPS的trackFps()方法):

package biz.hireholly.engine;

import android.graphics.Canvas;
import android.view.SurfaceHolder;
import java.util.ArrayList;


/**
* The GameLoop is a thread that will ensure updating and drawing is done at set intervals.
* The thread will sleep when it has updated/rendered quicker than needed to reach the desired fps.
* The loop is designed to skip drawing if the update/draw cycle is taking to long, up to a MAX_FRAME_SKIPS.
* The Canvas object is created and managed to some extent in the game loop,
* this is so that we can prevent multiple objects trying to draw to it simultaneously.
* Note that the gameloop has a reference to the gameview and vice versa.
*/

public class GameLoop extends Thread {

private static final String TAG = GameLoop.class.getSimpleName();
//desired frames per second
private final static int MAX_FPS = 30;
//maximum number of drawn frames to be skipped if drawing took too long last cycle
private final static int MAX_FRAME_SKIPS = 5;
//ideal time taken to update & draw
private final static int CYCLE_PERIOD = 1000 / MAX_FPS;

private SurfaceHolder surfaceHolder;
//the gameview actually handles inputs and draws to the surface
private GameView gameview;

private boolean running;
private long beginTime = 0; // time when cycle began
private long timeDifference = 0; // time it took for the cycle to execute
private int sleepTime = 0; // milliseconds to sleep (<0 if drawing behind schedule)
private int framesSkipped = 0; // number of render frames skipped

private double lastFps = 0; //The last FPS tracked, the number displayed onscreen
private int fpsTrackCount = 1; // number we'll divide the fpsSTore by to get average
private ArrayList<Double> fpsStore = new ArrayList<Double>(); //For the previous fps values
private long lastTimeFpsCalculated = System.currentTimeMillis(); //used in trackFps

public GameLoop(SurfaceHolder holder, GameView gameview) {
super();
this.surfaceHolder = holder;
this.gameview = gameview;
}

public void setRunning(boolean running) {
this.running = running;
}
@Override
public void run(){

Canvas c;

while (running) {
c = null;
//try locking canvas, so only we can edit pixels on surface
try{
c = this.surfaceHolder.lockCanvas();
//sync so nothing else can modify while were using it
synchronized (surfaceHolder){

beginTime = System.currentTimeMillis();
framesSkipped = 0; //reset frame skips

this.gameview.update();
this.gameview.draw(c);

//calculate how long cycle took
timeDifference = System.currentTimeMillis() - beginTime;
//good time to trackFps?
trackFps();

//calculate potential sleep time
sleepTime = (int)(CYCLE_PERIOD - timeDifference);

//sleep for remaining cycle
if (sleepTime >0){
try{
Thread.sleep(sleepTime); //saves battery! :)
} catch (InterruptedException e){}
}
//if sleepTime negative then we're running behind
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS){
//update without rendering to catch up
this.gameview.update();
//skip as many frame renders as needed to get back into
//positive sleepTime and continue as normal
sleepTime += CYCLE_PERIOD;
framesSkipped++;
}

}

} finally{
//finally executes regardless of exception,
//so surface is not left in an inconsistent state
if (c != null){
surfaceHolder.unlockCanvasAndPost(c);
}
}
}

}

/* Calculates the average fps every second */
private void trackFps(){
long currentTime = System.currentTimeMillis();

if(timeDifference != 0){
fpsStore.add((double)(1000 / timeDifference));
}
//If a second has past since last time average was calculated,
// it's time to calculate a new average fps to display
if ((currentTime - 1000) > lastTimeFpsCalculated){
int fps = 0;
int toDivideBy = fpsTrackCount;
while ((fpsStore != null) && (fpsTrackCount > 0 )){
fps += fpsStore.get(fpsTrackCount-1);
fpsTrackCount--;
}
lastFps = fps / toDivideBy;
lastTimeFpsCalculated = System.currentTimeMillis();
fpsTrackCount = 1;
fpsStore.clear();
}
else{
fpsTrackCount++;
}
}
/* So That it can be drawn in the gameview */
public String getFps() {
return String.valueOf(lastFps);
}

}

最佳答案

好的,让我们先来看这个......

java.lang.IndexOutOfBoundsException: Invalid index 26, size is 6
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:257)
at java.util.ArrayList.get(ArrayList.java:311)
at biz.hireholly.engine.GameLoop.trackFps(GameLoop.java:120)

所以在 trackFPS 的某个地方,get() 荒谬地越界了......

private void trackFps()
{
long currentTime = System.currentTimeMillis();

if(timeDifference != 0)
{
fpsStore.add((double)(1000 / timeDifference));
}
//If a second has past since last time average was calculated,
// it's time to calculate a new average fps to display
if ((currentTime - 1000) > lastTimeFpsCalculated)
{
int fps = 0;
int toDivideBy = fpsTrackCount;
while ((fpsStore != null || !fpsStore.isEmpty()) && (fpsTrackCount > 0 ) && (fpsTrackCount < fpsStore.getCount()))
{
//synchronized(this) {
fps += fpsStore.get(fpsTrackCount-1);
fpsStore.remove(fpsTrackCount-1); //otherwise we'll get stuck because of getCount condition
fpsTrackCount--;
//}
}
lastFps = fps / toDivideBy;
lastTimeFpsCalculated = System.currentTimeMillis();
//fpsTrackCount = 1;
//fpsStore.clear();
Log.d("trackFPS()", "fpsTrackCount = "+fpsTrackCount+"\tfpsStore.size() = "+fpsStore.size()+"\t"+fpsStore.toString());
}
else
fpsTrackCount++;
}

试一试。如果它的表现不太好,请尝试取消对同步块(synchronized block)的注释。

至于您的其他问题,关于 TextDrawable,我查看了您的 GameView...

这是我在 SurfaceChanged() 中发现的内容

  fps = new TextDrawable();
fps.setText("HELLO");

现在,为什么不将其移至 SurfaceCreated()?也许您收到了很多 surfaceChanged() 回调,但由于没有 Logcat 调用而没有意识到它? :) 这是我所看到的唯一实例化 TextDrawable 的地方。

关于java - 如何在空游戏中计算 FPS?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11738701/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com