You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
            }
        }
    }
}

问题根源与修复方案

核心问题点

  1. 组件添加时机错误:先调用setVisible(true)再添加GameEngine组件,导致组件未完成布局就启动渲染线程,布局异常引发白屏。
  2. BufferStrategy初始化不严谨:创建BufferStrategy后直接return,首次渲染时可能无有效缓冲区。
  3. Graphics资源未释放:渲染过程中创建的Graphics对象未调用dispose(),资源泄漏导致渲染异常。
  4. Swing线程不安全:在非EDT(事件调度线程)中创建Swing组件,违反Swing线程安全规则。
  5. 渲染性能瓶颈:使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 15:59:51