首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基本游戏引擎:计时器,FPS和逻辑循环。

基本游戏引擎:计时器,FPS和逻辑循环。
EN

Code Review用户
提问于 2016-03-11 22:48:32
回答 1查看 3.8K关注 0票数 5

我正在创建一个简单的Java游戏引擎(只是为了好玩)。这个想法是为开发任何类型的2D游戏创建一个基本的框架,一个抽象的引擎来完成艰苦的工作。你想回顾一下它的核心,并给我一些提示吗?

GameTimer类

代码语言:javascript
复制
public class GameTimer {

    public static final double ONE_SECOND = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);

    private long start;
    private long[] ticks = new long[1000];
    private int first, last;
    private int fps;

    public GameTimer(){}

    public void start(){
        this.start = System.nanoTime();
    }

    public void tick(){
        ticks[last] = System.nanoTime();
        while (ticks[last] - ticks[first] > ONE_SECOND){
            first = circular(first + 1);
            fps--;
        }
        last = circular(last + 1);
        fps++;
    }

    public long tock(){
        return System.nanoTime() - ticks[circular(last-1)];
    }

    public long getElapsedNanoTime(){
        long prev = ticks[circular(last-2)];
        return (prev == 0) ? 0 : ticks[circular(last-1)] - prev;
    }

    public double getElapsedTime(){
        return this.getElapsedNanoTime() / ONE_SECOND;
    }

    public long getNanoTime(){
        return System.nanoTime() - this.start;
    }

    public double getTime(){
        return this.getNanoTime() / ONE_SECOND;
    }

    public int getFPS(){
        return this.fps;
    }

    private int circular(int n){
        return Math.floorMod(n, ticks.length);
    }
}

对策环

代码语言:javascript
复制
double desiredFPS = 60d;
long sleep = (long) (GameTimer.ONE_SECOND / desiredFPS);
GameTimer timer = new GameTimer();
timer.start();

try {
    while(true){

        // tick and print fps
        timer.tick();
        System.out.println(timer.getFPS());

        // input, update and draw (abstract methods of my engine class)
        input();
        update();
        draw();

        // sleep
        long wake = System.nanoTime() + sleep - timer.tock();
        do {Thread.sleep(0);}
        while (System.nanoTime() < wake);
    }
} catch (InterruptedException e) {
    // oh someone paused the game
}

备注:

  1. 为了获得更高的精度,我使用的是纳秒和一个繁忙的等待循环,而不是仅仅调用Thread.sleep()方法(它非常不准确)。我的测试表明,发动机可以保持理想的频率相当精确。
  2. 我使用循环缓冲区来计算每秒的帧数,我不喜欢那个循环来计数延迟的滴答数,但是我找不到更好的方法来做它。
  3. start变量和GameTimer中相关的get方法--它只是用于游戏逻辑,对于fps计数或滴答操作来说并不重要。
EN

回答 1

Code Review用户

发布于 2016-11-24 11:51:21

你的主回路不需要整个滴答机构。

您已经设置了这个精心制作的装置(我花了一段时间才理解),它度量了您最后一次“框架”所用的时间。然后,在决定需要等待多长时间才能到达下一个帧时,检索该时间,并从当前时间减去该时间加上您想要等待的时间。

所有这些都是一个非常复杂的机制:

代码语言:javascript
复制
long sleep = (long) (GameTimer.ONE_SECOND / desiredFPS);
try {
    while(true){

        // tick and print fps
        long lastTick = System.nanoTime();
        timer.tick();
        System.out.println(timer.getFPS());

        // input, update and draw (abstract methods of my engine class)
        input();
        update();
        draw();

        // sleep
        long wake = lastTick + sleep;
        do {Thread.sleep(0);}
        while (System.nanoTime() < wake);
    }
} catch (InterruptedException e) {
    // oh someone paused the game
}

这种重写并没有真正的帮助,所以我将解释:

你所做的是跟踪滴答声的东西。那好吧。但是,在计算中使用滴答计时器是没有意义的--你已经有了启动滴答的时间--这是你进入循环的时候!

您的GameTimer对于以图形形式显示调试信息或在滞后点中查找模式等操作非常有用。但是对于这个用法来说,只要一个长就行了。

firstlast的命名也是错误的。它不是指向循环缓冲区中的第一个和最后一个项的指针,而是指向过去秒的第一个和最后一个帧的指针。这应该在变量名中进行编码。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/122611

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档