使一个Thread等待Graphics2D方法完成

时间:2014-11-14 20:09:59

标签: java swing graphics2d thread-synchronization

我尝试在java中为2D游戏编写一个简单的结构,使用主动渲染和fps独立游戏更新,我遇到了run()方法的问题。

正如您在下面的代码中看到的,我创建了一个BufferedImage frameBuff,在其上绘制一堆矩形并将其渲染到嵌套Window类中的JPanel面板。 我的问题是,当我设置我的fps上限非常低时,我可以看到第一个BufferedImage frameBuff并没有真正被绘制到面板上,尽管调用了renderImage()方法。 这似乎是因为在我将frameBuff渲染到面板之前,Graphics2D对象没有完成绘图。

在渲染frameBuff之前,我确实可以在绘制后暂停一段时间,但是当场景变得更复杂时,完成绘制到frameBuff所需的时间可能会有所不同。 那么有没有办法在g2d完成绘制之前等待/准确睡眠?

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class Game implements Runnable
{
    private Window gui; 

    private int frameCap;
    private double delta, fixedSpeed, frameTimeCap;
    private long lastFrame, currentFrame;

    private BufferedImage frameBuff;
    private Thread mainThread;
    private boolean running;
    private static final long oneSecondInNano = 1000000000;
    private static final long oneMilliInNano = 1000000;

    public static void main(String[] args)
    {
        new Game(60, 0);
    }

    /**
     * Sets up the Game, setting its fixed speed (time relative) and the
     * framecap.
     * 
     * @param fixedSpeed
     *            - the speed at which the game should run in units/second
     *            <p>
     *            <strong>Example:</strong> If a rectangle should move 60
     *            pixels/second, the speed would be set to 60 and the rectangle
     *            would be moved by 1*delta every frame)
     * @param frameCap
     *            - the amount of frames/second the game must not exceed
     */
    public Game(double fixedSpeed, int frameCap)
    {
        this.frameCap = frameCap;

        if(this.frameCap == 0)
            this.frameTimeCap = 0;
        else
            this.frameTimeCap = oneSecondInNano/frameCap;

        this.fixedSpeed = oneSecondInNano/fixedSpeed;

        gui = new Window();
        running = true;

        lastFrame = System.nanoTime();

        mainThread = new Thread(this);
        mainThread.start();
    }

    /**
     * Mainloop
     */
    @Override
    public void run()
    {
        double x = 0;

        while (running)
        {
            //Calculating delta
            currentFrame = System.nanoTime();
            long diff = currentFrame - lastFrame;
            System.out.println("last Frametime:\t" + diff + "ns");
            delta = diff / fixedSpeed;



            //Update Game
            if(frameBuff == null)
                frameBuff = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = (Graphics2D)frameBuff.getGraphics();
            g2d.setColor(Color.red);
            g2d.fillRect(0, 0, frameBuff.getWidth(), frameBuff.getHeight());
            g2d.setColor(Color.black);

            System.out.println("current Delta:\t" + delta);
            x+= 3*delta;

            g2d.fillRect((int)x%640, (int)x%480, 20, 20);
            if(x%640 > 620 && x%480 > 460)
                g2d.fillRect(0, 0, (int)x%640 - 620, (int)x%480 - 460);
            else if(x%640 > 620)
                g2d.fillRect(0, (int)x%480, (int)x%640 - 620, 20);
            else if(x%480 > 460)
                g2d.fillRect((int)x%640, 0, 20, (int)x%480 - 460);

            g2d.dispose();

            //Render image
            gui.renderImage(frameBuff);



            //Wait a bit
            Thread.yield();

            //Stay at/under the frameCap
            diff = System.nanoTime() - currentFrame;
            if(diff < frameTimeCap)
            {
                try
                {
                    Thread.sleep((long)(frameTimeCap - diff)/oneMilliInNano);
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
            lastFrame = currentFrame;
        }
    }

    /**
     * GUI for the Game.
     * 
     */
    public class Window extends JFrame
    {
        private static final long serialVersionUID = 1L;
        JPanel contentPane, panel;

        /**
         * Sets up the Window. Creates the JPanel.
         */
        public Window()
        {
            // Sets up the contentPane with Border and LayoutManager
            contentPane = new JPanel();
            contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
            contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));
            this.setContentPane(contentPane);

            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            this.setResizable(false);

            // Sets up the panel and adds it to the contentPane
            panel = new JPanel();
            //panel.setBackground(Color.cyan);
            panel.setPreferredSize(new Dimension(640, 480));
            contentPane.add(panel);

            this.pack();
            this.setVisible(true);

            this.setIgnoreRepaint(true);
            panel.setIgnoreRepaint(true);
        }

        /**
         * Renders a BufferedImage using the JPanel's Graphics Object.
         * 
         * @param im
         *            Buffered Image that is drawn.
         */
        public void renderImage(BufferedImage im)
        {
            Graphics2D g2d = (Graphics2D) panel.getGraphics();
            g2d.drawImage(im, 0, 0, null);
        }
    }
}

0 个答案:

没有答案