Java Swing Canvas渲染不稳定求助:矩阵大小影响渲染概率
Java Swing Canvas渲染不稳定问题排查与解决
问题现象
开发Java Swing游戏时遇到Canvas渲染不稳定,有时正常显示,有时出现白屏。矩阵规模越小(cellSize越大),正常渲染概率越高;即使完全移除矩阵逻辑,仍偶尔会出现白屏情况。
相关代码
GameFrame类
package com.GameLearning.main; import java.awt.Color; import javax.swing.JFrame; import javax.swing.JPanel; public class GameFrame extends JFrame { public static JFrame frame; public static JPanel panel; private static GameEngine gameEngine; public GameFrame () { this.setExtendedState(JFrame.MAXIMIZED_BOTH); this.setUndecorated(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.pack(); this.setVisible(true); this.setBackground(Color.BLACK); gameEngine = new GameEngine(this.getWidth(), this.getHeight()); this.add(gameEngine); gameEngine.start(); } public static void main (String[] args) { frame = new GameFrame(); } }
GameEngine类
package com.GameLearning.main; import java.awt.Canvas; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferStrategy; import java.awt.image.BufferedImage; public class GameEngine extends Canvas implements Runnable{ // Loop tools private Thread thread; private Boolean isRunning; // Game attributes private final int WIDTH, HEIGHT; // Game elements private Background background; public GameEngine (int WIDTH, int HEIGHT) { this.WIDTH = WIDTH; this.HEIGHT = HEIGHT; background = new Background(0, 0, WIDTH, HEIGHT); } private void tick() { background.tick(); } private void render() { // Get buffer strategy BufferStrategy bs = this.getBufferStrategy(); if (bs == null) { this.createBufferStrategy(3); return; } // Init layer BufferedImage layer = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g = layer.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Render game background.render(g); // Draw Graphics g = (Graphics2D) bs.getDrawGraphics(); g.drawImage(layer, 0, 0, WIDTH, HEIGHT, null); // show bs.show(); } public synchronized void start () { thread = new Thread(this); isRunning = true; thread.start(); } @Override public synchronized void run() { // Tools for the main loop double desiredFPS = 60; double nanoInterval = 1e9 / desiredFPS; long lastTime = System.nanoTime(); long delta; // Main loop while (isRunning) { delta = System.nanoTime() - lastTime; if (delta >= nanoInterval) { lastTime = System.nanoTime(); // tick and render tick(); render(); } } } }
Background类
package com.GameLearning.main; import java.awt.Color; import java.awt.Graphics; public class Background { private static final int cellSize = 10; private int x, y, WIDTH, HEIGHT; private String[][] matrix; public Background (int x, int y, int WIDTH, int HEIGHT) { this.x = x; this.y = y; this.WIDTH = WIDTH; this.HEIGHT = HEIGHT; matrix = new String[WIDTH / cellSize][HEIGHT / cellSize]; for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { matrix[i][j] = "0"; } } } public void tick () { } public void render(Graphics g) { // render back background g.setColor(Color.BLACK); g.fillRect(x, y, WIDTH, HEIGHT); // render background g.setColor(Color.GREEN); for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { g.drawString(matrix[i][j], i * cellSize, j * cellSize + cellSize); } } } }
问题根源与修复方案
核心问题点
- 组件添加时机错误:先调用
setVisible(true)再添加GameEngine组件,导致组件未完成布局就启动渲染线程,布局异常引发白屏。 - BufferStrategy初始化不严谨:创建BufferStrategy后直接return,首次渲染时可能无有效缓冲区。
- Graphics资源未释放:渲染过程中创建的Graphics对象未调用
dispose(),资源泄漏导致渲染异常。 - Swing线程不安全:在非EDT(事件调度线程)中创建Swing组件,违反Swing线程安全规则。
- 渲染性能瓶颈:使用String数组存储单字符,渲染时频繁创建临时对象,加重线程负载。
修复后代码
GameFrame修复
package com.GameLearning.main; import java.awt.Color; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class GameFrame extends JFrame { public static JFrame frame; private static GameEngine gameEngine; public GameFrame () { this.setExtendedState(JFrame.MAXIMIZED_BOTH); this.setUndecorated(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setBackground(Color.BLACK); // 先添加组件再设置可见,确保布局完成 gameEngine = new GameEngine(this.getWidth(), this.getHeight()); this.add(gameEngine); this.pack(); this.setVisible(true); gameEngine.start(); } public static void main (String[] args) { // 在EDT中创建Swing组件,保证线程安全 SwingUtilities.invokeLater(() -> { frame = new GameFrame(); }); } }
GameEngine修复
package com.GameLearning.main; import java.awt.Canvas; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferStrategy; import java.awt.image.BufferedImage; public class GameEngine extends Canvas implements Runnable{ private Thread thread; private volatile boolean isRunning; private final int WIDTH, HEIGHT; private Background background; public GameEngine (int WIDTH, int HEIGHT) { this.WIDTH = WIDTH; this.HEIGHT = HEIGHT; background = new Background(0, 0, WIDTH, HEIGHT); } private void tick() { background.tick(); } private void render() { BufferStrategy bs = this.getBufferStrategy(); if (bs == null) { this.createBufferStrategy(3); // 创建后重新获取缓冲区,避免直接return导致无有效资源 bs = this.getBufferStrategy(); if (bs == null) return; } BufferedImage layer = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g = layer.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); background.render(g); // 释放layer的Graphics资源 g.dispose(); Graphics2D bsG = (Graphics2D) bs.getDrawGraphics(); bsG.drawImage(layer, 0, 0, WIDTH, HEIGHT, null); bs.show(); // 释放BufferStrategy的Graphics资源 bsG.dispose(); } public synchronized void start () { if (isRunning) return; isRunning = true; thread = new Thread(this); thread.start(); } @Override public void run() { double desiredFPS = 60; double nanoInterval = 1e9 / desiredFPS; long lastTime = System.nanoTime(); long delta; while (isRunning) { delta = System.nanoTime() - lastTime; if (delta >= nanoInterval) { lastTime = System.nanoTime(); tick(); render(); } // 短暂休眠,降低CPU占用,同时给EDT留处理事件的时间 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Background优化
package com.GameLearning.main; import java.awt.Color; import java.awt.Graphics; public class Background { private static final int cellSize = 10; private int x, y, WIDTH, HEIGHT; // 用char数组替代String数组,减少内存开销和渲染负载 private char[][] matrix; public Background (int x, int y, int WIDTH, int HEIGHT) { this.x = x; this.y = y; this.WIDTH = WIDTH; this.HEIGHT = HEIGHT; matrix = new char[WIDTH / cellSize][HEIGHT / cellSize]; for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { matrix[i][j] = '0'; } } } public void tick () { } public void render(Graphics g) { g.setColor(Color.BLACK); g.fillRect(x, y, WIDTH, HEIGHT); g.setColor(Color.GREEN); for (int i = 0; i < matrix.length; i++) { int xPos = i * cellSize; for (int j = 0; j < matrix[i].length; j++) { int yPos = j * cellSize + cellSize; // 直接绘制char,比String更高效 g.drawChar(matrix[i][j], xPos, yPos); } } } }
内容的提问来源于stack exchange,提问作者Rafael Silva
相关产品推荐
相关产品推荐

