Java 游戏循环口吃

Posted

技术标签:

【中文标题】Java 游戏循环口吃【英文标题】:Java game loop stutters 【发布时间】:2021-05-18 22:02:00 【问题描述】:

作为练习,我尝试制作一个简单的程序,您所要做的就是控制屏幕周围的方块。我试图用插值制作一个游戏循环,但是我遇到了问题,它经常结结巴巴,我不知道为什么。我试过删除 Thread.sleep(1);看看这是否是问题所在。如果我删除它,它实际上会减少口吃,并且 fps 将从 300 fps 左右跳到 8000 fps 左右。但是当然,如​​果我让这个游戏变得更复杂,我将无法获得 8000 fps,所以我想看看如果我只有 300 fps 左右,卡顿是否仍然存在,所以我所做的就是在屏幕上添加一堆方块.这将 fps 降至 300 左右,我注意到口吃又回来了。我查看了Game loop 和Fix your timestep,但我找不到解决方案。我做错了什么?

游戏循环:

    int targetUps = 60;
    int timePerUpdate = 1_000_000_000 / targetUps;
    long oldTime = System.nanoTime();
    long newTime;
    int accumulator = 0;
    int upsTimer = 0;
    int upsCounter = 0;
    int fpsCounter = 0;

    while(true) 
        newTime = System.nanoTime();
        accumulator += newTime - oldTime;
        upsTimer += newTime - oldTime;
        oldTime = newTime;

        while(accumulator >= timePerUpdate) 
            update();
            accumulator -= timePerUpdate;
            upsCounter++;
        

        render(Math.min(1.0f, (double) accumulator / timePerUpdate));
        fpsCounter++;

        if(upsTimer >= 1_000_000_000) 
            System.out.println("UPS: " + upsCounter + " | " + "FPS: " + fpsCounter);
            fpsCounter = 0;
            upsCounter = 0;
            upsTimer = 0;
        
        try 
            Thread.sleep(1);
         catch (InterruptedException e) 
            e.printStackTrace();
        
    

渲染方法:

private void render(double delta) 
    BufferStrategy bs = window.getBufferStrategy();
    Graphics g = bs.getDrawGraphics();
    g.setColor(Color.DARK_GRAY);
    g.fillRect(0,0,Window.WIDTH, Window.HEIGHT);
    player.render(g, delta);
    bs.show();
    g.dispose();

玩家的渲染方法:

 public void render(Graphics g, double delta) 
    g.setColor(Color.GREEN);
    double renderX = oldX + (x - oldX) * delta;
    double renderY = oldY + (y - oldY) * delta;
    g.fillRect((int) renderX, (int) renderY, 32, 32);

【问题讨论】:

Thread.sleep(1) 将无条件休眠一秒钟。您希望睡眠时间取决于已经花费了多少时间。因此,例如,30 FPS,您希望每帧持续约 33 纳秒。如果实际计算需要 16 纳秒,那么您需要休眠 17 纳秒来弥补差异。这称为增量计时,您的代码应该为您执行这些计算。 @SilvioMayolo 对,但如果您使用插值,那将没有意义,因为您想尽可能快地渲染,对吗? 如果你想“尽可能快地”渲染,那么就不要使用sleep。根据操作系统的优先级,您将不一致地获得 30 到 238742340174 FPS,并且您将缩短处理器的保质期。你绝对想要尽可能快地渲染。您希望以 30 或 60 FPS 为目标(研究表明,人眼的平均处理速度不能超过 60 FPS),并将剩余的处理时间留给操作系统以供其他软件和任务运行。 @SilvioMayolo 是的,我已经删除了 Thread.sleep();但这仍然不能解决口吃问题。 Thread.sleep(1) 表示睡眠 1 毫秒,而不是一整秒,@SilvioMayolo 【参考方案1】:

你需要像这样使用delta:

state = currentState * delta + previousState * ( 1.0 - delta );

当累加器等于 timePerUpdate (60 FPS) 时,delta 将为 0。

由于您的代码从不使用 (1.0 - delta),

 double renderX = oldX + (x - oldX) * delta;
 double renderY = oldY + (y - oldY) * delta;

意味着您忽略了更新,因此口吃。

【讨论】:

你说得对。我试过这个,但现在有时广场在静止时会抖动。我认为这是因为当正方形以 x 100.0 为例时,有时 x 被计算为 99.9,所以它被渲染为 x 99。我尝试通过使用 Math.round() 方法对 renderX 和 renderY 进行舍入来解决这个问题,虽然它确实修复了静止时的抖动,但它并没有修复口吃。

以上是关于Java 游戏循环口吃的主要内容,如果未能解决你的问题,请参考以下文章

来自 lockCanvas 方法的游戏循环中的偶尔卡顿/滞后

Java2D 游戏随机卡顿。我究竟做错了啥?

Java KeyListener 口吃

试图理解 Java 中的高级游戏循环

当我添加游戏循环时,为啥我的 Java 游戏会崩溃?

重置游戏循环/重新启动游戏 java swing