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

Java Swing 2D优化:如何高效绘制大量移动小星星?

Swing 2D星背景渲染性能优化问题

我正在开发一款游戏,游戏背景包含大量从右向左缓慢移动的小星星。全屏状态下每帧需绘制约170颗星,渲染每帧的CPU时间主要消耗在星星绘制上。请问是否有更高效的资源利用方式?

我已参考过相关Swing 2D性能优化要点,尝试过两种实现方案:

  • 使用设备兼容的VolatileImage(采用三种变体之一以避免BITMASK透明度带来的缩放问题),平均每帧渲染耗时约22ms,分析器输出显示drawImage操作占主要CPU耗时。
  • 改用*fillOval()绘制星星而非图片,每帧运行时间降至约20ms,分析器输出显示fillOval()*为主要耗时操作。

测试环境为搭载Intel UHD 620显卡、3840x2160显示器的笔记本电脑。我希望这款游戏能在多种配置的设备上流畅运行,因此需要在这类入门级显卡设备上也有良好表现。


我愿意尝试其他实现相同星背景效果的策略,但核心目标是深入理解Swing 2D的性能优化逻辑,从而全面提升游戏的整体性能。

以下是展示当前实现方式的最小可复现示例(MRE),在我的测试设备上使用VolatileImage的平均渲染速度比直接绘制椭圆更慢:

package space;

import java.awt.AWTException;
import java.awt.BufferCapabilities;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.ImageCapabilities;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.VolatileImage;
import java.util.Random;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class MRE {

    private static final int OVAL_SIZE = 4;
    private static final int NUM_STARS = 150;
    private static final boolean useImages = false;

    private static VolatileImage volatileImage;

    public static void main(String[] args) {
        System.setProperty("sun.java2d.opengl", "true");
        System.setProperty("sun.java2d.accthreshold", "0");
        JFrame frame = new JFrame();
        frame.setSize(new Dimension(1000, 800));
        frame.setIgnoreRepaint(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        RepaintManager manager = new RepaintManager() {
            @Override
            public synchronized void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
                // Ignored
            }

            @Override
            public Rectangle getDirtyRegion(JComponent aComponent) {
                return aComponent.getBounds();
            }

            @Override
            public void markCompletelyDirty(JComponent aComponent) {
                // Ignored
            }

            @Override
            public void markCompletelyClean(JComponent aComponent) {
                // Ignored
            }
        };
        createVolatileImage(frame);
        RepaintManager.setCurrentManager(manager);
        Timer st = new Timer(250, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                long startTime;
                startTime = System.nanoTime();
                JComponent contentPanel = ((JComponent) frame.getContentPane());
                contentPanel.paintImmediately(contentPanel.getBounds());
                long elapsed = System.nanoTime() - startTime;
                System.out.println("Rendering time: " + elapsed / 1000000.0 + "ms.");
            }

        });

        frame.setContentPane(createContentPane());

        setWindowVisible(frame, true);
        st.start();
    }

    private static void createVolatileImage(JFrame frame) {
        GraphicsConfiguration gc = frame.getGraphicsConfiguration();
        volatileImage = gc.createCompatibleVolatileImage(4, 4, Transparency.BITMASK);
        Graphics2D g2 = volatileImage.createGraphics();
        g2.setColor(Color.WHITE);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.fillOval(0, 0, OVAL_SIZE, OVAL_SIZE);
        g2.dispose();
    }

    private static JPanel createContentPane() {
        JPanel contentPane = new JPanel() {
            private static final long serialVersionUID = 2500224655024127049L;

            @Override
            public void paintComponent(Graphics g) {
                Graphics2D g2 = (Graphics2D)g;
                Random rand = new Random();
                g2.setColor(Color.BLACK);
                g2.fillRect(0, 0, getWidth(), getHeight());
                for(int i = 0; i < NUM_STARS; i++) {
                    double x = rand.nextDouble() * getWidth();
                    double y = rand.nextDouble() * getHeight();
                    g2.translate(x, y);
                    if(useImages) {
                        g2.drawImage(volatileImage, 0, 0, null);
                    } else {
                        g2.setColor(Color.WHITE);
                        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                        g2.fillOval(0, 0, OVAL_SIZE, OVAL_SIZE);
                    }
                    g2.translate(-x, -y);
                }
            }
        };
        return contentPane;
    }

    private static void setWindowVisible(JFrame frame, final boolean visible) {
        SwingUtilities.invokeLater(() -> {
            frame.setVisible(visible);
            try {
                final BufferCapabilities caps = new BufferCapabilities(new ImageCapabilities(true),
                    new ImageCapabilities(true), BufferCapabilities.FlipContents.UNDEFINED);
                frame.createBufferStrategy(2, caps);
            } catch (final AWTException e) {
                frame.createBufferStrategy(2);
                System.err.println("Could not set double buffered FLIP strategy, using default double buffer: "
                    + e.getLocalizedMessage());
            }
        });
    }

}

内容的提问来源于stack exchange,提问作者working_man

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:25:00