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 游戏循环口吃的主要内容,如果未能解决你的问题,请参考以下文章