在 Java AWT Canvas 中绘制点矩阵太慢

Drawing point matrix too slow in Java AWT Canvas

我正在尝试实施 Conway's Game of Life 来自学 Java。来自 C/C++ 也很好理解。到目前为止,我已经相当成功并且(我认为)我快完成了。代码似乎可以工作,但有两件事仍然困扰着我。

  1. 我将要绘制的点存储在矩阵中,并使用 Canvas::paint(Graphics aGraphics) 方法中的 aGraphics.drawLine(i, j, i, j); 在双 for-loop 中绘制它们。这里的问题是 canvas 的堆积从左到右可见。我确定问题是 drawLine() 方法的重复调用,因为当我切换嵌套 for-loops 的顺序时,构建是自上而下的。将点存储在图像中会更好吗?

  2. 另一个问题是游戏本身运行速度太快了。在不停止 mid-paint 的情况下暂停程序的计算是什么好主意?

这是我的 Canvas Class:

class GOLCanvas extends Canvas {
private int m_iWidth;
private int m_iHeight;
private int[][] m_iPoints;
private int[][] m_iNeighbours;
private double m_dSeed;
private Random m_cRnd;

GOLCanvas(int aWidth, int aHeight, double aSeed) {
    m_iWidth = aWidth;
    m_iHeight = aHeight;
    m_dSeed = aSeed;
    m_cRnd = new Random();
    m_cRnd.setSeed(m_cRnd.nextLong());
    m_iPoints = new int[m_iHeight][m_iWidth];
    m_iNeighbours = new int[m_iHeight][m_iWidth];   
}

public void init() {
    // init Points randomly
}

private int getRandomInt(double aProbability) {
    return (m_cRnd.nextDouble() < m_dSeed) ? 1 : 0;
}

public void countNeighbours () {
    // ditto name   
}

public void calcNextStep () {
    // ditto name
}
@Override
public void paint(Graphics aGraphics) {
    // **ANY IDEAS TO SPEED UP THIS PART HERE?**
    for(int i = 0; i < m_iHeight; i++) {
        for(int j = 0; j < m_iWidth; j++) {
            if (m_iPoints[i][j] == 1){
                aGraphics.drawLine(i, j, i, j);
            }
        }
    }
}

我会去看看 this link and this one

基本上归结为使用 BufferedImages。至于减慢程序的一部分,您可以使用 Thread.sleep(1000) 其中 1000 等于 1 秒。

查看您的代码,我建议将设计更改为动态呈现 Canvasrepaint 未实现多缓冲,可能会导致闪烁。另外关于你的游戏 运行 快,你需要在你自己的 Thread(不是主线程)中 运行 你的游戏然后实现一个 update 方法并将它同步到 N Thread.sleep.

每秒一次

设计可以是这样的:

public class Game extends Canvas implements Runnable {

    // resolution
    public static final int     WIDTH         = 640;
    public static final int     HEIGHT        = 480;

    // window title
    private static final String TITLE         = "Title";

    /**
     * Number of logical/physical updates per real second
     */
    private static final int    UPDATE_RATE   = 60;

    /**
     * Number of rendering buffers
     */
    private static final int    BUFFERS_COUNT = 3;

    /**
     * Value of a second in NanoSeconds DO NOT CHANGE!
     */
    private static final long   NANOS_IN_SEC  = 1000000000L;

    /**
     * Update interval in double precision NanoSeconds DO NOT CHANGE!
     */
    private static final double UPDATE_SCALE  = (double) NANOS_IN_SEC / UPDATE_RATE;

    private JFrame              window;
    private Thread              gameThread;
    private boolean             running;

    // temp values 
    int                         x             = 0;
    int                         y             = 0;

    ////////////////

    public Game(JFrame window) {
        this.window = window;
        this.running = false;

        setPreferredSize(new Dimension(WIDTH, HEIGHT));

        this.window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        // properly ends the game by calling stop when window is closed
        this.window.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                stop();
                super.windowClosing(e);
                System.exit(0);
            }
        });

        this.window.getContentPane().add(this);
        this.window.setResizable(false);
        this.window.pack();
        this.window.setLocationRelativeTo(null);
        this.window.setVisible(true);
    }

    // starts the game
    public synchronized void start() {
        if (running)
            return;

        running = true;
        gameThread = new Thread(this);
        gameThread.start();
        System.out.println("Game thread started");
        System.out.println("UPDATE_RATE: " + UPDATE_RATE);
    }

    // ends the game
    public synchronized void stop() {
        if (!running)
            return;

        running = false;
        boolean retry = true;
        while (retry) {
            try {
                gameThread.join();
                retry = false;
                System.out.println("Game thread stoped");
            } catch (InterruptedException e) {
                System.out.println("Failed sopping game thread, retry in 1 second");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    private void update() {
        // this will run UPDATE_RATE times a second
        x++;
        y++;
    }

    private void render() {
        BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            createBufferStrategy(BUFFERS_COUNT);
            return;
        }

        Graphics2D g2d = (Graphics2D) bs.getDrawGraphics().create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        clear(g2d, 0);

        // render here
        g2d.setColor(Color.red);
        g2d.fillRect(x, y, 50, 50);
        //////////////

        g2d.dispose();

        bs.show();
    }

    private void clear(Graphics2D g2d, int shade) {
        g2d.setColor(new Color(shade, shade, shade));
        g2d.fillRect(0, 0, WIDTH, HEIGHT);
    }

    // game loop thread
    public void run() {
        long startTime = System.currentTimeMillis();
        long tick = 1000;

        int upd = 0;
        int fps = 0;

        double updDelta = 0;

        long lastTime = System.nanoTime();

        while (running) {
            long now = System.nanoTime();
            updDelta += (now - lastTime) / UPDATE_SCALE;
            lastTime = now;

            while (updDelta > 1) {
                update();
                upd++;
                updDelta--;
            }
            render();
            fps++;

            if (System.currentTimeMillis() - startTime > tick) {
                window.setTitle(TITLE + " || Upd: " + upd + " | Fps: " + fps);
                upd = 0;
                fps = 0;
                tick += 1000;
            }

            try {
                Thread.sleep(5); // always a good idea to let is breath a bit
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }
}

用法:

public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    EventQueue.invokeLater(() -> {
        new Game(new JFrame()).start();
    });
}

显然这只是一种方法(还有很多其他方法),请根据您的需要随意调整。 我花了大约 20 分钟的时间来写,所以我希望它没有白费,这会有所帮助,如果您发现代码中有任何您无法修复的错误,请告诉我(我有点写它而没有检查它是否有效)。