Java JPanel默认双缓冲不';我似乎无法摆脱颠簸的运动

Java JPanel默认双缓冲不';我似乎无法摆脱颠簸的运动,java,swing,game-loop,double-buffering,Java,Swing,Game Loop,Double Buffering,我一直在做一个简单的2D Java游戏来提高我的编码技能。我已经读到,默认情况下,JPanel将是双缓冲的,因此我认为,如果我只是使用JPanel作为“画布”,我不应该有任何起伏运动的问题。不幸的是,这似乎不是100%的时间都有效。通常情况下,玩家的动作非常流畅,但在看似随机的情况下,动作会变得极其起伏。我试着给fps设定上限,但那没什么不同。我自己也尝试过创建缓冲区策略,但也没有解决这个问题。正如一条评论(谢谢!)所建议的,这里是一个简单的版本,它只显示了问题: package main;

我一直在做一个简单的2D Java游戏来提高我的编码技能。我已经读到,默认情况下,JPanel将是双缓冲的,因此我认为,如果我只是使用JPanel作为“画布”,我不应该有任何起伏运动的问题。不幸的是,这似乎不是100%的时间都有效。通常情况下,玩家的动作非常流畅,但在看似随机的情况下,动作会变得极其起伏。我试着给fps设定上限,但那没什么不同。我自己也尝试过创建缓冲区策略,但也没有解决这个问题。正如一条评论(谢谢!)所建议的,这里是一个简单的版本,它只显示了问题:

package main;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main {

    final static private Canvas canvas = new Canvas();
    static private Window gameWindow = new Window();
    final static private GameLoop gameLoop = new GameLoop();
    final static private Player player = new Player();

    //getters/(setters)
    public static Canvas canvas() {
        return canvas;
    }

    public static Window gameWindow() {
        return gameWindow;
    }

    public static Player player() {
        return player;
    }

    public static GameLoop gameLoop() {
        return gameLoop;
    }

    public static void main(String[] args) {

        gameWindow().makeWindow(false);
        gameLoop().startLoop(60.0);
        gameWindow().setVisible(true);
    }
}

class GameLoop {

    private boolean gameIsRunning = true;
    private double tps = 0;

    public void startLoop(double ticksps){
        tps = ticksps;
        new Thread(new Runnable() {
            @Override
            public void run() {
                loop();
            }
        }).start();
    }

