游戏循环更新太快

Game Loop updates too fast

我想为 Pong 克隆创建一个开始菜单,其中背景中的球从边缘反弹。然而,游戏循环更新得很快,所以球的坐标在您看到它之前就已经在 J​​Frame 之外,并且它移动得很快。我通过 sysouts 发现了这一点。

我想这与线程有关,但我不确定。 主要 class 将此 Class 称为线程,但重要的部分在 class BackgroundBallMovement

package main;

public class BackgroundBallMovement implements Runnable{

    private boolean running = true;


    @Override
    public void run() {

        long lastTime = System.nanoTime();
        final double ns = 1000000000.0 / 60;        
        double delta = 0;

        while(running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            while(delta >= 1) {
                update();
                delta = 0;
                System.out.println("X-Koordinate " + Var.ballX + " " + "Y-Koordinate " + Var.ballY);
            }
            render();
        }
    }

        //Spiellogik updaten
        private synchronized void update() {
            Var.ballX += 1;
            Var.ballY += 1;

        }

        //Objekte zeichnen
        private synchronized void render() {
             Var.drawStartMenue.repaint();
        }

}

那是因为你的数学很烂 ;-)

是的,(now - lastTime) / ns 是自引擎启动后应该渲染的帧数。因此,您的代码简化为:

while (true) {
    delta = framesSinceStart();
    while (delta >= 1) {
        renderFrame();
        delta = 0;        
    }
}

相当于

while (true) {
    if (framesSinceStart() >= 1) {
        renderFrame();
    }
}

也就是说,你的代码正确地等待渲染第一帧,但没有记录它已经渲染了那一帧,因此总是认为它已经晚了,再也不会等待了。

相反,您可以尝试类似的方法:

while (true) {
    if (framesSinceStart() - framesRendered >= 1) {
        renderFrame();
        framesRendered++;
    }
}

顺便说一句,简单地停留在无限循环中以消磨时间并不是很省电。最好使用 Thread.sleep() 之类的东西来等待 - 但是手动计算它应该等待的时间有点毛茸茸。幸运的是, Java API 自带好帮手 类 可以使用:

ThreadPoolExecutor executor = Executors.newSingleThreadExecutor();
executor.scheduleAtFixedRate(() -> renderFrame(), 0, 1000 / 60, TimeUnit.ms);

您没有使用 Thread.sleep()。相反,您要等到 System.nanoTime() 更改。这意味着 CPU 一直是 运行(不好)。

还有这个循环:

while(delta >= 1) {
    ...
    delta = 0;
    ...
}

没有意义,因为它可以被 if 替换。

那么您永远不会更新 lastTime 变量。所以这一行:

delta += (now - lastTime) / ns;

会产生一个二次函数,因为它会产生这样的结果(每次循环执行):

delta += 0.1;
delta += 0.2;
delta += 0.3;
...

那么因为你永远不会在 1 秒后更新 lastTime 变量,所以条件

while(delta >= 1)

总会遇到,你的球会移动得非常快。

您的第一种方法可能是这样的:

@Override
public void run()
{
    while(running)
    {
        update();
        render()
        System.out.println("X-Koordinate " + Var.ballX + " " + "Y-Koordinate " + Var.ballY);
        Thread.sleep(1000L/60L);
    }
}

尝试Thread.sleep(long millis)

while(running) {
        long now = System.nanoTime();
        delta += (now - lastTime) / ns;
        while(delta >= 1) {
            update();
            delta = 0;

            try{
                Thread.sleep(1000);
            } catch(Exception e) { }

            System.out.println("X-Koordinate " + Var.ballX + " " + "Y-Koordinate " + Var.ballY);
        }
        render();
    }

您可以尝试的另一件事是降低球速。

private synchronized void update() {
        Var.ballX += 0.01;
        Var.ballY += 0.01;

    }