java - BufferStrategy 不能解决闪烁问题

标签 java paint game-engine

我试图了解 BufferStrategy 是如何工作的。我制作了一个简单的应用程序,每 60 帧每秒一次又一次地绘制一些 Sprite 对象。我可以看到 Canvas 上的图像,但由于某种原因它们在闪烁。你能告诉我为什么吗?如果您不想阅读所有代码,只需关注 Paint 方法和主游戏循环即可。

public abstract class Frame extends JFrame implements KeyListener {

    private static final long serialVersionUID = 1L;

    //------------------------Variables------------------------//
    private boolean initialized = false;
    private boolean fullScreen = false;

    public boolean running = true;
    private GraphicsDevice vc;
    private BufferStrategy strategy;
    private Graphics2D g2d;
    private int timer = 0;
    //------------------------Variables------------------------//

    public final void __init__() {
        this.addKeyListener(this); //Adding key listener.
        this.setVisible(true);
        this.setIgnoreRepaint(true);
        this.createBufferStrategy(2);
        this.strategy = this.getBufferStrategy();
        this.setResizable(false);
        this.initialized = true;   //Initialized.
    }

    //Create a window.
    public final void set_mode(int width, int height, boolean fullScreen) throws NotInitializedException {
        //Frame not initialized.
        if (!this.initialized) {
            throw new NotInitializedException("Frame not initialized!");
        } else {
            //--------------------Variables--------------------//
            GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
                        //--------------------Variables--------------------//

            //Setting vc equal to the default graphics device of the system.
            this.vc = env.getDefaultScreenDevice();

            //Full Screen.
            if (fullScreen) {
                this.fullScreen = fullScreen;

                //Creating the display mode.
                DisplayMode mode = new DisplayMode(width, height, 32, DisplayMode.REFRESH_RATE_UNKNOWN);

                //If display settings are allow to change display mode.
                if (this.vc.isDisplayChangeSupported()) {
                    this.vc.setDisplayMode(mode); //Change to the new mode.
                }

                //Set the screen to full screen.
                this.vc.setFullScreenWindow(this);
            } //Not full screen.
            else {
                this.setSize(width, height);
                this.addWindowListener(new WindowHandler(this));
            }
        }

    }

    //This mehod is been called from Sprite.draw() method.
    public void paint(Sprite sprite) {

        do {

            do {
                this.g2d = (Graphics2D) this.strategy.getDrawGraphics();
                g2d.drawImage(sprite.getImage(), sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight(), null);
                this.g2d.dispose();

            } while (this.strategy.contentsRestored());

            this.strategy.show();

        } while (this.strategy.contentsLost());
    }

    public final int tick(int fps) {
        int ms = 1000 / fps;
        timer += 1;

        //Try to sleep.
        try {
            Thread.sleep(ms);
        } //Thread interrupted.
        catch (Exception e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }

        return timer;
    }

    public class MyApp extends Frame {

        public static String BG_PATH = "C:/Users/admin/Desktop/game/bg.jpg";
        public static String PL_PATH = "C:/Users/admin/Desktop/game/player.png";
        public static String EN_PATH = "C:/Users/admin/Desktop/game/enemy.png";

        private int speed = 20;

        private boolean left = false;
        private boolean right = false;

        @Override
        public void keyPressed(KeyEvent arg0) {
            // TODO Auto-generated method stub

            if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
                this.left = true;
            } else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
                this.right = true;
            } else if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {
                this.close();
            }

        }

        @Override
        public void keyReleased(KeyEvent arg0) {
            // TODO Auto-generated method stub

            if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
                this.left = false;
            } else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
                this.right = false;
            }

        }

        @Override
        public void keyTyped(KeyEvent arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onWindowClose() {
            // TODO Auto-generated method stub

        }

        //This method starts the game.
        public void startApp() {
            this.__init__(); //initialize the frame.

            Sprite bg = new Sprite(this, Picture.load(BG_PATH), "bg"); //Create a new sprite obj

            this.set_mode(bg.getWidth() - 500, bg.getHeight() - 100, false); //Create the window.

            Sprite player = new Sprite(this, Picture.load(PL_PATH), "player");

            player.setX(bg.getWidth() / 3);
            player.setY(bg.getHeight() / 2);

            //Game Main Loop
            while (this.running) {

                bg.draw();
                player.draw();

                player.moveHorizontal(left, right, speed); //Dont worry about this line.
                this.tick(50);
            }
        }
    }
}

最佳答案

我可以清楚地发现您的一些问题。

首先,您必须明白,在 Swing/Awt 中绘图并不以其速度而闻名,实际上它以完全相反的速度而闻名。事实上,即使您告诉游戏以 60 fps 运行,它也可能无法做到。因此闪烁。本质上,您的应用程序陷入了“绘图数据竞赛”,并且总是稍微落后。快速尝试一些事情;将 Thead.Sleep() 设置为 10 或 30。我觉得这可能会完全解决您的问题。

如果没有,请考虑第二个问题。您在player.Draw();内部调用this.strategy.show();函数,当它需要是您在绘制调用中执行的最后一件事时。换句话说:

                //Game Main Loop
                while (this.running) {

                    bg.draw(); // DON'T SWAP BUFFERS!
                    player.draw(); // DON'T SWAP BUFFERS!
                    // draw other entities
                    player.moveHorizontal(left, right, speed); //Dont worry about this line.
                    this.tick(50);
                    this.strategy.show(); // This needs to be the very last thing you do. You're swapping buffers here, which only needs to be done once per frame.
                }

我的猜测是您还在 bg.Draw(); 期间交换了缓冲区。功能也是如此,这实际上就是你的屏幕闪烁的原因。所以这是两件事。尝试将每秒帧数降低到 Java 可以实际处理的水平,并且在绘图例程结束之前不要交换缓冲区。

其他一些建议:

使用直接变量访问而不是 getter 和 setter。调用“player.getX()”时会产生开销。当你可以调用“player.x”时。

Java 2D 游戏开发没有 future 。 Swing/AWT(你正在使用的)已经完全死了。如果您想制作游戏,并且认真对待它,请花时间学习 OpenGL(在您的情况下,它将是 Lwjgl 作为包装器)。

关于java - BufferStrategy 不能解决闪烁问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34357618/

相关文章:

java - 具有异常 unicode 字符和单词边界的正则表达式模式

java - jpa hibernate映射joincolumn对象中的最新对象

Java JFrame 绘制

不同 API 上的 Android 绘画问题

swing - 在 Swing 中强制立即布局和绘制

javascript - HTML5 游戏引擎 - JavaScript 动画

macos - 是否有支持 PC 和 Mac 的图形/游戏引擎?

java - 我可以像在 Visual Studio 上一样在 Eclipse 上创建项目模板吗

c++ - OGRE C++ 游戏开发

Java - Collections.sort() 问题