【问题标题】:Java 2D game "powered" by repaint(); lags in a strange way由 repaint() “驱动”的 Java 2D 游戏;以一种奇怪的方式滞后
【发布时间】:2015-12-11 08:39:34
【问题描述】:

我最近几天用 Java 编写了一个小型 2D 游戏。我只想使用普通的 Java SDK。到目前为止,一切都很好。我现在完成了,只有一个小问题: 我使用 Thread.sleep 方法来创建类似 gameloop/tick 的东西。由于我对面向对象编程不太熟悉,所以我不太确定我是否使用了正确的表达式,所以请忽略错误的表达式。

奇怪的是,如果我将等待时间(我称之为“Takt”)切换到 100 或以上,一切正常。我想把它改成50,但是一旦低于100,游戏就开始极度滞后(这个简单的乒乓球游戏所涉及的弹跳红球开始剧烈闪烁。

这是我的 Display 类的一段代码,它包括 paint();方法。如果您需要其他代码部分,请告诉我!请原谅我的变量名,我来自德国。代码如下:

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

@SuppressWarnings("serial")

public class Output extends JFrame implements Runnable{

public static int W = 400;
public static int H = 700;

public static int Playerx;
public static int Playery = H-30;
public static int Punkte;

public static int Trash;

public static long Takt = 100;

public static boolean DrawGameOver;
public static boolean finished;

public static void main(String[] args) throws InterruptedException {
    JOptionPane.showMessageDialog(null, "Das Spiel startet, sobald Sie auf OK drücken. Das Ziel ist es, möglichst viele Punkte zu erzielen.", "Ping-Pong-Solo", JOptionPane.NO_OPTION);
    JOptionPane.showMessageDialog(null, "Mindstanforderungen: 1. Dual-Core Prozessor 2. Bildschirmauflösung mit mindestens 400*700p", "Systemanforderungen", JOptionPane.NO_OPTION);

    Output Fenster = new Output();
    Fenster.setSize(W, H);
    Fenster.setResizable(false);
    Fenster.setLocationRelativeTo(null);
    Fenster.setVisible(true);
    Fenster.setTitle("Ping Pong Release 1.0");
    Fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Fenster.addKeyListener(new MyKeyListener());
    new Physics();
    new Thread(new Output()).start();
    while(true){
        if(DrawGameOver == false && finished){
            Fenster.repaint();
        }else{
            GameOver();
        }
        try {
            Thread.sleep(Takt);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(!DrawGameOver){
            Physics.Bewegung();
        }
    }
}       

public static void GameOver(){
    JOptionPane.showMessageDialog(null, "Sie erreichten "+Punkte+" Punkte. ", "Spiel beendet.", JOptionPane.NO_OPTION);
    JOptionPane.showMessageDialog(null, "(c) Joel Krimmel", "Der Boss", JOptionPane.NO_OPTION);
    System.exit(0);
}
    public void paint(Graphics g){
        finished = false;

        g.setColor(Color.BLACK);
        g.fillRect(0, 0, W, H);
        g.setColor(Color.BLUE);
        g.fillRect(Playerx, Playery, 100, 20);
        g.setColor(Color.RED);
        g.fillRoundRect(Physics.x, Physics.y, Physics.W, Physics.H, 500, 500);
        g.setColor(Color.YELLOW);
        g.drawString("Punkte: "+Punkte, W-100, 50);

        finished = true;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){
            MyKeyListener.Verarbeitung();
        }
    }
} 

【问题讨论】:

  • 这样的问题很难回答。堆栈溢出规则规定您必须至少对问题有一个最低限度的了解。如果你能找到它滞后的原因,我们可以回答这个问题。
  • 代码是脱节的,断章取义,你的绘画方法可能做错了什么可怕的错误,很难知道。考虑提供可运行的示例来演示您的问题。您也可以考虑使用 Swing Timer 而不是“while-lop”,但在我们看到更多信息之前,这只是一个猜测
  • 为了证明这一点,您可以查看this example,这是一个简单的弹力球示例,this examplethis example 是关于渲染大量对象(如果不是 1000 则为 100 )...
  • 另一方面,您将希望避免使用KeyListener 来支持键绑定API,这将解决与KeyListener 相关的焦点问题。更多详情请见How to Use Key Bindings

标签: java swing 2d lag


【解决方案1】:

我注意到您正在使用 JFrame,这意味着每次重绘时,您都在重绘整个窗口。解决方案是使用速度更快的 JPanel,并将其放入 JFrame 中,以免闪烁。 另外,thread.sleep 本质上会冻结你的整个程序,所以请使用摇摆计时器。

【讨论】:

    【解决方案2】:

    因此,您的游戏闪烁可能有多种原因。我认为最有可能的是您没有使用缓冲。这意味着您在显示屏幕时正在绘制屏幕。所以有时会显示像半画半画这样的奇怪的东西。相反,您可以先将内容写入图像,然后一次绘制整个图像。
    在代码中它看起来像这样:

    public void paint(Graphics g){
        finished = false;
        // the buffer image
        BufferedImage bufferImage = new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_INT_RGB);
        // I used Graphics2D instead of Graphics here, because its more flexible and lets you do more things.
        Graphics2D g2 = (Graphics2D)bufferImage.getGraphics();
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, W, H);
        // ...
        g2.drawString("Punkte: "+Punkte, W-100, 50);
        // now draw the buffer image on the screen.
        g.drawImage(bufferImage, 0, 0, null);
        finished = true;
    }
    

    (该代码并不理想,但它是一种快速的方法。如果您想知道如何做,最好查找“双缓冲”。)

    另一个(不太可能)的原因可能是每个渲染过程的渲染时间不同。您可以通过使用System.currentTimeMillis() 测量渲染所需的时间并调整睡眠时间来解决此问题。

    另外:不要用德语命名变量。我也是德国人(你好不来梅),我也不这样做。不好。 :-)
    但更重要的是不要以大写字母开头变量名,否则它们很容易与类混淆。
    你应该看看java naming conventions

    【讨论】:

    • 或者,如果 OP 使用基于 JComponent 的类,只需利用已经提供的双缓冲支持...但是没有上下文可以做出这样的决定...
    • 嗯,这对我来说很难理解......为什么这会在屏幕上从 0,0 到最后一个点绘制一个黑色矩形?使用它只会给我一个黑屏 O.o
    猜你喜欢
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 1970-01-01
    • 2017-01-17
    • 2017-12-04
    相关资源
    最近更新 更多