游戏循环更新太快
Game Loop updates too fast
我想为 Pong 克隆创建一个开始菜单,其中背景中的球从边缘反弹。然而,游戏循环更新得很快,所以球的坐标在您看到它之前就已经在 JFrame 之外,并且它移动得很快。我通过 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;
}
我想为 Pong 克隆创建一个开始菜单,其中背景中的球从边缘反弹。然而,游戏循环更新得很快,所以球的坐标在您看到它之前就已经在 JFrame 之外,并且它移动得很快。我通过 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;
}