    private void loop() {
        //stolen from https://gamedev.stackexchange.com/questions/52841/the-most-efficient-and-accurate-game-loop
        //and edited slightly for fps capping by using sleep
        long lastTime = System.nanoTime();
        double ns = 1000000000 / tps;
        double delta = 0;
        long timer = System.currentTimeMillis();
        int frames = 0;
        long lastTimePainted = 0;
        int ticks = 0;
        long now = 0;
        int timeToNextFrame = 0;
        while(gameIsRunning) {
            now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while(delta >= 1) {
                ticks++;
                updateGame();
                delta--;
            }
            if (System.nanoTime() - lastTimePainted > 3000000) {
                frames++;
                Main.canvas().repaint();
                lastTimePainted = System.nanoTime();
            }
            else {
                timeToNextFrame = (int) (3000000 - (System.nanoTime() - lastTimePainted));
                //System.out.println(timeToNextFrame);
                try {
                    if (timeToNextFrame > 1999998) {
                        Thread.sleep(0, 999999);
                    }
                    else if (timeToNextFrame > 0) {
                        Thread.sleep(0, timeToNextFrame / 2);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if (System.currentTimeMillis() - timer > 1000) {
                System.out.println(frames + " " + ticks);
                ticks = 0;
                frames = 0;
                timer += 1000;
            }
          }
    }

    private void updateGame() {
        Main.player().move();   
    }
}

class Player{

    private int x = 0;
    private int y = 0;
    private int velX = 2;
    private int velY = 2;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void move() {
        if (x + 50 >= Main.gameWindow().getWidth() || x < 0) {
            velX = -velX;
        }
        if (y + 50 >= Main.gameWindow().getHeight() || y < 0) {
            velY = -velY;
        }
        x += velX;
        y += velY;
    }
}

class Window extends JFrame {

    private static final long serialVersionUID = 1L;

    public void makeWindow (boolean fullscreen) {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Game Prototype");
        setLayout(null);
        setResizable(false);
        getContentPane().add(Main.canvas());
        Main.canvas().setLocation(0, 0);
        setFocusable(true);
        setIgnoreRepaint(true);
        setSize(1024, 576);
        Main.canvas().setSize(1024, 576);
        setLocationRelativeTo(null);
        validate();
        repaint();
    }
}

class Canvas extends JPanel {
    private static final long serialVersionUID = 1L;

    Canvas(){
        setLayout(null);
        setBackground(Color.WHITE);
        setDoubleBuffered(true);
        setIgnoreRepaint(true);
        setVisible(true);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.RED);
        g.fillOval(Main.player().getX(), Main.player().getY(), 50, 50);
    }

}
packagemain;
导入java.awt.Color;
导入java.awt.Graphics;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
公共班机{
最终静态私有画布=新画布();
静态私有窗口gameWindow=新窗口();
最终静态私有GameLoop GameLoop=新GameLoop();
最终静态私人玩家=新玩家();
//接受者/(接受者)
公共静态画布(){
返回画布;
}
公共静态窗口gameWindow(){
返回游戏窗口;
}
公共静态播放器(){
返回球员;
}
公共静态GameLoop GameLoop(){
返回游戏循环;
}
公共静态void main(字符串[]args){
gameWindow().makeWindow(false);
gameLoop().startoop(60.0);
gameWindow().setVisible(true);
}
}
类GameLoop{
私有布尔gamesrunning=true;
私人双tps=0;
公共无效STARTOOP(双滴答声){
tps=tps;
新线程(newrunnable()){
@凌驾
公开募捐{
loop();
}
}).start();
}
私有void循环(){
//偷自https://gamedev.stackexchange.com/questions/52841/the-most-efficient-and-accurate-game-loop
//并使用sleep对fps封顶进行了轻微编辑
long lastTime=System.nanoTime();
双ns=100000000/tps;
双增量=0;
长定时器=System.currentTimeMillis();
int帧=0;
长LastTimePaint=0;
int ticks=0;
long now=0;
int timeToNextFrame=0;
同时(游戏运行){
now=System.nanoTime();
增量+=(现在-上次)/ns;
上次=现在;
而(增量>=1){
ticks++;
updateGame();
三角洲--;
}
如果(System.nanoTime()-LastTimePaint>300000){
frames++;
Main.canvas().repaint();
LastTimePaint=System.nanoTime();
}
否则{
timeToNextFrame=(int)(3000000-(System.nanoTime()-lastTimePaint));
//System.out.println(timeToNextFrame);
试一试{
如果(timeToNextFrame>1999998){
睡眠(099999);
}
else if(timeToNextFrame>0){
睡眠(0,timeToNextFrame/2);
}
}捕捉(中断异常e){
e、 printStackTrace();
}
}
if(System.currentTimeMillis()-计时器>1000){
System.out.println(帧+“”+刻度);
滴答声=0;
帧=0;
定时器+=1000;
}
}
}
私有void updateGame(){
Main.player().move();
}
}
职业选手{
私有整数x=0;
私有整数y=0;
私有int velX=2;
私有int=2;
公共int getX(){
返回x;
}
公共int getY(){
返回y;
}
公开作废动议(){
如果(x+50>=Main.gameWindow().getWidth()| | x<0){
velX=-velX;
}
如果(y+50>=Main.gameWindow().getHeight()| | y<0){
velY=-velY;
}
x+=velX;
y+=0;
}
}
类窗口扩展JFrame{
私有静态最终长serialVersionUID=1L;
公共void生成窗口(布尔全屏){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(“游戏原型”);
setLayout(空);
可设置大小(假);
getContentPane().add(Main.canvas());
Main.canvas().setLocation(0,0);
设置聚焦(真);
setIgnoreRepaint(真);
设置大小(1024576);
Main.canvas().setSize(1024576);
setLocationRelativeTo(空);
验证();
重新油漆();
}
}
类Canvas扩展了JPanel{
私有静态最终长serialVersionUID=1L;
画布(){
setLayout(空);
挫折地面(颜色:白色);
setDoubleBuffered(真);
setIgnoreRepaint(真);
setVisible(真);
}
@凌驾
公共组件(图形g){
超级组件(g);
g、 setColor(Color.RED);
g、 fillOval(Main.player().getX(),Main.player().getY(),50,50);
}
}

非常感谢您的帮助。因为我只是从我的代码中复制粘贴了这些内容,所以完整代码中可能会有一些无用的剩余内容。

不要对您未创建的图形对象调用dispose()。特别是,传递给绘画方法的图形对象是由Swing创建的,由Swing拥有,并且很可能用于绘画其他东西。为了更快地获得更好的帮助,请添加or。感谢@Andrew,我按照您所说的做了。我建议的第一件事是用每秒启动的Swing
计时器替换游戏循环。