Java Swing 渲染不一致和闪烁

问题描述 投票:0回答:1

我正在尝试熟悉用 java 制作 GUI,并编写了一个程序,该程序应该根据 WASD 键输入在屏幕上移动一个方块(遵循在线教程)。

但是,运行时,似乎方块会闪烁并且“滞后”,有时渲染顺利,但大多数时候渲染不流畅。

这是我的 JPanel 类,这是主要内容发生的地方:

package me.analyzers.scs.game;

import me.analyzers.scs.utilities.KeyPressHandler;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MainPanel extends JPanel implements Runnable{
    final int FPS = 240;
    final int tileSize = 16;
    final int scaling = 3;
    final int realTileSize = tileSize * scaling;
    final int widthX = 16;
    final int widthY = 12;
    final int screenWidth = realTileSize * widthX;
    final int screenHeight = realTileSize * widthY;

    Thread mainLoop;
    KeyPressHandler keyPressHandler = new KeyPressHandler();

    public MainPanel() {
        super();
        this.setPreferredSize(new Dimension(screenWidth, screenHeight));
        this.setBackground(Color.WHITE);
        this.addKeyListener(keyPressHandler);
        this.setFocusable(true);
    }

    public void startGameLoop() {
        mainLoop = new Thread(this);
        mainLoop.start();
    }

    public boolean isRunning() {
        return mainLoop != null;
    }

    int posX = 0;
    int posY = 0;

    @Override
    public void run() {
        long lastTime = 0;
        while(isRunning()) {
            long currentTime = System.currentTimeMillis();
            if(currentTime-lastTime > 1000/FPS) {
                lastTime = System.currentTimeMillis();
                tick();
                repaint();
            }
        }
    }

    public void tick() {
        switch (keyPressHandler.getLastKey()) {
            case 'w' -> posY-=1;
            case 's' -> posY+=1;
            case 'a' -> posX-=1;
            case 'd' -> posX+=1;
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.BLACK);
        g2d.fillRect(posX, posY, realTileSize, realTileSize);
        g2d.dispose();
    }
}


起初我认为这可能是并发问题引起的,所以我多次切换了“主循环”代码;我尝试通过检查 currentTime() 和 lastTime 有条件地执行它,我尝试执行 Thread.sleep,还尝试使用 swing 的 Timer 类。它们都产生相同的结果。

需要注意的一个有趣的事情是,如果我在 PaintComponent() 方法中调用 repaint() (我认为会导致循环),它会正常工作;但这会在每个游戏循环中调用paintComponent()数百次。

在网上搜索类似问题但无法修复后,我尝试在 Windows 虚拟机中运行相同的 jar 存档。令人惊讶的是,它渲染得很顺利!而且,方块在 Windows 机器上的移动速度似乎比我的机器上慢得多。这让我很困惑,因为我认为 JVM 应该在所有平台上保持一致。

编辑:我发现持续输入(这包括垃圾邮件点击窗口)会暂时停止奇怪的口吃。这让我想知道这是否不是由某些进程试图在我的 GUI 上“节省 CPU 时间”引起的?这个存在吗?

java swing concurrency
1个回答
0
投票

你的程序应该像这样开始:

class Program {

   public static void main( String[] args ) {
      EventQueue.invokeLater( () -> { 
         gui = new Gui();
      });
   }
}

class Gui extends JFrame {

   public Gui() {
      .....
      .....
      setVisible( true );
   }

....
....

}

这样,你的整个程序将在事件线程上运行。
另一方面, setVisible( true ); 必须位于构造函数的最后一行(后面可以跟监听器赋值)。
通过进行这些更正,您的问题可能会消失。

© www.soinside.com 2019 - 2024. All rights reserved.